Npm mysql - can't I query WHERE conditions using a single object? - mysql

I am using express and npm mysql (Link)
I want to do a call using
query('SELECT * FROM TABLE WHERE ?', where, cb)
where is a javascript object f.e. : {col1: 'one', col2: 'two'}
But it seems that this doesn't work. It works for SET though to update multiple columns at once.
I want a general method where I can send a different combination of columns to search. I was wondering if there is a built in method to do this.

Meanwhile, I created this script:
var andQuery = "";
var andParams = [];
var isFirst = true;
for (var filter in where) {
if (where.hasOwnProperty(filter)) {
if(!isFirst) {
andQuery += " AND ? ";
} else {
andQuery += " ? ";
isFirst = false;
}
andParams.push({[filter]: where[filter]});
}
}
db.query(
'SELECT * FROM `Table` WHERE ' + andQuery, andParams, (err, results) => {

Related

Node function inputting undefined variables into SQL database

I'm sure this is a simple error in my syntax but I'm currently using a nodejs function to input into my SQL database, however, while the overall query works, and some variables get input correctly, a couple are input as undefined, which has thrown me for a loop. I'll input the query below and I presume I either added extra punctuation where not required or something.
con.query("INSERT INTO _rounds(roundnum, roundse, roundtk, winner) VALUES('"+ roundnumres +"', '"+ roundse +"', '"+ roundtk +"', '"+ roundwinner +"')", function (err, result) {
});
For more information, the roundnumres and the roundtk variables are the ones inserted as undefined, and are both defined by a random string generator which looks as follows:
function makese(length) {
var roundse = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for ( var d = 0; d < length; d++ ) {
roundse += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return roundse;
}
var roundse = makese(20);
Any help would be appreciated!
you could do this. you don't have to concat strings using plus
const query = `INSERT INTO _rounds(roundnum, roundse, roundtk, winner) VALUES('${roundnumres}', '${roundse}, '${roundtk}', '${roundwinner}')"
con.query(query, () => {})

Update table with myCon.query using an array of set values

I am trying to use an array of values to populate an escaped sql query. The array being accepted.
I was using:
function genUpdateString(settings) {
var sql = "";
var first = true;
for (attr in settings) {
if (
settings[attr] != undefined &&
attr != "userid" &&
attr != "api_key"
) {
if (first) {
first = false;
} else {
sql += ", ";
}
sql += attr + "='" + settings[attr] + "'";
}
}
return sql;
}
sql_update = genUpdateString(...);
var sql = "UPDATE user SET " + sql_update + " WHERE name = '" + newSettings.userid + "'";
myCon.query(sql, (err, result) => {
This works fine but when I tried to move to the escaped format it crashes:
var sql = "UPDATE user SET ? WHERE name = ?";
myCon.query(sql, [sql_update, userid], (err, result) => {
When I create the string manually it runs through fine and updates my database tables but using the second method it crashes with the error:
Error: ER_PARSE_ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''age=\'33\'' WHERE name = 'Josephy Krakowski'' at line 1
Your genUpdateString function isn't SQL injection safe either – just adding single quotes isn't secure.
Something like this is – we're generating the query fragment with question marks and the args "in parallel".
function genUpdateAndArgs(map) {
const names = [];
const args = [];
for(var key in map) {
const value = map[key];
if(value !== undefined) {
names.push(key);
args.push(value);
}
}
// ['foo', 'bar'] => 'foo = ?, bar = ?'
const namesFragment = names.map((name) => `${name} = ?`).join(', ');
return [namesFragment, args];
}
const settings = {
'foo': 123,
'bar': 456,
};
const userId = 1337;
const [namesFragment, updateArgs] = genUpdateAndArgs(settings);
const query = `UPDATE user SET ${namesFragment} WHERE name = ?`;
const args = [].concat(updateArgs).concat(userId);
console.log(query, args);
The output is
UPDATE user SET foo = ?, bar = ? WHERE name = ?
[ 123, 456, 1337 ]
which you could then plug into db.query...
However I really heartily recommend a query builder like Knex instead – in Knex, the same would be approximately
knex('user')
.where('name', userId)
.update(settings)

How to use result array of a query in another query in Mysql+Node JS?

I want to use the result of first query(sql1) in second query(sql2). The result of first query(sql1) is getting in .ejs page but no result for the second query(sql2).
var sql1 = 'SELECT * FROM `Rooms`';
con.query(sql1,function(err,rows,fields){
if(!err)
{
var count=rows.length;
for(i=0;i<count;i++)
{
arr_room[i]=rows[i].room_name;
}
}
else
{
console.log('error...');
}
});
var sql2 = 'SELECT * FROM Lights WHERE room_name =? ' ;
con.query(sql,arr_room[0],function(err,rows,fields){
if(!err)
{
var count=rows.length;
for(i=0;i<count;i++)
{
arr_icon[i]=rows[i].icon;
}
}
else
{
console.log('error...');
}
res.render('lighting',{arr_icon,arr_room});
});
You need to nest sql2 into sql1, in nodejs everything is asynchronous, that means you must wait for something to finish first.
And you had a typo on the second query, you called sql instead of sql2
var sql1 = 'SELECT * FROM `Rooms`';
con.query(sql1, function(err, rows, fields) {
if (!err) {
var count = rows.length;
//for (i = 0; i < count; i++) {
// arr_room[i] = rows[i].room_name;
//}
if (count) {
// simplified version instead of for
var arr_room = rows.map(i => i.room_name);
// you can safely use it inline since data comes straight from the database
// query will use `in` condition: room_name in ('roomX','roomY', 'roomZ')
var sql2 = `SELECT * FROM Lights WHERE room_name in ('${arr_room.join("','")}') `;
con.query(sql2, function(err, rows, fields) {
if (!err) {
var count = rows.length;
for (i = 0; i < count; i++) {
arr_icon[i] = rows[i].icon;
}
} else {
console.log('error...');
}
res.render('lighting', {
arr_icon,
arr_room
});
});
} else {
console.log('no records to pass for sql2');
}
} else {
console.log('error...');
}
});
I know this question has an answer already, but I wanted to contribute a SQL-only solution: using a subquery instead of executing two separate queries and operating on their results in Node.js.
SELECT
*
FROM lights
WHERE
room_name IN (
SELECT room_name FROM rooms
);

Use Sequelize on for loop findAll query and merge result

I'm coding opensource project in the university course
It is a function to search the value of another table by dividing input keyword by comma.
under this example data
Python,CPP,Csharp
var keyword = result[0].keyword;
var keyword_arr = [];
var keyword_split = keyword.split(',');
for (var i in keyword_split)
{
keyword_arr.push(keyword_split[i]);
}
I have succeeded in separating them with commas like above, but I'm looking for a loop in sequelize.
"Error: Can not set headers after they are sent."
An error is returned and is not executed.
I want to output the results merged. What should I do?
my code is
for (i = 0; i < keyword_arr.length; i++) {
query += models.contents.findAll({
where: {keyword: {like: '%' + keyword_arr[i] + '%'}},
raw: true
});
}
Regards.
You were in the right direction , but here it his how you can do :
queries = [];
for (i = 0; i < keyword_arr.length; i++) {
queries.push({keyword: {like: '%' + keyword_arr[i] + '%'}});
}
models.contents.findAll({
where: {
$or : queries
}
raw: true
}).then(results => {
console.log(results); // <---- Check this
})
NOTES :
models.contents.findAll() //<---- Returns promises
You can't just combine the promises by += as its not string or number
like that
In your case , it will create and run the query for each tag , so
that's not proper way of doing , you should combine the tags and create a single query as I did

Why aren't my Fusion Table Layers and Styles working?

I am currently using a Google Maps Fusion Table for County information to display the county boundaries for Texas. There are over 200+ counties in Texas. I am looping through an array of values for each county and need to color-code the county based on the value in the array. There are 4 levels of colors for the county: Stop, Warning, Watch and Open. Everything seems to be working, except that the color is only being applied to 5 counties. The limit of styles is 5 and the limit of layers is also 5, but I am only using 1 layer and 4 styles.
Can someone tell me what I am dong wrong? Or is this just not possible via the API?
Below is a snippet of the code:
var styles = new Array();
var ftLayer = new google.maps.FusionTablesLayer();
function loadTexas() {
loadFusionLayer('TX');
ftLayer.setMap(map);
for (var i = 0; i < aryCounty.length; i++) {
styleLayer("'State Abbr.' = 'TX' AND 'County Name' = '" +
aryCounty[i].County + "'", 1000);
}
ftLayer.set('styles', styles);
}
function loadFusionLayer(state) {
if (state != null) {
where = "'State Abbr.' IN ('" + state + "')";
}
var select = "geometry, 'State Abbr.', 'County Name'";
ftLayer.setOptions({
query: {
select: select,
from: countyTableId,
where: where
}
});
}
function styleLayer(where, actualValue) {
var color = setPolygonColorBy(actualValue);
styles.push({
where: where,
polygonOptions: {
fillColor: color,
fillOpacity: 0.6
}
});
}
function setPolygonColorBy(actualValue, divisor) {
var status;
var stop = STATUS_LAYER_STYLES["Stop"].color;
var warning = STATUS_LAYER_STYLES["Warning"].color;
var watch = STATUS_LAYER_STYLES["Watch"].color;
var open = STATUS_LAYER_STYLES["Open"].color;
if (actualValue >= minValue && actualValue < midValue) {
status = watch;
}
else if (actualValue >=midValue && actualValue < maxValue) {
status = warning;
}
else if (actualValue >= maxValue) {
status = stop;
}
else {
status = open;
}
return status;
}
You really only have 4 styles. You need to get the dollar value for each county into your own Fusion Table. You could download the US Counties Fusion Table, perhaps only the TX counties and create a new FT. Then add you own dollar value column. (A simpler better approach would be to merge your actualValues with the Counties table, but I'm not famiiar with merging table. You need your actual Values and the State-County key values. The merge should create a new table owned by you)
Then you can create your 4 styles as described in the Maps FT Docs.
There may be syntax errors here.
ftLayer = new google.maps.FusionTablesLayer({
query: {
select: 'geometry',
from: countyTable_with_actualValues_Id },
styles: [{
// default color
where: "'State Abbr.' ='" + state + "'";
polygonOptions: {
fillColor: open
}
}, {
where: "'State Abbr.' ='" + state + "' AND actualValue >= " + maxValue;
polygonOptions: {
fillColor: stop
}
}, {
where: "'State Abbr.' ='" + state + "' AND actualValue >= " + minValue + "AND actualValue < " + midValue;
polygonOptions: {
fillColor: watch
},
// Final condition left as an exercise :-)
}]
});
ftLayer.setMap(map);
Answer Update:
If your company does not want to push the data out and you are writing an internal application that is only available to employees, then you don't want to use Fusion Tables. The data in Fusion Tables is generally available to the public and is published one of two ways:
Follow-up To Your May 25, 2012 Comment (if you decide to move forward with FusionTables):
With your comments, I understand better what you are doing. I believe the code you have in the loadTexas function that is calling the styleLayer function to create the FusionTablesStyle objects is correct.
The part that looks suspicious is the ordering of the code in the loadTexas function and the loadFusionLayer function. There is also a missing var where declaration in the loadFusionLayer function. I don't think it is causing a problem (at least not in the code you have shown), but it does inadvertently create a global, so corrected that problem in the code that follows. I suggest making the following changes:
Create a new var fusionTablesOptions in the global space and use fusionTablesOptions to set up all of the options for the ftLayer FusionTablesLayer.
Change the declaration of var ftLayer so that it is assigned to: null.
Iterate the aryCounty array, build the styles array, and assign it to the fusionTablesOptions.styles property.
Assign fusionTablesOptions.map to the existing instance of google.maps.Map named: map (and remove this line of code in the loadTexas function: ftLayer.setMap(map);).
Assign fusionTablesOptions.query to the object that is built in the loadFusionLayer function.
Now that all of the necessary fusionTablesOptions properties have been set, create a new instance of google.maps.FusionTablesLayer, passing fusionTablesOptions to the constructor function.
var styles = new Array();
//Step 1:
var fusionTablesOptions = {};
//Step 2:
var ftLayer = null;
function loadTexas() {
//Step 3:
for (var i = 0; i < aryCounty.length; i++) {
//Call to the styleLayer function not shown to reduce code size
}
fusionTablesOptions.styles = styles;
//Step 4:
fusionTablesOptions.map = map;
loadFusionLayer('TX');
}
function loadFusionLayer(state) {
var where = null; //Corrects the missing var keyword
if (state != null) {
where = "'State Abbr.' IN ('" + state + "')";
}
var select = "geometry, 'State Abbr.', 'County Name'";
//Step 5:
fusionTablesOptions.query : {
select: select,
from: countyTableId,
where: where
}
//Step 6:
ftLayer = new google.maps.FusionTablesLayer( fusionTablesOptions );
}
Apologies for the large answer, but I wanted to make sure I conveyed all of my suggestions clearly in a way thought would be easy to review or implement.