How to update a table from a JSON object where the conditions are in a JSON object? - node.js

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("");
});

Related

Unable to run includes() method on string returned by fs.readFileSync()

having a bit of trouble:
let data = fs.readFileSync(pathToCsv, "utf8");
the value of data comes out to be:
clan,mem1,mem2,mem3,mem4,language,managerID,serverJoinedDate
pm,
pm
(through console.log())
but still data.toString().includes("pm") is false.
Here is my full code:
const filter = (m) => m.author.bot === false;
await ogMessage.author.dmChannel
.awaitMessages(filter, {
max: 1,
time: 60000,
})
.then((collected) => {
if (clans[parseInt(collected.toJSON()[0].content) - 1]) {
let clan = clans[parseInt(collected.toJSON()[0].content) - 1];
let data = fs.readFileSync(pathToCsv, "utf8");
console.log(typeof clan);
// let reg = new RegExp(clan, "g");
// let count = (data.match(reg) || []).length;
if (data.split(",").includes(clan)) {
ogMessage.author.send(
"People from this clan are already registered!\nPlease contact the hosts for help!"
);
return;
} else {
teamCheck = true;
}
} else {
ogMessage.author.send("Invalid Clan! Please try again!");
return;
}
})
.catch((collected) => {
try {
console.log("Error" + collected);
} catch (e) {
console.log(e);
}
});
if (teamCheck === false) {
return;
}
I have tried splitting the data, using regular expressions but nothing seems to work on the string returned by
readFileSync()
PS. I am making a discord bot.
In the source string you have pm with a space before it. That's why after calling split("," you end up with the element " pm" in the result array and "pm" is not equal to it.
You just need to trim spaces in all elements before searching some string in it
The problem was in the clans array.
I was defining it as such:
var clans = fs.readFileSync(pathToData, "utf8").split("\n");
but the problem was that the readFileSync() method added an "\r" after every string of the array. That is why it was not able to match the clan string to the data
So what worked was var clans = fs.readFileSync(pathToData, "utf8").split("\r\n");
Now, the array includes only the string, and the includes() method can find a match!

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

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

not equal to not working in postgresql query

if (req.body.positionDetails && req.body.positionDetails.length > 0) {
let total = req.body.positionDetails.length;
for (let i = 0; i < total; i++) {
db.query('SELECT * FROM position WHERE position <> $1',[req.body.positionDetails[i].position],function(err,p) {
console.log(p.rows)
});
}
}
It is selecting all the values from the database without checking the condition. How to solve this??
data is like
"positionDetails":[{"position":"manager"},{"position":"developer"}] and it is from postman.
Your prepared statement looks off to me. Try using ? as a placeholder for the position in your query.
db.query(
'SELECT * FROM position WHERE position <> ?', [req.body.positionDetails[i].position],
function(err, p) {
console.log(p.rows)
});
If this fixes the problem, then I suggest that you were comparing the position against the literal string $1, and every comparison failed resulting in every record appearing in the result set.

Am I paginating through a huge Firebase collection the right way?

I am trying to find a way to iterate all objects from a large collection of data in Firebase Database.
My best attempt follows but I found it odd for several reasons:
startAt() values are always inclusive. So after fetching 100 elements, I had to use my last fetched key as an argument to startAt which results in the last item being fetched again
DataSnapshot's forEach method doesn't allow a callback with an index count as you would think it would based on JS's standards so I had to create a manual index - not sure it will work in every case as i'm not sure if forEach works perfectly synchronously
Here is my code, given the assumption my collection is located at users.
const mapAllTripsPaginated = function (database, childSnapshotCallback, start = '', limit = 100, totalNb = 0) {
return database.ref('/users').orderByKey().startAt(start).limitToFirst(limit).once('value').then((snapshot) => {
let childrenPromises = []
let lastChildKey = null
let idx = 0
snapshot.forEach((childSnapshot) => {
lastChildKey = childSnapshot.key
if (start !== '' && idx === 0) {
// console.log(`Skipping ${childSnapshot.key} as 1st element of page`)
} else {
childrenPromises.push(childSnapshotCallback(childSnapshot))
}
idx = idx + 1
})
return Promise.all(childrenPromises)
.then((result) => {
let newTotal = totalNb + result.length
if (snapshot.numChildren() === limit) {
console.log(`Paginating from ${lastChildKey}`)
return mapAllTripsPaginated(database, childSnapshotCallback, start = lastChildKey, limit = limit, totalNb = newTotal)
} else {
// Done paginating
return newTotal
}
})
})
}
Any idea on how I could make this method more elegant?
Firebase queries are inclusive both for their start and end conditions. You will indeed have to deduplicate the overlapping item on the client.
Firebase's Snapshot.forEach() is a synchronous operation.
I'd normally deduplicate based on already having the key of the item. That will also remove the need for the idx counter.
snapshot.forEach((childSnapshot) => {
if (lastChildKey !== childSnapshot.key) {
childrenPromises.push(childSnapshotCallback(childSnapshot))
}
lastChildKey = childSnapshot.key
})

How do I pass multiple datasets to my view in sailsjs?

I want to be able to pass multiple data sets to my view. Here is how I am currently doing it in my controller:
transactions: function (req, res) {
var queryexpenses = 'select * from expense order by name';
Expense.query(queryexpenses, function (err, expense) {
this.expenses = expense;
});
if (req.param('filter')) {
var where = 'where fk_expense = ' + req.param('expensefilter');
where += ' and datePosted > "' + req.param('yearfilter') + '-01-01" ';
where += ' and datePosted < "' + req.param('yearfilter') + '-12-31" ';
} else {
var where = 'where fk_expense IS NULL';
}
var query = 'select * from accounting ' + where + ' order by description';
Accounting.query(query, function (err, trans) {
this.transactions = trans;
});
var total = 0;
_.each(this.transactions, function (element, index, list) {
// format dates
element.datePosted = dateFormat(element.datePosted, 'dd/mm/yyyy');
var tmp0 = element.amount
var tmp1 = tmp0.replace(/ /g, '');
var tmp2 = parseFloat(tmp1);
total += tmp2;
});
this.total = total.toFixed(2);
return res.view();
}
This is the only way I am able to accomplish what Im trying to do but there are problems which I believe are caused by me putting the query objects in the "this" scope. The first problem is the page will crash after server restart on first reload. The second problem is everything seems to happen one step behind. What I mean is if I issue commands on the UI (eg submit a form) nothing will happen unless I take the same action twice.
So how do I pass multiple sets of data to my views without scoping them in "this"?
res.view({
corndogs: [{name: 'Hank the Corndog'}, {name: 'Lenny the Corndog'}]
});
Here is the relevant docs page: http://sailsjs.org/#!documentation/views
Also, it looks like you're not taking full advantage of Waterline for making SQL queries.

Resources