Accesing variable in nodejs module - javascript

Im' trying to build my first module in nodejs.
I have this code working perfectly :
var express = require('express');
var router = express.Router();
var sqlite3 = require('sqlite3').verbose();
var db;
var io = require('socket.io-client');
const notifier = require('node-notifier');
/* GET home page. */
router.get('/', function(req, res, next) {
createDb();
socket = io.connect('http://localhost:4000');
socket.on('connect', () => {
console.log("socket connected");
});
socket.on('message', (contenu) => {
console.log('message received');
console.log(contenu);
notifier.notify(contenu.contenu);
});
socket.emit('message', { contenu : 'test'});
res.render('index', { title: 'Accueil' });
});
/* SQLite */
function createDb() {
console.log("createDb chain");
db = new sqlite3.Database('./database_institut-villebon.db', createTable);
}
function createTable() {
console.log("createTable etudiants");
db.run("DROP TABLE IF EXISTS etudiants");
db.run("CREATE TABLE IF NOT EXISTS etudiants (Nom TEXT, NumeroGroupe INTEGER, NumeroCandidat INTEGER PRIMARY KEY, Filiere TEXT)", insertRows);
}
function insertRows() {
console.log("insertRows in etudiants");
var stmt = db.prepare("INSERT INTO etudiants VALUES (?,?,?,?)");
for (var i = 0; i < 3; i++) {
stmt.run("John Doe",i,i,"S");
}
stmt.finalize(readAllRows);
}
function readAllRows() {
console.log("readAllRows etudiants");
db.all("SELECT rowid AS id, Nom, NumeroGroupe, NumeroCandidat, Filiere FROM etudiants", function(err, rows) {
rows.forEach(function (row) {
console.log(row.id + ": " + row.NumeroCandidat +","+ row.Filiere);
});
closeDb();
});
}
function closeDb() {
console.log("closeDb");
db.close();
}
function runChain() {
createDb();
}
module.exports = router;
But when i try to put it in a module it say that the table "etudiants" doesn't exist ...
This is my module :
var sqlite3 = require('sqlite3').verbose();
"use strict";
/* SQLite */
var BddUtils = function () {
console.log("createDb chain");
this.database = new sqlite3.Database('./database_institut-villebon.db');
}
BddUtils.prototype.closeDb = function () {
console.log("closeDb");
this.database.close();
}
BddUtils.prototype.readAllRows = function() {
console.log("readAllRows etudiants");
this.database.all("SELECT rowid AS id, Nom, NumeroGroupe, NumeroCandidat, Filiere FROM etudiants", function(err, rows) {
rows.forEach(function (row) {
console.log(row.id + ": " + row.NumeroCandidat +","+ row.Filiere);
});
this.database.closeDb();
});
}
BddUtils.prototype.insertRows = function() {
console.log("insertRows in etudiants");
var stmt = this.database.prepare("INSERT INTO etudiants VALUES (?,?,?,?)");
for (var i = 0; i < 3; i++) {
stmt.run("John Doe",i,i,"S");
}
//stmt.finalize(this.readAllRows());
}
BddUtils.prototype.createTable = function () {
console.log("createTable etudiants");
this.database.run("DROP TABLE IF EXISTS etudiants");
this.database.run("CREATE TABLE IF NOT EXISTS etudiants (Nom TEXT, NumeroGroupe INTEGER, NumeroCandidat INTEGER PRIMARY KEY, Filiere TEXT)", this.insertRows());
}
BddUtils.prototype.init = function () {
this.createTable();
}
exports.BddUtils = exports = new BddUtils();
I have been looking for an issue and i found that if I don't drop the table everything works !
So i suppose that the "insertRows" function is called before the create table... but it's a callback function ....
Any help will be appreciate, thanks in advance.
EDIT : I'm maybe on something :
The context of the function (the this object inside the function) is
the statement object. Note that it is not possible to run the
statement again because it is automatically finalized after running
for the first time. Any subsequent attempts to run the statement again
will fail.
If execution was successful, the this object will contain two
properties named lastID and changes which contain the value of the
last inserted row ID and the number of rows affected by this query
respectively. Note that lastID only contains valid information when
the query was a successfully completed INSERT statement and changes
only contains valid information when the query was a successfully
completed UPDATE or DELETE statement. In all other cases, the content
of these properties is inaccurate and should not be used. The .run()
function is the only query method that sets these two values; all
other query methods such as .all() or .get() don't retrieve these
values.
So it's possible that my this.database is not in the current context anymore... don't know how to proceed..

