Doesn't execute dynamic mysql query in nodejs - mysql

I am trying to execute a dynamic query needed for an api written using nodejs as server language and mysql as db. The query that i have written is a dynamic query and the query used UNION to join two queries. For some reason, it doesn't take the seconds conditions.where in the sql query and give the below error message.
"You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '?) AND r.ReviewRating IN (?) AND r.Title IS NOT NULL AND r.DateCreated BETWEEN (' at line 1"
Below is the function in my modal class.:
getReviewsAndPhotoReportBySellerId: (sellerId, reviewRating, title, photoAvailable, dateFrom, dateTo, callback) => {
//Dynamic Query Generation based on user input
function buildConditions() {
var conditions = [];
var values = [];
if (typeof sellerId !== 'undefined') {
conditions.push("r.SellerId IN (?)");
values.push(sellerId);
}
if (typeof reviewRating !== 'undefined') {
conditions.push("r.ReviewRating IN (?)");
values.push(reviewRating);
}
if(title == "has comment"){
conditions.push("r.Title IS NOT NULL");
}else if(title == "no comment"){
conditions.push("r.Title IS NULL");
}
if(photoAvailable == 1){
conditions.push("rp.ReviewImageUrl1 IS NOT NULL || rp.ReviewImageUrl2 IS NOT NULL || rp.ReviewImageUrl3 IS NOT NULL");
}else if(photoAvailable == 'undefined'){
conditions.push("rp.ReviewImageUrl1 IS NULL && rp.ReviewImageUrl2 IS NULL && rp.ReviewImageUrl3 IS NULL");
}
if(typeof dateFrom !== 'undefined' && typeof dateTo !== 'undefined' ){
conditions.push("r.DateCreated BETWEEN (?) AND (?) ");
values.push(dateFrom);
values.push(dateTo);
}
return {
where: conditions.length ?
conditions.join(' AND ') : '1',
values: values
};
}
var conditions = buildConditions();
var sql =
'(select *, r.ReviewId AS ActualReviewId, r.DateCreated AS ActualDateCreated from review r LEFT join reviewphoto rp ON r.ReviewId = rp.ReviewId WHERE '
+ conditions.where
+ ') UNION '
+ '(select *, r.ReviewId AS ActualReviewId, r.DateCreated AS ActualDateCreated from review r RIGHT join reviewphoto rp ON r.ReviewId = rp.ReviewId WHERE '
+ conditions.where
+ ') ORDER BY ActualDateCreated DESC ';
console.log(sql);
pool.query(sql, conditions.values, (error, results, fields) => {
if (error) {
callback(error);
}
return callback(null, results);
}
);
},

Related

Using the LIKE operator in MYSQL voids the "id > ? operation"

I am using the like operator to select where like %x% and using the id > ? at the same time but it doesn't work for whatever reason where I keep pulling in the same row of information.
My code is concise and works well I think but the like operator is making it so I keep pulling in the same data (I used = and it worked). Any help please, thank you.
var query = 'SELECT * FROM products WHERE 1 = 1';
var arr = [];
if(typeof(req.body.category) == 'string' && req.body.category.trim() !== '') {
// query += ' AND category LIKE ? OR title LIKE ?'; //doesnt work keep pulling in same data
// arr.push('%' + req.body.category + '%', '%' + req.body.category + '%'); //doesnt work keep pulling in same data
// query += ' AND category = ? OR title = ?'; //works
// arr.push(req.body.category, req.body.category); //works
}
if(typeof(req.body.reset_index) == 'boolean' && req.body.reset_index == true) {
req.session.last_id_main_products = 0;
}
query += ' AND id > ?';
arr.push(req.session.last_id_main_products);
if(
typeof(req.body.longitude) == 'number' &&
typeof(req.body.latitude) == 'number' &&
typeof(req.body.if_location) == 'boolean' && req.body.if_location == true
) {
var left_long = req.body.longitude - 0.3;
var right_long = req.body.longitude + 0.3;
var upper_lat = req.body.latitude + 0.3;
var lower_lat = req.body.latitude - 0.3;
query += ' AND (room_geo_longitude > ? AND room_geo_longitude < ?) AND (room_geo_latitude > ? AND room_geo_latitude < ?)';
arr.push(left_long, right_long, lower_lat, upper_lat);
}
query += ' AND hidden = false LIMIT 1';
pool.query(query, arr, (err, result) => {
if(err) {
res.json(err);
return;
}
if(result.length > 0) {
req.session.last_id_main_products = result[result.length - 1].id;
}
res.json({
room_products: result,
query: query,
arr: arr
});
});
UPDATE: When using query, use parentheses around specific types of operations or else will return bad data.

