Dealing with async for each element of an array using async.js module in nodejs - node.js

I need to run a mysql query for each element of the array and add the final results to a response.
Is there no way to do this without adding a setTimeout? I find it odd. Tried everything from async / await , promises , callback , async.js module
How to deal with something like this in practice?
Here's what I'm trying to do
let reseach = { }
let drinktypes = ["1", "2", "3", "4"];
async.eachSeries(
drinktypes,
async item => {
console.log( 'item:', item )
let query = ` SELECT us.*,
fl.web_path,
pr.product_name,
pr.acidity,
pr.drink_type,
pr.recmd,
pr.raw_ingredient,
pr.milling,
pr.label_image as li,
fl2.web_path AS wp,
pr.alcohol,
pr.sake_index,
pr.kouboname,
pr.kojiname,
pr.product_details,
pr.memo,
pr.type_name,
pr.code AS code,
pr.raw_ingredient,
pr.milling,
mk.brewery_name,
mk.prefecture
FROM users_scores us
join products pr
ON us.product_id = pr.pid
LEFT JOIN fileslabels fl2 ON pr.label_image = fl2.id
join makers mk
ON pr.brewery = mk.mid
join product_label pl
ON pr.pid = pl.product_id
join files fl
ON pl.file_id = fl.id
WHERE user_id =\"${req.body.userid}\"
AND dranktimes <> 0
AND pr.drink_type = \"${item}\"`;
connection.query(query, function (error, results ,fields) {
if(!error) {
reseach[item] = results[0];
} else {
throw error;
}
});
Promise.resolve() // <-- instead of callback
},
err => {
console.log('err:', err)
console.log(reseach) // null
setTimeout(function(){
console.log("timeout", reseach); //works
}, 100);
}
)

If I understood your question right then you can simply do this with one SQL query (this query is short just for example):
let query = 'SELECT name FROM users WHERE drink_type IN (?)';
connection.query(query, drinktypes);
Using this

Related

Node with SQL Server - response with for json path query not responding as expected

I am working with Node and SQL Server. Everything is fine with simple two dimensional tables, but when I try to do a nested query with a for json path response, I get an unexpected result I don't know how to solve with node.js
Here is my code:
let sqlString = `
SELECT codeid, code, validFrom, validTo,
(SELECT dbo.PLprospectAgentCodesComp.productIdentifier, dbo.masterGroupsProducts.productName, dbo.PLprospectAgentCodesComp.compensation
FROM dbo.PLprospectAgentCodesComp INNER JOIN
dbo.masterGroupsProducts ON dbo.PLprospectAgentCodesComp.productIdentifier = dbo.masterGroupsProducts.productIdentifier
WHERE (dbo.PLprospectAgentCodesComp.codeid = dbo.PLprospectAgentCodes.codeid) for json path ) as products
FROM dbo.PLprospectAgentCodes
WHERE (plid = ${userData.plid}) for json path`
let conn = await sql.connect(process.env.DB_CONNSTRING)
let recordset = await conn.query(sqlString)
if (recordset.rowsAffected[0] > 0) {
jsonResponse.success = 1
jsonResponse.data = recordset.recordset;
}
else {
JsonResponse.success = 0;
JsonResponse.message = "ERR 1:Invalid credentials";
}
res.json(jsonResponse)
await conn.close();
Everything was fine until I utilized for json auto at my sql string, which I need in order to get the results the way I need them.
I tried stringyfying, .toString(), JSON.parse, nothing worked, and this is the result I get:
{
"success": 1,
"data": [
{
"JSON_F52E2B61-18A1-11d1-B105-00805F49916B": "[{\"codeid\":1,\"code\":\"pablo\",\"validFrom\":\"2020-01-01\",\"validTo\":\"2022-01-01\",\"products\":[{\"productIdentifier\":\"Sigma\",\"productName\":\"Sigma Tramma\",\"compensation\":28.00},{\"productIdentifier\":\"membership\",\"productName\":\"Membership\",\"compensation\":30.00}]},{\"codeid\":2,\"code\":\"paola20\",\"validFrom\":\"2021-01-01\",\"validTo\":\"2020-01-01\",\"products\":[{\"productIdentifier\":\"Sigma\",\"productName\":\"Sigma Tramma\",\"compensation\":18.00},{\"productIdentifier\":\"membership\",\"productName\":\"Membership\",\"compensation\":20.00}]}]"
}
]
}
How can I fix this, from either the query, or Node?
Thanks.
As AlwaysLearning said, try
let sqlString = `
select (SELECT codeid, code, validFrom, validTo,
(SELECT dbo.PLprospectAgentCodesComp.productIdentifier, dbo.masterGroupsProducts.productName, dbo.PLprospectAgentCodesComp.compensation
FROM dbo.PLprospectAgentCodesComp INNER JOIN
dbo.masterGroupsProducts ON dbo.PLprospectAgentCodesComp.productIdentifier = dbo.masterGroupsProducts.productIdentifier
WHERE (dbo.PLprospectAgentCodesComp.codeid = dbo.PLprospectAgentCodes.codeid) for json path ) as products
FROM dbo.PLprospectAgentCodes
WHERE (plid = ${userData.plid}) for json path) as data`
This will result in
{
"success": 1,
"data": [
{
"data": "[{\"codeid\":1,\"code\":\"pablo\",\"validFrom\":\"2020-01-01\",\"validTo\":\"2022-01-01\",\"products\":[{\"productIdentifier\":\"Sigma\",\"productName\":\"Sigma Tramma\",\"compensation\":28.00},{\"productIdentifier\":\"membership\",\"productName\":\"Membership\",\"compensation\":30.00}]},{\"codeid\":2,\"code\":\"paola20\",\"validFrom\":\"2021-01-01\",\"validTo\":\"2020-01-01\",\"products\":[{\"productIdentifier\":\"Sigma\",\"productName\":\"Sigma Tramma\",\"compensation\":18.00},{\"productIdentifier\":\"membership\",\"productName\":\"Membership\",\"compensation\":20.00}]}]"
}
]
}
Then you can
let parsedResponse=JSON.parse(apiResponse.data[0].data);
console.log(parsedResponse)
Hope it helped.

