How do I insert a mysql row from Object (in nodeJS)? - mysql

Right now I use this cumbersome approach when I want to add a row whose data is in a JS Object
Adding a row to a table:
const mysql = require('mysql')
var db = mysql.createConnection(DBInfo)
var databaseObj = {val1: '1', name: 'John', age: 40} // row to insert
var query = 'INSERT INTO my_table ('
var databaseKeys = Object.keys(databaseObj)
for (let i = 0; i < databaseKeys.length; i++) {
query += databaseKeys[i] + (i !== databaseKeys.length - 1 ? ', ' : ')')
}
query += ' ' + 'VALUES('
for (let i = 0; i < databaseKeys.length; i++) {
query += '\'' + databaseObj[databaseKeys[i]] + '\'' + (i !== databaseKeys.length - 1 ? ', ' : ')')
}
db.query(query, function (err, results, fields) {...
Is there any simpler or neater way to add a row into a table, where such row data is in a JS Object? The examples I see around use an array of arrays, but in my case the info is in a Object

I should use the INSERT into table SET because they are equivalent
var db = mysql.createConnection(DBInfo)
var databaseObj = {val1: '1', name: 'John', age: 40}
var query = 'INSERT INTO my_table SET ' + db.escape(databaseObj)
db.query(query, function (err, results, fields) {...

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.

Google Apps Script - How do i get my AdminDirectory.Users.List query to be accepted?

I'm using GAS to create a contact lookup, where the function parameters build a query that is used in AdminDirectory.Users.List . This problem only happens when the orgTitle and orgDepartment parameters are used. When I copy and paste the Logger output in place of the q variable, the query works. If I used the q variable, it fails.
function listAllUsersBase(name = null, familyName = null, givenName = null, orgTitle = 'Help Desk', orgDepartment = null) {
var aQuery = 'query:\''
if (name !== null){
Logger.log('Name: ' + name);
var aQuery = aQuery + 'name:' + name + ' ';
} if (familyName !== null){
Logger.log('familyName: ' + familyName);
var aQuery = aQuery + 'familyName:' + familyName + ' ';
} if (givenName !== null){
Logger.log('givenName: ' + givenName);
var aQuery = aQuery + 'givenName:' + givenName + ' ';
} if (orgTitle !== null){
Logger.log('orgTitle: ' + orgTitle);
var aQuery = aQuery + 'orgTitle:\\\'' + orgTitle + '\\\' ';
} if (orgDepartment !== null){
Logger.log('orgDepartment: ' + orgDepartment)
var aQuery = aQuery + 'orgDepartment:\\\'' + orgDepartment + '\\\' ';
}
var aQuery = aQuery + '\''
var q = aQuery.toString();
Logger.log(q);
var pageToken;
var page;
do {
page = AdminDirectory.Users.list({
domain: 'yourdomain.com',
query: q,
orderBy: 'givenName',
maxResults: 100,
pageToken: pageToken
});
var users = page.users;
if (users) {
for (var i = 0; i < users.length; i++) {
var user = users[i];
Logger.log('%s (%s)', user.name.fullName, user.primaryEmail);
}
} else {
Logger.log('No users found.');
}
pageToken = page.nextPageToken;
} while (pageToken);
}
There are a number of issues with your script.
First, you do not need to re-declare the aQuery variable. The first declaration of var aQuery = ... is sufficient for your use-case.
Second, query is not a valid field for a search clause so it is not necessary (see documentation with list of valid fields available to search clauses). To clarify, when you first declare your aQuery variable you initialized it as var aQuery = 'query:\'';. Here, query is being used as a field in a search clause. However, it is not a valid option in this context.
Third, the position of the opening single-quotes on the field values for orgTitle and orgDepartment are incorrect, the opening escaped single-quote should precede the escaped backslash.
So, instead of:
var aQuery = aQuery + 'orgTitle:\\\'' + orgTitle + '\\\' ';
you should have:
var aQuery = aQuery + 'orgTitle:\'\\' + orgTitle + '\\\' ';

loop to generate a unique url-frinedly route

I have a table in mariadb in this structure,
CREATE TABLE `items` (
`id` char(36), `route` varchar(255), `value` text,
PRIMARY KEY (`id`),
UNIQUE KEY `route` (`route`)
)
I use the route column to get user-friendy urls, such as http://www.examle.com/this-is-the-route-of-an-item
When a user creates a new item, apart from omitting spaces and illegal characters, I would like to "catch" cases where the route chosen for the new item is in use, and generate a valid route.
For example, if route-of-an-item is already in use, i would fallback to route-of-an-item-a, or route-of-an-item-b, etc.
The naive solution could be querying db in a loop, for example (kind of pseudo code):
var additionalChars = "";
while (db.query("select count * from `items` where `route`='" + route + "-" + additionalChars + "'"))
additionalChars = nextAdditionalChars(additionalChars);
finalRoute = route + '-' + additionalChars;
Since this involves querying the db many times I thought of another solution.
var additionalChars = "";
var usedRoutes = db.query("select `route` from `items` where `route` like '" + route + "%'");
while(usedRoutes.contains(route + '-' + additionalChars))
additionalChars = nextAdditionalChars(additionalChars);
finalRoute = route + '-' + additionalChars;
Is there any better way to approach this kind of a problem?
Am I correct that the second solution would perform better?
If I use the second solution, should I add a fulltext index to route field?
You could sort your query by route descending and only retrieve and check one item. In your pseudo code this would look like:
var additionalChars = "";
var usedRoutes = db.query("select `route` from `items` where `route` like '" + route + "%' order by `route` desc limit 1");
if(usedRoutes.route is already in use)
additionalChars = nextAdditionalChars(additionalChars);
finalRoute = route + '-' + additionalChars;
Ok, so after consulting with a colleague I ended up using solution 2, here is the code (node.js), in case anyone faces this problem:
db access
var route = req.body.route || 'noname';
route = route.replace(/[\s_]/g, '-').toLowerCase().replace(/[^0-9a-z\u0591-\u05F4\u0621-\u064A\-_\s]/g, "").replace(/_+/g, ' ').trim().replace(/[\s_]+/g, '-');
var isArabic = (/[\u0621-\u064A]/g).test(route),
isHebrew = (/[\u0591-\u05F4]/g).test(route),
lang = isArabic ? 'ar' : (isHebrew ? 'he' : 'en');
Items.findAll({ where: { route: { $like: route + '%' } }, attributes: ['route'] })
.then((items) => {
var routes = _.keyBy(items, 'route'),
prefix = '';
while (routes[route + (prefix ? '-' + prefix : '')])
prefix = charactersCount(prefix, lang);
Items.create({ route: route + (prefix ? '-' + prefix : ''), value: req.body.value })
.then(function(item){
res.send({ item: _.pick(item, ['id', 'route', 'author_id', 'created_at']) })
})
.catch(function(){ res.sendStatus(500)});
})
.catch(function(){ res.sendStatus(500) });
generate additional characters
var chars = {
ar: { val: "اﻻبتثجحخدذرزسشصضطظعغفقكلمنهةوىي", len: 0 },
he: { val: "אבגדהוזחטיכלמנסעפצקרשת", len: 0 },
en: { val: "abcdefghijklmnopqrstuvwxyz", len: 0 }
};
_.forEach(chars, (c) => { c.len = c.val.length });
function charactersCount (current, lang) => {
if (!current) return chars[lang].val[0];
lang = lang || 'en';
var curr = current.split(''),
len = curr.length,
pointer = len,
lastIndex;
while ((lastIndex = chars[lang].val.indexOf(curr[--pointer]) + 1) >= chars[lang].len) curr[pointer] = chars[lang].val[0];
if (pointer < 0) { curr.unshift(''); pointer++; }
curr[pointer] = chars[lang].val[lastIndex];
return curr.join('');
}
so I end up with one select query and one inset query, and prevent the clashes in node's side.
and a fulltext index is not needed since the % apears only at the end of the like operater

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]
}

Passing a key-value array in NodeJS for MySQL select query

I can pass an array to an mysql insert in nodeJS like so..
var data = {userId: 3, name: "sample"}
db.query('insert into my_table SET ?', data, function(err, result){...}
Is there a similar way of passing an array to a select query in the where clause... without specifying all the fields?
var data = {userId: 3, name: "sample"}
db.query('select * from my_table WHERE ?', data, function(err, result){...}
Doesn't seem to work.. nor does using the SET name in place of where...
database.conn.config.defaultQueryFormat = function (query, values) {
if (!values) return query;
var updatedQuery = query.replace("?", function () {
var whereClause = "";
for(var index in values){
whereClause += mysql.escapeId(index) + " = " + db.escape(values[index]) + " and ";
}
whereClause = whereClause.substring(0, whereClause.length - 5);
return whereClause;
});
return updatedQuery;
};
This appears to work.. e.g.
var val = db.query('select * from my_table where ?', data, function(err, result) {
}