Mysql NodeJs syncronous queries

Thank you for the answer,
I did that : i used "sync-mysql" :
but now its very very slow...
Maybe i could do the same code using Mysql NPM
Do you know how my code must look like if I want to use asyncronous function and doing the same thing as below ? It will help me a lot :)
I have almost finished my project and I only have this function left
const customer_booked = []
customer_booked[0] = []
customer_booked[1] = []
let sql = "SELECT * " +
"FROM customer as C " +
"WHERE customer_reference REGEXP '^[c]i*' "
if (filters[0].value.length){
sql += "AND C.customer_name LIKE '%" + filters[0].value + "%' "
}
if (filters[3].value.length){
sql += "LIMIT " + filters[3].value
}
var result = connection.query(sql);
const customers = [];
const booked = connection.query('SELECT cr.customer_id, a.codeAgent ' +
'FROM customer_reservation as cr ' +
'INNER JOIN agent as a ' +
'ON a.id = cr.agent_id')
booked.forEach(customer_booking => {
customer_booked[0].push(customer_booking.customer_id)
customer_booked[1].push(customer_booking.codeAgent)
});
result.forEach( customer => {
var months;
let d1 = new Date(customer.last_order);
let d2 = new Date();
months = (d2.getFullYear() - d1.getFullYear()) * 12;
months -= d1.getMonth() + 1;
months += d2.getMonth();
months = months <= 0 ? 0 : months;
if (customer_booked[0].includes(customer.customer_id)){
let code_agent_index = customer_booked[0].indexOf(customer.customer_id)
customer.available = 'booked'
customer._rowVariant = 'warning'
customer.agent_code = customer_booked[1][code_agent_index]
}
else if (months >= 12){
customer.available = 'available'
customer._rowVariant = 'success'
} else {
customer.available = 'notAvailable'
customer._rowVariant = 'danger'
}
let sql2 = "SELECT * " +
"FROM customer_addresses AS CA " +
"WHERE CA.customer_id = " + customer.id
customer.addresses = connection.query(sql2)
customers.push(customer);
//customers[customers.length].push()
})
callback(false, result)
You can use node.js async/await using IIFE, like this:
(async() => {
const users = await getUsers();
for(const user of users){
user.addresses = await getAddresses(user.id);
// your other code just translated to JS.
}
return users;
})()
So, the main idea is to await your async code.
For example we use IIFE (Immediately Invoked Function Expression) to access needed async/await and for tests.
In real code you should name functions with keyword async
Here is nice tutorials which could explain how to use async/await 1, 2

How to build dynamic query by binding parameters in node.js-sql?