How to update a table from a JSON object where the conditions are in a JSON object?

I want to update a taable from a JSON object. The WHERE clause of the update has more than one columns. I tried this :
connexion.query("update my_table set ? where ?", [{ "param_valeur": emails }, { "param_key": "paramKey", "flotte_id": 1 }], function (err, rows) {
console.log("=================== sql =", this.sql);
if (err)
throw err;
res.send("");
});
but at runtime the WHERE clause does not have the AND keyword ! So how to fix that ?
OK, you want to use dynamic data by sending a json to MySQL WHERE query, from what I found out until now this is not a trivial operation... So, my first answer is not a good solution for your requirements so I write a new and dynamic code. (a little bit more complicated than the first answer...)
Please try the solution and let my know if this is what you want.
my answer is based on https://www.npmjs.com/package/mysql#preparing-queries, you can read this docs section for better understanding of the double question marks syntax.
let paramsSETObj = { "param_valeur": emails };
let paramsWHEREObj = { "param_key": "paramKey", "flotte_id": 1 };
// separate the question marks with comma ', '
let questionMarksForSET = this.getQuestionMarksStr(Object.keys(paramsSETObj).length, ', ');
let valuesForSETArray = this.convertObjectToArray(paramsSETObj);
// separate the question marks with 'AND'
let questionMarksForWHERE = this.getQuestionMarksStr(Object.keys(paramsWHEREObj).length, ' AND ');
let valuesForWHEREArray = this.convertObjectToArray(paramsWHEREObj);
let sql = `UPDATE my_table SET ${questionMarksForSET} WHERE ${questionMarksForWHERE}`;
let inserts = [...valuesForSETArray, ...valuesForWHEREArray];
sql = mysql.format(sql, inserts);
connexion.query(sql, function (err, rows) {
if (err)
throw err;
res.send("");
});
// Convert an object to array, where each key is before the value
module.exports.convertObjectToArray = function (obj) {
let arr = [];
for (let key in obj) {
arr.push(key);
arr.push(obj[key]);
}
return arr;
}
/**
* Get a question marks string separate each pair with the equal sign '='
* the amount of the questions marks pairs is the same as the keys of the object from the request.
* The separator in this case is comma ',' or 'AND' - this is a must for your requirement in the question.
* example:
* questionMarksForSET: ?? = ? , ?? = ?
* questionMarksForWHERE: ?? = ? AND ?? = ?
*/
module.exports.getQuestionMarksStr = function (objKeysLength, separator) {
let questionsMarks = '';
for (let i = 0; i < objKeysLength; i++) {
// if the index is not in the last object key - add the separator else add empty string
let commaOrAndSeparator = (i !== objKeysLength - 1) ? separator : '';
questionsMarks += ' ?? = ? ' + commaOrAndSeparator;
}
return questionsMarks;
}
Try this:
connexion.query('UPDATE my_table SET param_valeur = ? WHERE param_key = ? AND flotte_id = ?', [emails, "paramKey", 1], function (err, rows) {
if (err)
throw err;
res.send("");
});

