Return showing undefined javascript - javascript

im writing a discord bot to check points from a database then return them. This is my code at the moment.
function userCheck(id, name) {
date = new Date().toISOString().slice(0, 19).replace('T', ' ');
con.query("SELECT id FROM users WHERE id = "+id, function (err, row) {
if (!(row && row.length) ) {
con.query("INSERT INTO `users` (id,name,rank,points,signup) VALUES ('"+id+"', "+name+", '0', '0' , '"+date+"')"), function (err, result, fields){
if (err) throw err;
}
}else{
//fine
}
});
}
function checkPoints(user){
id = user.id;
name = con.escape(user.username);
userCheck(id, name);
console.log("SELECT points FROM users WHERE id = "+id);
con.query("SELECT points FROM users WHERE id = "+id, function (err, result, fields){
return result[0].points;
});
}
My code which calls these functions is this:
return message.author.send(checkPoints(message.author));
This makes discordjs error as it is trying to return an empty message. This means my functions arent returning right. Ive look at this over and over its probably just a simple fix but I cant see it.
Thanks in advance

function checkPoints(user){ doesn't return anything i.e. same as return undefined
Since con.query is asynchronous - the simplest fix is to use a callback, like so
function checkPoints(user, cb){
id = user.id;
name = con.escape(user.username);
userCheck(id, name);
console.log("SELECT points FROM users WHERE id = "+id);
con.query("SELECT points FROM users WHERE id = "+id, function (err, result, fields){
cb(result[0].points);
});
}
checkPoints(message.author, function(result) {
message.author.send(result);
});
or, use Promise
function checkPoints(user){
return new Promise(function(resolve, reject) {
id = user.id;
name = con.escape(user.username);
userCheck(id, name);
console.log("SELECT points FROM users WHERE id = "+id);
con.query("SELECT points FROM users WHERE id = "+id, function (err, result, fields){
if(err) return reject(err);
resolve(result[0].points);
});
});
}
checkPoints(message.author)
.then(function(result) {
message.author.send(result);
});

Related

Creating my own sql wrapper for nodejs/express

I'm trying to create my own wrapper for mysql for my nodejs application. I have two questions here one of which theres a work around and one where I'm unsure what to do as my javascript skills are still in the learning phase.
First thing: As of right now when you navigate to /api/finance it directs you to the finance controller and the index method. This is currently just for testing purposes trying to figure out how to this kind of stuff.
FinanceController:
const sql = require('../../sql.js')
module.exports = {
index: (req, res, next) => {
sql.get('test').then((result) => {
res.status(200).json(result);
})
}
}
sql.js
const mysql = require('mysql');
const { DB } = require('./config')
var connection = mysql.createConnection(DB)
module.exports = {
get: function(table, columns = '*') {
return new Promise((resolve, reject) => {
connection.query('SELECT ?? FROM ?? ', [columns, table], function (error, results, fields) {
if (error) throw error;
resolve(results);
});
})
},
all: function(table) {
return new Promise((resolve, reject) => {
connection.query('SELECT * FROM ?? ', table, function (error, results, fields) {
if (error) throw error;
resolve(results);
});
})
},
where: function(){
console.log('where called')
}
}
As you can see, I have a get() and all(). get() allows you to pass the table name and an array of columns for example: ['id', 'name'] would get you the id column and name column. columns = '*' was an attempt on being able to use one function to either get all columns of the table or specify specific columns however it returns an error: Unknown column in 'field list' so all() was my "workaround" however i'd like it to be one function.
Next I can't figure out how to stack/pipe methods? if thats the word.
The goal here would be so I could call the function like this:
index: (req, res, next) => {
sql.all('test').where().then((result) => {
res.status(200).json(result);
})
}
}
obviously within the .where() I would have it like: .where('id', '=', 'userID') or something along those lines.
however I'm unsure on how to go about doing that and would like some guidance if its possible. I receive the error: sql.all(...).where is not a function
Instead of immediately launching the SQL, you should simply register the provided information in an instance object (having the same methods) and return that object, and let each method enrich the SQL until the end of the chain is reached and you call a method that will launch the SQL.
The object that is passed from one method to the next (as this) maintains state, and collects the different elements of the SQL statement.
Here is one way to do it.
NB: In this demo I used a mock-object for connection. This mock object will not actually connect to a database. Its query method will just produce the final SQL (with all ? resolved) instead of a real data set.
// Mock connection object for this snippet only. Just produces the final SQL, not the result
var connection = {
query(sql, params, callback) {
let i = 0;
sql = sql.replace(/\?\??/g, (m) => {
if (m.length > 1) return [].concat(params[i++]).map(p => "`" + p + "`").join(", ");
if (typeof params[i] === "string") return "'" + params[i++].replace(/'/g, "''") + "'";
return params[i++];
});
setTimeout(callback(null, sql));
}
}
// Function to create an instance when a first method call is made on the `sql` object directly
// Chained method calls will keep using that instance
function getInstance(inst) {
if (inst.columns) return inst; // Keep using same instance in the chain
inst = Object.create(inst); // No instance yet: create one
inst.table = null;
inst.params = [];
inst.columns = [];
inst.conditions = [];
inst.order = [];
return inst;
}
// This sql object serves a starting point as well
// as proto object for the instance object that
// passes through the chain:
var sql = {
from(table) {
let inst = getInstance(this);
inst.table = table;
return inst;
},
select(...columns) {
let inst = getInstance(this);
inst.columns = inst.columns.concat(columns);
return inst;
},
where(column, cmp, value) {
if (!["<",">","<=",">=","=","!="].includes(cmp)) throw "invalid operator";
let inst = getInstance(this);
inst.params.push(column, value);
inst.conditions.push(cmp);
return inst;
},
orderBy(...columns) {
let inst = getInstance(this);
inst.order = inst.order.concat(columns);
return inst;
},
promise() {
if (!this.table) throw "no table specified";
// build SQL and parameter list
let sql = "SELECT *";
let params = [];
if (this.columns.length && this.columns != "*") {
sql = "SELECT ??";
params.push(this.columns);
}
sql += " FROM ??";
params.push(this.table);
if (this.conditions.length) {
sql += " WHERE " + this.conditions.map(cmp => `?? ${cmp} ?`).join(" AND ");
params.push(...this.params);
}
if (this.order.length) {
sql += " ORDER BY ??";
params.push(this.order);
}
return new Promise(resolve => {
connection.query(sql, params, function (error, results) {
if (error) throw error;
resolve(results);
});
});
}
};
// demo
sql.from("customer")
.select("id", "name")
.where("name", ">", "john")
.where("name", "<", "thomas")
.orderBy("name", "id")
.promise()
.then(console.log);
Note that in this implementation it does not matter in which order you chain the from, select, where and order method calls. You could even do the following if you wanted to:
sql .orderBy("name", "id")
.where("name", ">", "john")
.from("customer")
.where("name", "<", "thomas")
.select("id", "name")
.promise()
.then(console.log);

Checking existing element in database with Node.js and MySQL

so here's the issue, everything inside the con.query(query, function (err, result, fields) is never called. So this is the part of code that is about verifying that when the user is signing up, the email that he took is not already taken. argv[0] contains the email that the user typed (it's not wrong, I debugged it). So I obviously created two accounts with the same email but the second one is never flagged (the function is continuing instead of returning "userexists").
var mysql = require('mysql');
var con = mysql.createConnection(Credentials). // 👈 The connection is established, it's not a problem.
var query = "SELECT id FROM accounts_data WHERE email = '" + argv[0] + "'";
var r;
con.query(query, function (err, result, fields) {
if (err)
return "failed";
if(result != undefined)
return "userexists" // The if is never checked
});
Thank you.
EDIT:
Hello everyone, so the console.log(results.length) is printing the right result, but how can I give the result to r? Because the last console.log(r) is still printing 0.
var r = 0;
var sql = 'SELECT * FROM accounts_data WHERE email = ' + con.escape(argv[0]);
con.query(sql, function (error, results, fields) {
if (error) throw error;
console.log(results.length); // Right result
r = results.length; // Not giving the result to r
});
console.log(r); // Not the right result
Try using Promise's, they'll help get away from 'callback-hell'
const userExists = async emailAddress => new Promise((resolve, reject) => {
con.query(`
SELECT EXISTS(
SELECT id FROM accounts_data WHERE email = ?
);
`,
[emailAddress],
(err, result) => {
if (err) { reject(err); }
resolve(result);
}
)
);
await userExists('abc#example.com') // => boolean

Callback until for loop with query is done

I posted a question before and realized my problem actually was async functions. I managed to work out most of it, but I got one little problem left. Using async I used waterfall to create an order for the some queries...
exports.getMenu = function(id_restaurant, callback){
async.waterfall([
async.apply(firstQuery, id_restaurant),
secondQuery,
thirdQuery,
fourthQuery,
formMenu
], function(err, result){
if(err){
console.log(err);
}
callback(result);
});
};
Everything works until fourthQuery, where I have to loop to get all dishes of a menu.
function fourthQuery(array_totalP, array_nombresSecc, array_secciones, callback){
var size = array_nombresSecc.length;
var array_secciones = array_secciones;
var array_nombresSecc = array_nombresSecc;
var dishes = [];
pool.getConnection(function(err, connection) {
if(err) {
console.log(err);
callback(true);
return;
}
for (var i = 0; i < size; i++) {
connection.query("SELECT name, price FROM menu_product WHERE id_seccion = ? AND active = 1", [array_secciones[i]],
function(err, results2) {
if(err) {
console.log(err);
callback(true);
return;
}
console.log("Result query 4 " + JSON.stringify(results2));
dishes[i] = results2;
console.log("VALOR PLATILLOS EN i : " + JSON.stringify(dishes[i]));
// this prints the result but only if it has a value over 2
});
};
}); // pool
console.log("I'm sending " + dishes); // this logs an empty array
callback(null, dishes, array_nombresSecc);
};
So what i can see that happens from printing the value of 'i' each loop is that it always has the value of 2. Because that's 'size' value. Also, even though it's saving results of index '2' I believe the callback is being done even before the for loop is done, because my fifth function is recieving an empty array.
How can i make my code wait to callback until my for loop is done?
NOTE: Sorry, part of my code is in spanish, tried to translate the important parts of it.
There are a few ways to handle this, one is to look into promise architecture. Promise.all will let you supply one callback to handle the values from each child promise.
To use what you've already got, however, I'd push the values into your dishes array, rather than assigning them specifically to i indexes, then check the size of that array at the end of each connection. When the array length matches the size, fire the callback. (as seen below)
If you need a way to tie each result to that specific i value, I'd recommend pushing them as an object
dishes.push({'index': i, 'dish': results2})
Afterward, if you need the array of just dishes, you can sort the array by that index value and run a map function.
dishes.sort(function(a,b){ return a.index - b.index; })
dishes = dishes.map(function(a){ return a.dish })
Here's the code adjusted:
function fourthQuery(array_totalP, array_nombresSecc, array_secciones, callback) {
var size = array_nombresSecc.length;
var array_secciones = array_secciones;
var array_nombresSecc = array_nombresSecc;
var dishes = [];
pool.getConnection(function(err, connection) {
if (err) {
console.log(err);
callback(true);
return;
}
for (var i = 0; i < size; i++) {
connection.query("SELECT name, price FROM menu_product WHERE id_seccion = ? AND active = 1", [array_secciones[i]],
function(err, results2) {
if (err) {
console.log(err);
callback(true);
return;
}
console.log("Result query 4 " + JSON.stringify(results2));
dishes.push(results2)
if(dishes.length == size){
console.log("I'm sending " + dishes);
callback(null, dishes, array_nombresSecc)
}
console.log("VALOR PLATILLOS EN i : " + JSON.stringify(dishes[i]));
// this prints the result but only if it has a value over 2
});
};
}); // pool
;
};
Since you're already using the async, I would suggest replacing the for() loop in fourthQuery with async.each().
The updated fourthQuery would look like this:
function fourthQuery(array_totalP, array_nombresSecc, array_secciones, callback){
var size = array_nombresSecc.length;
var array_secciones = array_secciones;
var array_nombresSecc = array_nombresSecc;
var dishes = [];
pool.getConnection(function(err, connection) {
if(err) {
console.log(err);
callback(true);
return;
}
async.each(array_secciones,
function(item, itemCallback) {
// Function fun for each item in array_secciones
connection.query("SELECT name, price FROM menu_product WHERE id_seccion = ? AND active = 1", [item],
function(err, results2) {
if(err) {
console.log(err);
return itemCallback(true);
}
console.log("Result query 4 " + JSON.stringify(results2));
dishes.push(results2);
console.log("VALOR PLATILLOS EN i : " + JSON.stringify(dishes[dishes.length-1]));
// this prints the result but only if it has a value over 2
return itemCallback();
});
},
function(err) {
// Function run after all items in array are processed or an error occurs
console.log("I'm sending " + dishes); // this logs an empty array
callback(null, dishes, array_nombresSecc);
});
}); // pool
};
Alternatively, you can use async.map(), which handles gathering the results in the final callback so doesn't rely on the dishes variable.

ER_DUP_ENTRY even after checking if key exists using SELECT

thank you for taking the time to read my question.
I have an array with records that I want to put into MYSQL database from NodeJS. These records have a jobkey field that I want to be UNIQUE, but I don't want it to be the PRIMARY KEY since the jobkey coming from an API and I want to use my own recid for better indexing and sorting.
I am using NodeJS to first check if the jobkey exists by doing a SELECT WHERE and checking if rows.length > 0? then adding the job if jobkey does not already exist. For some reason I keep getting a ER_DUP_ENTRY error and I can't figure out why my rows.length sometimes === 0 even though the jobkey exists. I have been hitting my head against the wall now for a few days. I hope it's not just a semi-colon or one of those simple errors I have overlooked. Anyways I need help please look at my code.
function is_job_key(_jobkey, cb){ // cb(err, flag)
var sql = 'SELECT * FROM tbl_indeed WHERE ?';
conn.query(sql, {jobkey: _jobkey}, function(err, rows){
if(err){
console.log(err);
return cb(true);
}
bar.tick();
if(rows.length > 0){ // jobkey exists
return cb(null, true);
}
cb(null, false);
});
}
function insert_job(posts, cb){ // cb(err)
var sql = 'INSERT INTO tbl_indeed SET ?';
conn.query(sql, posts, function(err, result){
if(err){
console.log(err);
return cb(true);
}
bar.tick();
cb(null);
});
}
function insert_all_jobs(job_arr, query_str, cb){ // cb(err, count_inserted)
function get_posts(job){
var posts = {jobkey: job.jobkey, snippet: job.snippet, url: job.url, jobtitle: job.jobtitle, company: job.company, date: job.date, query_string: query_str};
return posts;
}
bar = new progress(' ' + query_str + ' (:current / :total) [:bar] :percent :etas', {
complete: '=',
incomplete: ' ',
width: 40,
total: job_arr.length*2
});
var count_inserted = 0;
async.each(job_arr, function(job, cb){
var posts = get_posts(job);
is_job_key(job.jobkey, function(err, result){
if(err) return cb(true);
if(result){
bar.tick();
return cb(); // jobkey exists
}
insert_job(posts, function(err){
if(err) return cb(true);
count_inserted++;
cb();
});
});
}, function(err){
if(err) return cb(true);
console.log('done');
conn.end();
cb(null, count_inserted);
});
}
var job_arr = JSON.parse(fs.readFileSync('technician.txt', 'utf-8'));
//job_arr = job_arr.splice(1, 10); //for some reason this works ok...
insert_all_jobs(job_arr, 'technician', function(err, result){
if(err) return console.log('error');
console.log(result);
});
thank you for reading my question and have a lovely day
~A

Node.js find method works weird

I have this Node.js method for GET queries
app.get('/:tableName', function(req, res) {
var schema = require('./schema/' + req.param('tableName'));
var q = unescape(Object.keys(req.query)[0]);
console.log('query:' + q);
schema.find(q, function(err, result) {
if (err) {
console.log(err);
res.status(500).json({status: 'failure'});
}
if (result) {
res.json(result);
} else {
res.json({status: 'not found'});
}
});
});
The strage, that if I call it i.e. whit this input .. (it is exactly the value of var q)
{
"remoteID" : "80E75ED5-B696-446E-9AA7-9B170350D7E6"
}
.. I get back 3 items but non of them match the condition, basically it returns all the items in table.
[{"lastName":"K","remoteID":"D5DC51C1-C415-4073-A647-1D41BB47D03E","password":"p","firstName":"J","email":"xxx","airline":"73C02335-C019-419E-BCC7-C87537F38492","userName":"J","_id":"5353b639889a61000038848c","__v":0},
{"lastName":"asd","remoteID":"C471495C-C218-4440-BA81-7BD37CBB5605","password":"wn831","firstName":"asd","email":"asd#asd.hu","airline":"73C02335-C019-419E-BCC7-C87537F38492","userName":"ASA","_id":"5353b639889a610000388491","__v":0},
{"remoteID":"5A0D51DE-DCD0-4E3E-9D97-7CAD066DB68A","userName":"R","password":"R","_id":"5353b639889a610000388492","__v":0}]
One important line was missing:
var jq = JSON.parse(q);
schema.find(jq, function(err, result) {
find() method requires not string but JSON object.

Categories