Node.js - sqlstring alternative which allows named named replacements - sql

The sqlstring node module allows creating of queries using an ordered array. So if I have a template query like:
sqlstring.format('Select * from users where id = ?', ['my_id'])
It will become:
Select * from users where id = 'my_id'
However here I need to remember the order of the question marks, so if the same thing is being in multiple places it becomes a hassle. Is there an alternative which allows me to do the following:
sqlstring.format('Select :id + :foo as bar from users where id = :id', {id: 1, foo: 3})
Which would become:
Select 1 + 3 as bar from users where id = 1
I know knex query builder does this, but I don't want install the entirety of knex just for the query builder.

You can use mysql2 package, that support that format:
Named placeholders
You can use named placeholders for parameters by setting
namedPlaceholders config value or query/execute time option. Named
placeholders are converted to unnamed ? on the client (mysql protocol
does not support named parameters). If you reference parameter
multiple times under the same name it is sent to server multiple
times.
connection.config.namedPlaceholders = true;
connection.execute('select :x + :y as z', {x: 1, y: 2}, function (err, rows) {
// statement prepared as "select ? + ? as z" and executed with [1,2] values
// rows returned: [ { z: 3 } ]
});
connection.execute('select :x + :x as z', {x: 1}, function (err, rows) {
// select ? + ? as z, execute with [1, 1]
});
connection.query('select :x + :x as z', {x: 1}, function (err, rows) {
// query select 1 + 1 as z
});

Related

Is there a way to set some values in the multiple dimensional json array

Scenario Outline: Explore Karate '<ID>'
* karate.set($attributesFirstRun[*].created_timestamp,'#present')
* karate.set($attributesSecondRun[*].created_timestamp,'#present')
* match attributesFirstRun == attributesSecondRun
Examples:
| read('Sample.csv') |
I tried this. But I'm getting this error
org.graalvm.polyglot.PolyglotException: SyntaxError: Unnamed:1:42 Expected an operand but found *
I think you are over-thinking this. Karate is actually just plain JS. And sounds like you are trying to do a "bulk update" via JsonPath - sorry, that's not supported, perhaps you would be interested in contributing code.
Here's the solution for updating all elements of an array:
* def before = [{ a: 1 }, { a: 2 }]
* def after = before.map(x => ({ a: x.a * 5 }))
* match after == [{ a: 5 }, { a: 10 }]
Keep in mind updating JSON is easy:
* def data = {}
* data.a = 1
* match data == { a: 1 }
Refer the docs: https://github.com/karatelabs/karate#json-transforms

How to query from "any"/"map" data type on Tarantool?