Sequelize - NodeJS - Return o function is undefined

I am trying to understand how I can fix my code, because my function is return undefined. I am wirting my code with Sequelize and NodeJS.
exports.getCreditsStudent = function(sgecode,collections,grade,year){
mssql.query('SELECT total FROM [dbo].[creditosAvulsos] WHERE codsge = $sgecode1 and nivelensino_idnivelensino = $collections1 and produto_idproduto = $grade1 and ano = $year1',
{ bind:{sgecode1: sgecode, collections1: collections, grade1: grade, year1: year}, type: mssql.QueryTypes.SELECT})
.then(total => {
return total
})
}
And I call this function on my Service.js, like this:
examples = db.getCreditsStudent(sgecode,collections,grade,year);
Where and how is the problem?
Thanks.
The function getCreditsStudent does not actually return anything. Instead it simply defines a async process. You'll need to return the promise you've defined and then either use async/await or a promise chain to use the result.
exports.getCreditsStudent = function(sgecode,collections,grade,year){
return mssql.query('SELECT total FROM [dbo].[creditosAvulsos] WHERE codsge = $sgecode1 and
nivelensino_idnivelensino = $collections1 and produto_idproduto = $grade1 and ano = $year1',
{ bind:{sgecode1: sgecode, collections1: collections, grade1: grade, year1: year},
type: mssql.QueryTypes.SELECT})
.then(total => {
return total
})
}
And then inside an async function you can do
examples = await db.getCreditsStudent(sgecode,collections,grade,year);
or you can use
db.getCreditsStudent(sgecode, collections, grade, year)
.then(value => {
examples = value;
// whatever you want to do with examples...
});

How to execute query async in sequelizer in NodeJs

hello i have try bellow code but my code not work properly
tags.forEach(function(value) {
var where1 = {};
var attr1 = ['id'];
attr1 = ['id'];
where1['name'] = value;
tagData['name'] = value;
tagModel.getTag(where1, attr1, function(code2,result2){
if(result2.length!=0) {
var quick_start_tagData={'quick_start_id' : result1['id'], 'tag_id' :result2[0]['id']}
quick_start_tagModel.saveData(quick_start_tagData, function(code2,result2){
});
console.log(quick_start_tagData);
} else {
tagModel.saveData(tagData, function(code2,result2) {});
}
});
});
problem is for loop iterate no.of data when i check value of that data into table if table have same value then get its id and insert and if not than add new record and get its id and insert into another table
but first select query execute all time and than insert query execute in loop
how to solve this issue
Query Execute like that way
Executing (default):
SELECT id FROM pxl_tag AS pxl_tag WHERE pxl_tag.name = 'a1';
SELECT id FROM pxl_tag AS pxl_tag WHERE pxl_tag.name = 'a2';
SELECT id FROM pxl_tag AS pxl_tag WHERE pxl_tag.name = 'a3';
INSERT INTO pxl_tag (id,name,created_at,updated_at) VALUES
(DEFAULT,'a3','2018-05-04 04:35:32','2018-05-04 04:35:32');
INSERT INTO pxl_tag (id,name,created_at,updated_at) VALUES
(DEFAULT,'a3','2018-05-04 04:35:32','2018-05-04 04:35:32');
INSERT INTO pxl_tag (id,name,created_at,updated_at) VALUES
(DEFAULT,'a3','2018-05-04 04:35:32','2018-05-04 04:35:32');
but i want first select and insert step by step
You can use each method from async library.
For example:
UPDATE
async.each(tags, function(value, cb) {
var where1 = {};
var attr1 = ['id'];
attr1 = ['id'];
where1['name'] = value;
tagData['name'] = value;
tagModel.getTag(where1, attr1, function(code2, result2) {
if (result2.length != 0) {
var quick_start_tagData = {
'quick_start_id': result1['id'],
'tag_id': result2[0]['id']
}
quick_start_tagModel.saveData(quick_start_tagData, function(code2, result2) {
cb();
});
console.log(quick_start_tagData);
} else {
tagModel.saveData(tagData, function(code2, result2) {
cb();
});
}
});
}, function(err) {
if (err) {
console.log('An error ocurred');
} else {
console.log('Nothing happens');
}
});
If I understood you correctly, the problem is not that your INSERTs doesn't run right after the SELECTs. Your problem is that you are calling async functions (getTag() and saveData()) inside a sync function loop (forEach()), thus, by the time that your async functions resolve, they get the last value for tagData which is defined inside the sync loop. You should give it a different scope to get the correct tagData value.
Try this (untested):
tags.forEach(function(value) {
var where1 = {};
var attr1 = ['id'];
attr1 = ['id'];
where1['name'] = value;
tagData['name'] = value;
saveTag(tagModel, quick_start_tagModel, where1, attr1, result1, tagData);
});
//Create a function so that tagData will have a different scope for each call
function saveTag(tagModel, quickStartTagModel, where, attr, result1, tagData) {
tagModel.getTag(where, attr, function(code,result){
if(result.length!=0) {
var quickStartTagData = {'quick_start_id' : result1['id'], 'tag_id' :result[0]['id']}
quickStartTagModel.saveData(quickStartTagData, function(code, result){});
console.log(quickStartTagData);
} else {
tagModel.saveData(tagData, function(code,result) {});
}
});
}
if you strictly need to run the SELECT and INSERT sequentially (and not async), you should look at promises and async library. This might help:
Resolve promises one after another (i.e. in sequence)?