I'm using nodejs-mysql module to do query in node.js recently, and in my working case I could only use the parameter-binding syntax like:
SELECT * FROM table WHERE name = ?
Now I want to build dynamic sql with these ? OR ?? parameters. Assume that I have 2 conditions(name and age) which either of them could be null (if user doesn't provide it),
So I want to build MySQL in 3 cases:
only name=Bob: SELECT * FROM table WHERE name = 'Bob'
only age=40: SELECT * FROM table WHERE age > 40
both: SELECT * FROM table WHERE name = 'Bob' AND age > 40
I know it's easy if you build the query on your own, but how can I achieve it when using placeholders which can only bind field or values ?
In document of nodejs-mysql, placeholder ? only stands for values and ?? stands for fields:
https://github.com/felixge/node-mysql/#escaping-query-values
https://github.com/felixge/node-mysql/#escaping-query-identifiers
My first thinking of solution is to insert query piece by using these placeholders, but it comes to failure because both ? and ?? will escape my query piece, and my query will be executed incorrectly.
My code so far is as below, which I'm defenitly sure it's not correct because query piece has been escaped:
// achieve paramters from url request
var condition = {};
if(params.name)condition["name"] = ["LIKE", "%" + params.name + "%"];
if(params.age)condition["age"] = parseInt(params.age, 10);
//build query
var sqlPiece = buildQuery(condition);
//try to replace ? with query
var sql = 'SELECT * FROM table WHERE ?';
connection.query(sql, sqlPiece, function(err, results) {
// do things
});
// my own query build function to proceed conditions
function buildQuery(condition) {
var conditionArray = [];
for(var field in condition){
var con = condition[field];
if(con !== undefined){
field = arguments[1] ? arguments[1] + "." + field : field;
var subCondition;
if(con instanceof Array) {
subCondition = field + " " + con[0] + " " + wrapString(con[1]);
}else{
subCondition = field + " = " + wrapString(con);
}
conditionArray.push(subCondition);
}
}
return conditionArray.length > 0 ? conditionArray.join(" AND ") : "1";
}
//wrap string value
function wrapString(value){
return typeof value === "string" ? "'" + value + "'" : value;
}
So is there any way I can fix this problem?
Update
Thanks to Jordan's Offer, it's working, but :
I know building query by string concat is very good, but in my case I can't use that, because I'm using some middleware or handle mysql and controller, so what I can do is to define interface, which is a sql string with placeholders. So, the interface string is predefined before, and I can't modify it during my controller function.
You're off to a really good start, but you may have been overthinking it a bit. The trick is to build a query with placeholders (?) as a string and simultaneously build an array of values.
So, if you have params = { name: 'foo', age: 40 }, you want to build the following objects:
where = 'name LIKE ? AND age = ?';
values = [ '%foo%', 40 ];
If you only have { name: 'foo' }, you'll build these instead:
where = 'name LIKE ?';
values = [ '%foo%' ];
Either way, you can use those objects directly in the query method, i.e.:
var sql = 'SELECT * FROM table WHERE ' + where;
connection.query(sql, values, function...);
How do we build those objects, then? In fact, the code is really similar to your buildQuery function, but less complex.
function buildConditions(params) {
var conditions = [];
var values = [];
var conditionsStr;
if (typeof params.name !== 'undefined') {
conditions.push("name LIKE ?");
values.push("%" + params.name + "%");
}
if (typeof params.age !== 'undefined') {
conditions.push("age = ?");
values.push(parseInt(params.age));
}
return {
where: conditions.length ?
conditions.join(' AND ') : '1',
values: values
};
}
var conditions = buildConditions(params);
var sql = 'SELECT * FROM table WHERE ' + conditions.where;
connection.query(sql, conditions.values, function(err, results) {
// do things
});
For Inserting into MYSQL like DB:
function generateInsertQuery(data, tableName) {
let part1 = `INSERT INTO ${tableName} (`;
let part2 = ")",
part3 = "VALUES (",
part4 = ")";
let tableKeys = "",
tableValues = "";
for (let key in data) {
tableKeys += `${key},`;
tableValues += `'${data[key]}',`
}
tableKeys = tableKeys.slice(0, -1);
tableValues = tableValues.slice(0, -1);
let query = `${part1}${tableKeys}${part2} ${part3}${tableValues}${part4}`;
return query;
}
generateInsertQuery({name: "Sam", tel: 09090909, email: "address#domain.com"}, "Person")
Output:
INSERT INTO Person (name,tel,email) VALUES ('Sam','9090909','address#domain.com');
Code Snippet for Update query:
function generateUpdateQuery(data, tableName, clauseKey, clauseValue) {
let part1 = `UPDATE ${tableName} SET`;
let part2 = `WHERE ${clauseKey} = ${clauseValue};`; //Add any number of filter clause statements here
let updateString = "";
for (let key in data) {
updateString += `${key} = '${data[key]}',`;
}
updateString = updateString.slice(0, -1);
let query = `${part1} ${updateString} ${part2}`;
return query;
}
generateUpdateQuery({
name: "Tanjiro",
tel: 77777777,
email: "tanjiro#demonslayer.com"
}, "Person", "ID", 111);
Output:
UPDATE Person SET name = 'Tanjiro',tel = '77777777',email = 'tanjiro#demonslayer.com' WHERE ID = 111;
I modify your code #Jordan-Running
describe("Test generateFilterQuery", () => {
it("Query filter with params", () => {
let params = []
params.push(Query.generateParams("title", "%_%", "Coding"))
params.push(Query.generateParams("published", "=", true))
console.log(Query.generateFilterQuery(params))
});
});
const qInclude = require('./QueryInclude');
exports.generateParams = (name, eq, value) => {
return {
name: name,
eq: eq, // %_%, %_, _%, =, >, <, !=,
value: value
}
}
exports.generateFilterQuery = (params) => {
let conditions, values = []
let conditionsStr;
if (params.length == 0) {
return false
}
[conditions, values] = qInclude.queryCondition(params)
let build = {
where: conditions.length ?
conditions.join(' AND ') : '1',
values: values
};
let query = 'SELECT * FROM table WHERE ' + build.where;
return [query, build.values]
}
exports.queryCondition = (params) => {
var conditions = [];
var values = [];
params.forEach(item => {
switch (item.eq) {
case '=': {
conditions.push(item.name + " = ?");
values.push(item.value);
break;
}
case '!=': {
conditions.push(item.name + " != ?");
values.push(item.value);
break;
}
case '<': {
conditions.push(item.name + " < ?");
values.push(item.value);
break;
}
case '>': {
conditions.push(item.name + " > ?");
values.push(item.value);
break;
}
case '%_%': {
conditions.push(item.name + " LIKE ?");
values.push("%" + item.value + "%");
break;
}
case '%_': {
conditions.push(item.name + " LIKE ?");
values.push("%" + item.value);
break;
}
case '_%': {
conditions.push(item.name + " LIKE ?");
values.push(item.value + "%");
break;
}
}
});
return [conditions, values]
}

LINQ Take syntax

I have written some LINQ to simulate an SQL GroupBy statement (see below). However, I also need to only consider only the last 10 settingIds before doing my group by. I think I would use Take to do this, but what would be the correct syntax in my statement?
var settings2 = from s in dc.SystemSettings
where s.campConfig.campaignType == campType
&& s.campId != campId
&& s.settingKey == ticket.setting.Key
orderby s.settingId descending
group s by s.settingValue
into grp
select new
{
SettingValue = grp.Key,
SettingCount = grp.Select(x => x.settingValue).Count()
};
I would do something like this
var settings2 = from sOuter in
(from s in dc.SystemSettings
where s.campConfig.campaignType == campType
&& s.campId != campId
&& s.settingKey == ticket.setting.Key
orderby s.settingId descending
select s).Take(10)
group sOuter by sOuter.settingValue
into grp
select new
{
SettingValue = grp.Key,
SettingCount = grp.Select(x => x.settingValue).Count()
};

LINQ to SQL Query Where Clause

I would like to create a single query that "adjusts" it's where clause based on a tuple. The first item in the tuple contains a enum value indicating the field in which to filter. The second tuple item is the filter value.
Notice the query below does not work:
var query = from p in db.Categories
where ( QueryBy.Item1 == CategoryFields.Name && p.Name == (string)(QueryBy.Item2) ) ||
( QueryBy.Item1 == CategoryFields.Id && p.Id == (long)(QueryBy.Item2) ) ||
( QueryBy.Item1 == CategoryFields.Description && p.Description == (string)(QueryBy.Item2) ) ||
( QueryBy.Item1 == CategoryFields.SortOrder && p.SortOrder == (int)(QueryBy.Item2) )
select...
if (query.Count() == 1) // ERRORS HERE CONVERSION OF INT
A similar query with only this where clause change will works:
var query = from p in db.Categories
where ( QueryBy.Item1 == CategoryFields.Name && p.Name == (string)(QueryBy.Item2) )
select...
if (query.Count() == 1) // Works HERE
Any idea what could be wrong? Can it be that LINQ where clause perform a short-circuit evaluation and thus the cast of item2 fails? Is there a better way to accomplish my overall goal of adjusting where clause?
Thanks in advance for your help!
LINQ to SQL isn't smart enough to optimize your query and generate it dynamically based on the value of your QueryBy.Item1. It will simply generate a SQL query let SQL server decide this for itself.
When you know that, the error makes sense, since it's impossible for one single value to be castable to both int, long, and string.
In your case you would be better of dynamically generating the right where clause. You can do this with the PredicateBuilder:
IQueryable<Category> query = db.Categories;
var whereClause = PredicateBuilder.False<Category>();
switch (QueryBy.Item1)
{
case CategoryFields.Name:
long id = (string)QueryBy.Item2;
whereClause = whereClause.Or(p => p.Name == name);
break;
case CategoryFields.Id:
string name = (string)QueryBy.Item2;
whereClause = whereClause.Or(p => p.Id == id);
break;
case CategoryFields.Description:
string des = (string)QueryBy.Item2;
whereClause =
whereClause.Or(p => p.Description == des);
break;
case CategoryFields.Id:
string sort = (int)QueryBy.Item2;
whereClause =
whereClause.Or(p => p.SortOrder == sort);
break;
}
query = query.Where(whereClause);