It looks like you need to wrap your CREATE TABLE statement into a Database.serialize() function.
Database#serialize([callback])
Puts the execution mode into serialized. This means that at most one
statement object can execute a query at a time. Other statements wait
in a queue until the previous statements are executed.
This ensures the CREATE TABLE statement gets executed in isolation.
The example that comes from the documentation:
db.serialize(function() {
// These two queries will run sequentially.
db.run("CREATE TABLE foo (num)");
db.run("INSERT INTO foo VALUES (?)", 1, function() {
// These queries will run in parallel and the second query will probably
// fail because the table might not exist yet.
db.run("CREATE TABLE bar (num)");
db.run("INSERT INTO bar VALUES (?)", 1);
});
});

Solved by using db.serialized()
BddUtils.prototype.createTable = function () {
var db = this.database;
db.serialize(function() {
console.log("createTable etudiants");
db.run("DROP TABLE IF EXISTS etudiants");
db.run("CREATE TABLE IF NOT EXISTS etudiants (Nom TEXT, NumeroGroupe INTEGER, NumeroCandidat INTEGER PRIMARY KEY, Filiere TEXT)");
var stmt = db.prepare("INSERT INTO etudiants VALUES (?,?,?,?)");
for (var i = 0; i < 3; i++) {
stmt.run("John Doe",i,i,"S");
}
stmt.finalize(function(){
console.log("readAllRows etudiants");
db.all("SELECT rowid AS id, Nom, NumeroGroupe, NumeroCandidat, Filiere FROM etudiants", function(err, rows) {
rows.forEach(function (row) {
console.log(row.id + ": " + row.NumeroCandidat +","+ row.Filiere);
});
db.close();
});
});
})
}

Related

How can I make a update prepared statement work in JavaScript in Node JS

.then(function (val) {
var roleId = roleArr.indexOf(val.role) + 1;
db.query(
'UPDATE employee SET role_id = ? WHERE first_name = ? AND last_name = ?;',
[roleId, val.firstname, val.lastName],
function (err) {
if (err) throw err;
console.table(val);
startPrompt();
}
);
});
This code comes from an inquirer statement. The roleID, val.firstname, and val.lastName are good variables because they are tested elsewhere in the program. The update statement is not updating though. I tried it with doublequotes around the where statement variables but that doesn't work. What am I doing wrong? The same statement works in a mysql shell.
I changed the code to get the id of the name I wanted to change instead of matching a first_name and last_name.
var nameArr = [];
function Name() {
db.query(
"SELECT * FROM employee",
function (err, res) {
if (err) throw err;
for (var i = 0; i < res.length; i++) {
nameArr.push(res[i].first_name + " " + res[i].last_name);
}
});
return nameArr;
}
and
var nameId = nameArr.indexOf(val.name) + 1;
Then I used the Name() to get the choices for the inquirer question.

I want to return the result when all the queries in the loop is done(express.js, react.js, mysql)