Mongodb $where query always true with nodejs

When I query my database with a function passed in the "$where" clause in nodejs, it always return me all documents in the db.
For example, if I do
var stream = timetables.find({$where: function() { return false; }}).stream();
it return me all the documents.
Instead, if I do
var stream = timetables.find({$where: 'function() { return false; }'}).stream();
the function is really executed, and this code doesn't return any document.
The problem is that if I convert in string my function the context's bindinds are removed, and I need them for more complex query. For example:
var n = 1;
var f = function() { return this.number == n; }
var stream = timetables.find({$where: f.toString()}).stream();
// error: n is not defined
Is this a normal behaviour? How can I solve my problem?
Please excuse me for my poor english!
First off, keep in mind that the $where operator should almost never be used for the reasons explained here (credit goes to #WiredPrairie).
Back to your issue, the approach you'd like to take won't work even in the mongodb shell (which explicitly allows naked js functions with the $where operator). The javascript code provided to the $where operator is executed on the mongo server and won't have access to the enclosing environment (the "context bindings").
> db.test.insert({a: 42})
> db.test.find({a: 42})
{ "_id" : ObjectId("5150433c73f604984a7dff91"), "a" : 42 }
> db.test.find({$where: function() { return this.a == 42 }}) // works
{ "_id" : ObjectId("5150433c73f604984a7dff91"), "a" : 42 }
> var local_var = 42
> db.test.find({$where: function() { return this.a == local_var }})
error: {
"$err" : "error on invocation of $where function:\nJS Error: ReferenceError: local_var is not defined nofile_b:1",
"code" : 10071
}
Moreover it looks like that the node.js native mongo driver behaves differently from the shell in that it doesn't automatically serialize a js function you provide in the query object and instead it likely drops the clause altogether. This will leave you with the equivalent of timetables.find({}) which will return all the documents in the collection.
This one is works for me , Just try to store a query as a string in one variable then concat your variable in query string,
var local_var = 42
var query = "{$where: function() { return this.a == "+local_var+"}}"
db.test.find(query)
Store your query into a varibale and use that variable at your find query. It works..... :D
The context will always be that of the mongo database, since the function is executed there. There is no way to share the context between the two instances. You have to rethink the way you query and come up with a different strategy.
You can use a wrapper to pass basic JSON objects, ie. (pardon coffee-script):
# That's the main wrapper.
wrap = (f, args...) ->
"function() { return (#{f}).apply(this, #{JSON.stringify(args)}) }"
# Example 1
where1 = (flag) ->
#myattr == 'foo' or flag
# Example 2 with different arguments
where2 = (foo, options = {}) ->
if foo == options.bar or #_id % 2 == 0
true
else
false
db.collection('coll1').count $where: wrap(where1, true), (err, count) ->
console.log err, count
db.collection('coll1').count $where: wrap(where2, true, bar: true), (err, count) ->
console.log err, count
Your functions are going to be passed as something like:
function () {
return (function (flag) {
return this.myattr === 'foo' || flag;
}).apply(this, [true])
}
...and example 2:
function () {
return (
function (foo, options) {
if (options == null) {
options = {};
}
if (foo === options.bar || this._id % 2 === 0) {
return true;
} else {
return false;
}
}
).apply(this, [ true, { "bar": true } ])
}
This is how it is supposed to be. The drivers don't translate the client code into the mongo function javascript code.
I'm assuming you are using Mongoose to query your database.
If you take a look at the actual Query object implementation, you'll find that only strings are valid arguments for the where prototype.
When using the where clause, you should use it along with the standard operators such as gt, lt that operates on in the path created by the where function.
Remember that Mongoose querying, as in Mongo, is by example, you may want to reconsider your query specification in a more descriptive fashion.

Resources