Following example from this answer. If I created map without index, how to query the inner value of the map?
box.schema.create_space('x', {format = {[1] = {'id', 'unsigned'}, [2] = {'obj', 'map'}}})
box.space.x:create_index('pk', {parts = {[1] = {field = 1, type = 'unsigned'}}})
box.space.x:insert({2, {text = 'second', timestamp = 123}}
box.execute [[ SELECT * FROM "x" ]]
-- [2, {'timestamp': 123, 'text': 'second'}]
How to fetch timestamp or text column directly from SQL without creating index?
Tried these but didn't work:
SELECT "obj.text" FROM "x"
SELECT "obj"."text" FROM "x"
SELECT "obj"["text"] FROM "x"
SELECT "obj"->"text" FROM "x"
You can register a Lua function to call it from SQL. The first example from our SQL + Lua manual shows exactly what you asked.
A bit simplified version of the example to explain the idea:
box.schema.func.create('GETFIELD', {
language = 'LUA',
returns = 'any',
body = [[
function(msgpack_value, field)
return require('msgpack').decode(msgpack_value)[field]
end]],
is_sandboxed = false,
param_list = {'string', 'string'},
exports = {'SQL'},
is_deterministic = true
})
After registration of the function you can call it from SQL:
tarantool> \set language sql
tarantool> select getfield("obj", 'text') from "x"
---
- metadata:
- name: COLUMN_1
type: any
rows:
- ['second']
...
tarantool> select getfield("obj", 'timestamp') from "x"
---
- metadata:
- name: COLUMN_1
type: any
rows:
- [123]
...
Differences from the example in the manual:
No hack with the global variable, but no dot syntax ('foo.bar.baz').
Exported only to SQL.
The return type is 'any': so it can be used for, say, the numeric 'timestamp' field. Downside: 'any' is reported in the result set metainformation.
(The idea suggested by Nikita Pettik, my teammate.)

Laravel where, orWhereHas and whereNotIn

Hello great people of SO!
I hope you all have a good day and have a good health
Note: I'm not good at SQL
Sorry for bad english, but I will try my best to explain my issue
I'm using Laravel v8.x for my app, and after setting up model relationships, events, queues, etc, now I'm working for SQL
ATM, I have 2 Models,
User
Post
Relationships:
User hasMany Post
User belongsToMany User (Block)
User belongsToMany User (Follow)
Post belongsTo User
Database:
5 record for User
2 record for Block
3 records for Post
Table: (Using faker)
users
[
{ id: 1, name: 'Jonathan Beatrice', username: 'kiana.fay', ... },
{ id: 2, name: 'Lacey Kirlin', username: 'kenna.turner', ... },
{ id: 3, name: 'Alexander Schiller', username: 'cassandra95', ... },
{ id: 4, name: 'Daniel Wickozky', username: 'nkoepp', ... },
{ id: 5, name: 'Maymie Lehner', username: 'frami.felton', ... }
]
block
[
{ id: 1, by_id: 1, to_id: 2 }, // User #1 block user #2
{ id: 2, by_id: 4, to_id: 1 } // User #4 block user #1
]
posts
[
{ id: 1, user_id: 2, body: 'Test post', ... },
{ id: 2, user_id: 5, body: 'Lorem ipsum dolor sit amet ...', ... },
{ id: 3, user_id: 4, body: 'ABCD festival soon! ...', ... },
]
Everything works fine and smooth
Now that I want to implement search system, I have a problem, since I'm not good with SQL
Here's my code
SearchController.php
use ...;
use ...;
...
public function posts(Request $request)
{
// For testing purpose
$user = User::with(['userBlocks', 'blocksUser'])->find(1);
// Get all id of user that $user block
// return [2]
$user_blocks = $user->userBlocks->pluck('pivot')->pluck('to_id')->toArray();
// Get all id of user that block $user
// return [4]
$blocks_user = $user->blocksUser->pluck('pivot')->pluck('by_id')->toArray();
// Merge all ids above (must be unique())
// return [2, 4]
$blocks = array_merge($user_blocks, $blocks_user);
// .../search?q=xxx
$query = $request->query('q');
$sql = Post::query();
// Search for posts that has `posts`.`body` LIKE ? ($query)
$sql->where('body', 'LIKE', "%$query%");
// This is where I got confused
$sql->orWhereHas('user', function ($post_user) use ($blocks, $query) {
$post_user
->whereNotIn('id', $blocks) // Exclude posts that has user and their id not in (x, x, x, x, ... ; $block variable above)
->where('name', 'LIKE', "%$query%") // Find user that has name LIKE ? ($query)
->orWhere('username', 'LIKE', "%$query%"); // or Find user that has username LIKE ? ($query)
});
$sql->orderBy('created_at', 'DESC');
$sql->with(['user']);
$posts = $sql->simplePaginate(10, ['*'], 'p');
return $posts;
}
I run the code, .../search?q=e
Note:
All users has alphabet E in their names
And also all posts has alphabet E in their body
We (as User #1), block User #2, and User #4, block us (User #1)
Result: Controller returned all posts
This is the query when I use DB::enableQueryLog() and DB::getQueryLog()
SELECT
*
FROM
`posts`
WHERE `body` LIKE ?
AND EXISTS
(SELECT
*
FROM
`users`
WHERE `posts`.`user_id` = `users`.`id`
AND (
`id` NOT IN (?)
AND `username` LIKE ?
OR `name` LIKE ?
))
ORDER BY `created_at` ASC
LIMIT 11 OFFSET 0
Goal: Search all posts that has body LIKE ?, OR posts that has user; username LIKE ? or name LIKE ? (But also exclude the user we block and the user that block us
Thanks in advance
If there's any unclear explanation, I will edit it A.S.A.P
If I run on my recent laravel install, with my proposed change for one of your issues, version 7.19.1, I get this query:
SELECT
*
FROM
`posts`
WHERE `body` LIKE ?
OR EXISTS <- line of interest
(SELECT
*
FROM
`users`
WHERE `posts`.`user_id` = `users`.`id`
AND (
`id` NOT IN (?)
AND (`username` LIKE ?
OR `name` LIKE ?) <- extra brackets ive added
))
ORDER BY `created_at` ASC
LIMIT 11 OFFSET 0
Have a look at the line of interest, and compare it with the query your version of laravel is running. The AND EXISTS line is being incorrectly generated by laravel. OrWhereHas isnt behaving correctly in your version, I can't find the release number to see where it was fixed.
Id recommend upgrading to latest if possible, but thats not always an option. I've had a dig around, and it looks like the user in this question here encountered a similar problem:
WhereHas() / orWhereHas not constraining the query as expected
You can try moving your $sql->with(['user']); to before you OrWhereHas clause. I'm not sure if that will change it to OR, but its worth a try.
Second thing, I've added whereNested to your OR clause to ensure the precedence is correct, which adds the extra brackets in the query above, as in you dont want:
(`id` NOT IN (1, 2, 3)
AND `name` LIKE % test %)
OR `username` LIKE % test %
Since then it would include your blocked posts in the exists clause.
So final changes look like this, which I think fufills your description:
$sql->with(['user']); //deleted from original position and move here
$sql->where('body', 'LIKE', "%$query%")->whereNotIn('id', $blocks); //additional line
$sql->orWhereHas('ambience', function ($post_user) use ($blocks, $query) {
$post_user
->whereNotIn('id', $blocks);
$post_user->whereNested(function($post_user) use ($query) { //new bit
$post_user->where('name', 'LIKE', "%$query%")
->orWhere('username', 'LIKE', "%$query%");
});
});

Multiple MySQL queries returning undefined when outputting value

I am running two database queries to retrieve data that I will outputting in a message embed. The queries are returning the proper rows when I just dump the entire result into the console. However, whenever I try to output the actual value for one of the rows, it displays as undefined in the message embed.
From what I've found based on examples, rows[0].somevalue should be outputting the correct results.
let mentionedUser = message.mentions.members.first();
let captainUser = client.users.find(user => user.id == `${mentionedUser.id}`);
con.query(`SELECT * FROM captains WHERE id = '${mentionedUser.id}';SELECT * FROM results WHERE captain = '${captainUser.username}'`, [2, 1], (err, rows) => {
if(err) throw err;
console.log(rows);
const infoEmbed = new Discord.RichEmbed()
.setColor("#1b56af")
.setAuthor('Captain Information', client.user.displayAvatarURL)
.setThumbnail('https://i.imgur.com/t3WuKqf.jpg')
.addField('Captain Name', `${mentionedUser}`, true)
.addField('Cap Space', `${rows[0].credits}`, true) // Returns undefined
message.channel.send(infoEmbed);
});
This is the console result
[ [ RowDataPacket {
id: '91580646270439424',
team_name: 'Resistance',
credits: 85,
roster_size: 2 } ],
[ RowDataPacket { id: 'Sniper0270', captain: 'BTW8892', credits: 10 },
RowDataPacket { id: 'Annex Chrispy', captain: 'BTW8892', credits: 5 } ] ]
In the code posted above, the expected output of rows[0].credits should output 85. No error codes are present, it just displayed as "undefined" in the message embed.
You are executing two queries inside a single query call. It looks like the mysql library returns an array of arrays in this scenario where the first value is the result of the first query and the second is the result of the second query. This is non standard. Normally you would either execute each query in its own query call or you would use a union to join the two queries into a single resultset.
this is not the practical way to send query request , as query is a single statement excluding the bulk update , you cannot execute two different query using a single con.query , it is not a proper way. execute them separately

knex insert multiple rows

I have a problem inserting many rows into postgres db with knex.
I have dynamic number of rows needed to be inserted. The result i expect is:
insert row four times (four is for an example. I dont know exact number of inserts as it comes dynamically from frontend):
field_id will be diffrent in every row: (1,2,3,4) - i have array of these ID's
id_of_product will be always the same
value will be always diffrent: (req.body[id] that comes from Frontend) - ID in brackets is same value as the field_id from an
array
How i can achieve that? I tried looping it with forEach, but it's async operation so i can't use .then() as it will be called four times
Here's what i tried. i dont know how to set field_id and req.body to take it dynamically.
fields = [1,2,3,4]
Expected result:
knex creates 4 inserts as follows:
field_id: 1,
product_id: some static id
value: frontValue[1]
ETC
knex('metadata').insert(
[{ field_id: fields,
product_id: product_id,
value: req.body[fields]
}]
)
If I understand correctly you want to insert 4 records to your metadata table:
{ field_id: 1, product_id: X, value: req.body[1] },
{ field_id: 2, product_id: X, value: req.body[2] },
{ field_id: 3, product_id: X, value: req.body[3] },
{ field_id: 4, product_id: X, value: req.body[4] }
To insert multiple records in the same statement they each need to be separate elements in the array that you supply to Knex (check out the insert docs for additional examples):
const product_id = X;
const fieldsToInsert = fields.map(field =>
({ field_id: field, product_id, value: req.body[field] }));
return knex('metadata').insert(fieldsToInsert)
.then(() => { /* handle success */ })
.catch(() => { /* handle failure */});