I'm working on a side project using react and express.
I want to send the drop data of 7 days of a specific item in my database in the list for search.
So I wrote the code like this.
Use date(droped_at) = date(date_add(now(),interval -*${i}* DAY) and for loop.
for (var i = 1; i < 8; i++) {
var resultSql1 = `SELECT T.channel_name, T.channel_number, T.VALUE, T.num from(SELECT itemId, channel_name, channel_number, COUNT(*) AS VALUE, ROW_NUMBER() over(order by value DESC) NUM FROM item_drop_exception WHERE itemId = (SELECT itemId FROM item_exception WHERE itemid = '${id}') AND date(droped_at) = date(date_add(now(),interval -${i} DAY)) GROUP BY channel_name, channel_number) T WHERE T.NUM<=3 ORDER BY T.num ORDER BY T.NUM`
db.query(resultSql1, id, (err, data) => {
if (!err) {
list1.push(data);
if (list1.length == 7) {
res.send(list1);
}
}
else {
res.send(err);
}
})
}
But it doesn't work.
I know the method is wrong.
I think the next loop is going on before the query results come in, but I don't know what to do.
Is it right to bring in seven days of data like that?
I want to send the data to the front when all results are the append and complete.
It's not easy because I'm self-taught, I need help.
The query has a small error.
You have a double ORDER BY t.NUM at the end, so when you remove it the query runs. But if it gets you the right result, is only answerable , fi we had data
So
var resultSql1 = `SELECT T.channel_name, T.channel_number, T.VALUE, T.num from(SELECT itemId, channel_name, channel_number, COUNT(*) AS VALUE, ROW_NUMBER() over(order by value DESC) NUM FROM item_drop_exception WHERE itemId = (SELECT itemId FROM item_exception WHERE itemid = '${id}') AND date(droped_at) = date(date_add(now(),interval -${i} DAY)) GROUP BY channel_name, channel_number) T WHERE T.NUM<=3 ORDER BY T.num`
Would give you a result for every days
Turn your function call into a promise, then await it inside each loop, ensuring each db query runs one by one.
const { promisify } = require('util');
const dbQueryAsync = promisify(db.query);
async function getData(req, res) {
var { id } = req.params;
var list = [];
for (var i = 1; i < 8; i++) {
var resultSql1 = `SELECT ... `;
try {
var data = await dbQueryAsync(resultSql1, id);
list.push(data);
} catch(err) {
res.send(err);
}
}
res.send(list);
);

Simplify nested promises within loops and closures

I wrote a ~50 lines script to perform housekeeping on MySQL databases. I'm afraid my code exhibits anti-patterns as it rapidly escalates to an unreadable mess for the simple functions it performs.
I'd like some opinions for improving readability.
The full script is at the bottom of this post to give an idea.
Spotlight on the problem
The excessive nesting is caused by patterns like this repeated over and over: (snippet taken from script)
sql.query("show databases")
.then(function(rows) {
for (var r of rows) {
var db = r.Database;
(function(db) {
sql.query("show tables in " + db)
.then(function(rows) {
// [...]
}
})(db);
}
});
I'm nesting one promise under the other within both a for loop and a closure. The loop is needed to iterate across all results from sql.query(), and the closure is necessary to pass the value of db to the lower promise; without the closure, the loop would complete even before the nested promise executes at all, so db would always contain only the last element of the loop, preventing the nested promise from reading each value of db.
Full script
var mysql = require("promise-mysql");
var validator = require("mysql-validator"); // simple library to validate against mysql data types
var ignoreDbs = [ "information_schema" ],
multiplier = 2, // numeric records multiplier to check out-of-range proximity
exitStatus = {'ok': 0, 'nearOutOfRange': 1, 'systemError': 2};
(function() {
var sql,
mysqlHost = "localhost",
mysqlUser = "user",
mysqlPass = "";
mysql.createConnection({
host: mysqlHost,
user: mysqlUser,
password: mysqlPass
}).then(function(connection) {
sql = connection;
})
.then(function() {
sql.query("show databases")
.then(function(rows) {
for (var r of rows) {
var db = r.Database;
if (ignoreDbs.indexOf(db) != -1) continue;
(function(db) {
sql.query("show tables in " + db)
.then(function(rows) {
for (var r of rows) {
var table = r["Tables_in_" + db];
(function(table) {
sql.query("describe " + db + "." + table)
.then(function(rows) {
for (var r of rows) {
(function(r) {
var field = r.Field,
type = r.Type, // eg: decimal(10,2)
query = "select " + field + " from " + db + "." + table + " ";
if (table != "nonce") query += "order by date desc limit 1000";
sql.query(query)
.then(function(rows) {
for (var r of rows) {
var record, err;
// remove decimal part, only integer range is checked
record = Math.trunc(r[field]);
err = validator.check(record * multiplier, type);
if (err) {
console.log(err.message);
process.exit(exitStatus.nearOutOfRange);
}
}
});
})(r);
}
});
})(table);
}
});
})(db);
}
});
})
.then(function() {
// if (sql != null) sql.end(); // may not exit process here: sql connection terminates before async functions above
//process.exit(exitStatus.ok); //
});
})();
Trivia
The purpose of the script is to automatically and periodically monitor if any record stored in any row, table and database in MySQL is approaching the out-of-range limit for its specific data type. Several other processes connected to MySQL continuously insert new numeric data with increasing values and nonces; this script is a central point where to check for such numeric limits. The script would then be attached to Munin for continuous monitoring and alerting.
Update: Revised script
As suggested by #Kqcef I modularized the anonymous functions out of the promise nest, and used let to avoid the explicit nesting of an additional function to preserve variable context.
Still this is excessively verbose, previously I wrote the same script in Bash in about 40 lines, but performance was screaming for a port to nodejs.
"use strict";
var mysql = require("promise-mysql");
var validator = require("mysql-validator"); // a simple library to validate against mysql data types
var ignoreDbs = [ "information_schema" ],
multiplier = 2, // numeric records multiplier to check out-of-range proximity
exitStatus = {'ok': 0, 'nearOutOfRange': 1, 'systemError': 2};
var mysqlHost = "localhost",
mysqlUser = "btc",
mysqlPass = "";
// return array of DBs strings
function getDatabases(sql) {
return sql.query("show databases")
.then(function(rows) {
var dbs = [];
for (var r of rows)
dbs.push(r.Database);
return dbs;
});
}
// return array of tables strings
function getTables(sql, db) {
return sql.query("show tables in " + db)
.then(function(rows) {
var tables = [];
for (var r of rows)
tables.push(r["Tables_in_" + db]);
return tables;
});
}
// return array of descriptions
function getTableDescription(sql, db, table) {
return sql.query("describe " + db + "." + table)
.then(function(rows) {
var descrs = [];
for (var r of rows) {
descrs.push({ 'field': r.Field, // eg: price
'type': r.Type}); // eg: decimal(10,2)
}
return descrs;
});
}
// return err object
function validateRecord(record, type) {
var record, err;
if (typeof record != "number") {
console.log("error: record is not numeric.");
process.exit(exitStatus.systemError);
}
// remove decimal part, only integer range is checked
record = Math.trunc(record);
err = validator.check(record * multiplier, type);
return err;
}
(function() {
var sql;
mysql.createConnection({
host: mysqlHost,
user: mysqlUser,
password: mysqlPass
}).then(function(connection) {
sql = connection;
})
.then(function() {
return getDatabases(sql)
})
.then(function(dbs) {
dbs.forEach(function(db) {
if (ignoreDbs.indexOf(db) != -1) return;
getTables(sql, db)
.then(function(tables) {
tables.forEach(function(table) {
getTableDescription(sql, db, table)
.then(function(descrs) {
descrs.forEach(function(descr) {
let field = descr.field,
type = descr.type,
query = "select " + descr.field + " from " + db + "." + table + " ";
if (table != "nonce") query += "order by date desc limit 1000";
sql.query(query)
.then(function(rows) {
rows.forEach(function(row) {
let err = validateRecord(row[field], type);
if (err) {
console.log(err.message);
process.exit(exitStatus.nearOutOfRange);
}
});
});
});
});
});
});
});
});
/*
.then(function() {
//if (sql != null) sql.end();
//process.exit(exitStatus.ok);
});
*/
})();
I agree with Jaromanda in terms of using let in your for loops to block scope the values and avoid your usage of an immediately-invoked function, which, while totally fine in terms of functionality, is decidedly less readable.
In terms of best practices and avoiding anti-patterns, one of the most important things you can strive for in terms of writing 'good' code is building modularized, reusable blocks of code. As it stands, your code has 5 or 6 anonymous functions that exist nowhere but within your chain of promise callbacks. If you were to declare those as functions outside of that chain, not only does that improve the maintainability of your code (you can test each individual one), but, if their names are clearly indicative of their purposes, would make for a very readable promise chain.
(Updated based on User Question)
Rather than leaving inner functions...
function getTableDescription(sql, db, table) {
return sql.query("describe " + db + "." + table)
.then(function(rows) {
var descrs = [];
for (var r of rows) {
descrs.push({ 'field': r.Field, // eg: price
'type': r.Type}); // eg: decimal(10,2)
}
return descrs;
});
}
...you can easily strip that out so that your code is self-documenting:
function collectDescriptionsFromRows(rows) {
var descriptions = [];
for (var row of rows) {
descriptions.push({'field': row.Field, 'type': row.Type});
}
return descriptions;
}
function getTableDescription(sql, db, table) {
return sql.query("describe " + db + "." + table)
.then(collectDescriptionsFromRows);
}
Also, if you ever find yourself doing data collection from one array to another, it's extremely helpful to get used to using built-in higher order functions (map, filter, reduce). Instead of the collectDescriptionsFromRows I just listed, it could be simplified to:
function collectDescriptionsFromRows(rows) {
return rows.map(row => { 'field': row.Field, 'type': row.Type});
}
Much less verbose, much more readable. Your code and promise-chain will shrink and read more like a step-by-step list of instructions if you continue to extract those anonymous functions in the chain. Anywhere you see function(...there is more extracting to do! You can also do some damage (positively) by extracting all the data you need to begin with and use local logic to boil it down to what you need, rather than making several queries. Hope this helps.

How to know all JSON object has been iterated? [duplicate]

This question already has answers here:
Wait until all jQuery Ajax requests are done?
(22 answers)
Closed 7 years ago.
I m working on phonegap product using jquery and jquery mobile, the scenario is, when user is logging in for first time, we sync all the data and after done we forward user to another view. The items are retrieved in json format from server. Here are the portion of my code. I have called the webservice and the response is returned as JSON objects in response variable.
$(response.data.module.registration).each(function(k,v){
//insert into app's sqlite database
});
$(response.data.module.attendance).each(function(k,v){
//insert into app's sqlite database
});
$(response.data.items.grocery).each(function(k,v){
//insert into app's sqlite database
});
//and so on. There could be many rows in each loop above.
My question is how to know all rows has been inserted from the loop so that I can forward user to restricted area.
more precisely, how to know all JSON object has been iterated successfully?
What i tried is put counter in each loop and check if sum of all the counters is equal to total items we are iterating. But this didn't work the sum of all the counters are readily available before all items are inserted.
EDIT
Here is my helper function that inserts record into sqlite db. This didn't work, user was logged in before all data are inserted. Can you tell me where I went wrong
var sqlhelper = {
insertJSONData:function(tablename,data,callback){
var dfrd = $.Deferred();
var fields=sqlhelper.separateFieldData(data,"field");
var dataval=sqlhelper.separateFieldData(data,"value");
sqlhelper.db.transaction(function(tx) {
var sqlquery='INSERT INTO '+tablename+' ('+fields+') values('+dataval+')';
console.log(sqlquery);
tx.executeSql(sqlquery,[],function(tx,result){
dfrd.resolve(result);
console.log('Success');
},sqlhelper.errorCB);
if(callback!=undefined){
callback();
}
});
return dfrd.promise();
}
}
And here is the code that fetches server response
function defObj1()
{
if(typeof response.data.module.registration!="undefined"){
$(response.data.module.registration).each(function(i,e){
var data = {
'reg_id': e.reg_id,
'reg_name': e.reg_name,
'reg_desc': e.reg_desc,
'reg_status': e.reg_status
};
sqlhelper.insertJSONData('tbl_registration',data);
}); // end of each loop
}
}
function defObj2()
{
if(typeof response.data.items.grocery!="undefined"){
$(response.data.items.grocery).each(function(i,e){
var data = {
'grocery_id': e.grocery_id,
'item_name': e.item_name,
'item_qty': e.item_qty,
'item_unit_price': e.item_unit_price
};
sqlhelper.insertJSONData('tbl_grocery',data);
}); // end of each loop
}
}
$.when(defObj1() ,defObj2()).done(function(a1,a2){
//sync complete so login user
doLogin();
})
Thanks
try this. (Edited)
var isValid = true, i = 0, sum, callback = function () {
//if all inserting is successfully it is called
};
...
$(response.data.module.registration).each(function (k, v) {
//insert into app's sqlite database
var data = {
'reg_id': e.reg_id,
'reg_name': e.reg_name,
'reg_desc': e.reg_desc,
'reg_status': e.reg_status
};
sqlhelper.insertJSONData('tbl_registration', data, function (data) {
if (!data) {
isValid = false;
sum++;
}
i++;//onSuccess function
checkLast(i);//call this lastly method or each
}, function () {
i++;//onError function
});
});
...
//other codes is identical logic
...
function checkLast(i) {
if (i == sum) {
callback();
}
}
...
I have added successCallbak and errorCallback to your sqlhelper
var sqlhelper = {
insertJSONData: function (tablename, data, successCallbak, errorCallback) {
var dfrd = $.Deferred();
var fields = sqlhelper.separateFieldData(data, "field");
var dataval = sqlhelper.separateFieldData(data, "value");
sqlhelper.db.transaction(function (tx) {
var sqlquery = 'INSERT INTO ' + tablename + ' (' + fields + ') values(' + dataval + ')';
console.log(sqlquery);
tx.executeSql(sqlquery, [], function (tx, result) {
dfrd.resolve(result);
if (successCallback) {
successCallback(result);
}
console.log('Success');
}, sqlhelper.errorCB);
if (errorCallback) {
errorCallback();
}
});
return dfrd.promise();
}
}

When I use Web SQL,I am in trouble

I tried to insert some data into web sql database.But I met a problem.
My code :
database();
for(var i=0;i<m.length;i++){
showid = m[i].id;
showtitle = m[i].title;
insert();
}
function database(){
//open the database
db = window.openDatabase("youyanchu", "1.0","youyanchu",500000);
db.transaction(function(tx) {
var table = tx.executeSql("CREATE TABLE showList (id int PRIMARY KEY, title NVARCHAR, finishDate NVARCHAR, status NVARCHAR, tkCount NVARCHAR )");
});
}
//INTEGER NOT NULL PRIMARY KEY
function insert(){
db.transaction(function(ar) {
ar.executeSql("INSERT INTO showList (id, title,finishDate,status) values(?,?,?,?)", [showid,showtitle,'aaa','bbb']);
});
}
m.length is 3 and "m" should be
aaa = {'id':'999','title':'ninini'}
bbb = {'id':'888','title':'ninini'}
ccc = {'id':'777','title':'ninini'}
At last,just "ccc" display in the web sql.
How to insert all data into the database?What mistake I made in the code?
Since tx.executeSql is asynchronous, I believe your loop finishes before the first insert runs.
Hence showid and showtitle will always have the last values of the object m
Try this instead:
for(var i=0;i<m.length;i++){
insert(m[i].id, m[i].title);
}
function insert(id, title){
db.transaction(function(tx) {
txexecuteSql("INSERT INTO showList (id, title,finishDate,status) values(?,?,?,?)", [id, title,'aaa','bbb']);
});
}

Categories

Resources