Run multiple CRUD operations in series in node,js - mysql

I've created a node.js bot using telegraf+mysql+node.js.
When a user selects a button on the bot called "Create new agent". The bot runs multiple CRUD operations to create the agent and stores the agent's details for later use.
I would like the CRUD operations to run in series or synchronous manner and handle any errors appropriately. maybe possible delete rows added in tables before an error is encountered.
How can I go about this?
This is how I've implemented the statements:
1.The agent is added in a temporary table called "tblagentstoadd"
var newagent=[firstName,lastName,status,agentnumber,IDDealer];
var sql='INSERT INTO
agentDB.tblbotagentstoadd(FirstName,LastName,SpecialInstructions,MSISDN,IDDealer)
VALUES (?);';
DB.query(sql,[newagent],function(err,result){
if (err){
throw err;
};
});
}
2.The agent is then added in a table called "tblagents"
var inagent=[firstName,lastName,status,IDDealer]
const query = new Promise((resolve, reject) => {
var sql='INSERT INTO agentDB.tblagents (agentDB.tblagents.FirstName, agentDB.tblagents.LastName, agentDB.tblagents.SpecialInstructions,agentDB.tblagents.DealerID) VALUES (?);'
DB.query(sql,[inagent],function(err,result){
if (err){
throw err;
}
resolve(result);
});
})
await query;
3. I then Lookup the recently added agent's ID in "tblagents" to be added in "tblagentnumbers"
(This is where I suspect it could lead to some errors/problems e.g., There could be more than one John Smith in tblagents)
var sql='SELECT agentDB.tblagents.AgentID FROM agentDB.tblagents
WHERE agentDB.tblagents.FirstName=? AND agentDB.tblagents.LastName=?
AND agentDB.tblagents.SpecialInstructions=?;'
DB.query(sql,[firstName,lastName,status],function(err,result){
if (err){
throw err;
};
console.log(result);
AgentcntIDStore=[];
result.forEach(agent=>{
AgentcntIDStore.push({
AgentID:agent.AgentID
})
ctx.session.AgentID=agent.AgentID
})
4.Then the agent's contact number is added in a seperate table called "tblagentnumbers"
var ccagentnum=[AgentID,newagentMSISDN,newagentMSISDNtxt,primarynum]
var sql='INSERT INTO agentDB.tblagentnumbers(AgentID,MSISDN,MSISDNtxt,IsPrimaryYN) VALUES (?);';
DB.query(sql,[ccagentnum],function(err,result){
if (err){
throw err;
};
})
The agent's number + agent ID is looked up in tblagentnumbers + stored to be used for later use.
var sql='SELECT agentDB.tblagentnumbers.AgentID, agentDB.tblagentnumbers.MSISDN AS MainNumber FROM agentDB.tblagentnumbers INNER JOIN agentDB.tblagents ON agentDB.tblagents.AgentID = agentDB.tblagentnumbers.AgentID WHERE agentDB.tblagentnumbers.AgentID =?; '
DB.query(sql,[ctx.session.AgentID],function(err,result){
if (err){
throw err;
};
console.log(result)
AgentIDStore=[];
result.forEach(agent=>{
AgentIDStore.push({
AgentID:agent.AgentID,
MainNumber:agent.MainNumber,
})
ctx.session.AgentID=agent.AgentID
ctx.session.agentnumber=agent.MainNumber
})
})
}
In step 3, I was thinking of actaully adding another select statement that will lookup the ID stored in the tblagentstoadd or tblagents and then make use of an inner join statement to avoid retrieving multiple entries from tblagents.
I hope this is not confusing and not too much. I am aware it's not the best code I'm still a beginner, your help would be very much appreciated. Thank you.

Related

nodejs X DevApi add return id

I have read in the mysql 8 docs that a collection.add operation returns the id of the document added i the result. But I have seen no example of how to get that returned id.
I tried the following. The document is inserted but no clue about the returned result
mysqlx
.getSession(mysqlxOptions)
.then(function (session) {
var db = session.getSchema('oa4');
// Use the collection 'my_collection'
var myColl = db.getCollection('order_items');
return myColl;
})
.then(function (myColl) {
// Insert documents
return Promise
.all([
myColl.add(req.body).execute()
])
})
.then(function(result){
console.log(result);
res.send(result);
})
.catch(function (err) {
// Handle error
console.log(err);
});
What is the right way to get the result and pass it on?
The execute() method returns a Promise that resolves to a Result instance, which in turn provides a getGeneratedIds() method that in the case of Collection.add() contains an list of the _ids that have been auto-generated by the server for any inserted document that does not have one already.
Promise.all([myColl.add(req.body).execute()])
.then(function (results) {
console.log(results[0].getGeneratedIds()[0]);
})
In this case, and assuming req.body is itself a single document, if it contains an _id property, its value will be effectively used as the identifier, and the server will not auto-generate one, and as such, it will not be available in the list returned by getGeneratedIds().
Disclaimer: I'm the lead maintainer of the X DevAPI Node.js connector for MySQL.

Loopback autoupdate not creating custom models

I'm trying to create an app using Loopback (V3) and i've encountered a weird error.
I'm trying to create my Mysql tables using autoupdate(), but for my custom models it is not working.
This is what i've tried so far ->
var server = require('../server');
var ds = server.dataSources.db;
var models = ['test','Post','User', 'AccessToken', 'ACL', 'RoleMapping', 'Role'];
ds.isActual(models, function(err, actual) {
if (!actual) {
ds.autoupdate(null, function(err,result){
console.log("DONE!");
console.log(result);
});
};
});
The script works. If the database is empty it will create tables for all EXCEPT test and Post. Those are my custom models, the others are built into loopback.
Is it because of model type? (tried Model and PersistedModel) or is it something else? I even tried without the isActual check and still nothing.
I would recommend that you keep two separate arrays for built-in models and custom models and write code like following, that way you could know where the issue is. also, I think there is an error in your code near ds.autoupdate(null, fun.....
please follow according to the below code
var builtInModels = ['AccessToken', 'ACL', 'RoleMapping','Role'];
var userDefinedModels = ['Post','test'];
// migrate built-in models
dataSource.isActual(builtInModels, function (err, actual) {
if (!actual) {
dataSource.autoupdate(builtInModels, function (err, result) {
if(err) console.log(err);
console.log('Datasource Synced: Built in models');
});
}
});
// migrate userdefined models
dataSource.isActual(userDefinedModels, function (err, actual) {
if (!actual) {
dataSource.autoupdate(userDefinedModels, function (err, result) {
if (err) console.log(err);
console.log('Datasource Synced: User defined models');
});
}
});

Node + Mysql: How to execute more queries based on another query

I'm pretty new to the node world and trying to migrate our php application to node. To be able to return all article data several different queries have to be done depending on the results of the first query. Currently my data object is empty as it's returned before the two queries run. How can I "chain" these queries using a promised based approach.
I found a library https://github.com/lukeb-uk/node-promise-mysql which I think could help but I have no idea how to implement it with my code.
exports.getArticleData = function(req, done) {
pool.getConnection(function(error, connection) {
if (error) throw error;
var data = {
article: {},
listicles: []
};
// Inital query
connection.query(
`SELECT article_id, title, is_listicle_article, FROM com_magazine_articles AS article WHERE article_id = ${req
.params.articleId}`,
function(error, results) {
data.article = results;
}
);
// This query should only be excuted if is_listicle_article = true
if (data.article.is_listicle_article) {
connection.query(
`SELECT * FROM com_magazine_article_listicles WHERE article_id = ${req.params
.articleId}`,
function(error, results) {
data.listicle = results;
}
);
}
// More queries depending on the result of the first one
// ....
// ....
// Callback with the data object
done(data);
connection.release();
});
};
What would be the best approach to execute queries based on other queries results? Any help is really appreciated.
The functionality you are looking for is Promise chaining, it allows you to construct a sequence of promises, each depending on the result of the previous value. Applying this to your code, you would get something like this:
exports.getArticleData = function(req, done) {
pool.getConnection(function(error, connection) {
if (error) throw error;
// Inital query
return connection.query(
`SELECT article_id, title, is_listicle_article, FROM com_magazine_articles AS article WHERE article_id = ${req
.params.articleId}`
).then((rows) => {
return Promise.all(rows.map((article) => {
if (article.is_listicle_article) {
return connection.query(
`SELECT * FROM com_magazine_article_listicles WHERE article_id = ${req.params
.articleId}`
);
} else {
return Promise.resolve(null);
}
}));
}).then((res) => {
connection.release();
done(res.filter(function(i){ return i != null; }));
})
// This query should only be excuted if is_listicle_article = true
// More queries depending on the result of the first one
// ....
// ....
// Callback with the data object
connection.release();
});
};
Obviously since I don't have all of your code, I couldn't verify this example, but this should be roughly the functionality you are looking for. That said, I think there were a couple of mistakes you should watch out for in your example code:
connection.query() returns a promise (aka doesn't need a callback function). Use this functionality to your advantage- it will make your code prettier.
connection.query() returns an array of rows, not a single value. You seemed to ignore this in your example code.
Try not to save things into a variable when using promises, it isn't necessary. To remedy this, read more into the Promise API (Promise.resolve(), Promise.reject(), Promise.any(), Promise.catch(), Promise.all()) etc.
It seems like these SQL queries could easily be combined into a single query. This will be way more efficient that performing two operations. Not sure if this is the case with the remaining queries you wish to use, but definitely something to look out for.

Increment a field in model using remote method in loopback?

There is a field called counter in a model and whenever if I call a custom remote method like
Request URL
http://loopback:3000/api/models/increment_counter
Request body
EMPTY
Response
{
"counter" : [Value after Increment]
}
Currently to increment First i have to get the counter value from db and increment it one and update the couter value, This involves two queries, Is it possible to do it in a single NodeAPI call like the below mysql query.I am currently using Mysql as DB.
mysql_query("
UPDATE model
SET counter = counter + 1
WHERE model_id = '1'
");
Thank you
Given the MySQL syntax you want, you seem to need an atomic counter.
database transactions
With the MySQL connector, you can use database transactions. It is supported by the MySQL connector. It's a bit overkill for just atomic counters but it will get the job done.
Here is an example, inside a custom remote method
MyModel.someRemoteMethodForIncrementing = function(callback) {
// Start the transaction
MyModel.beginTransaction({
isolationLevel: MyModel.Transaction.READ_COMMITTED
}, function(err, tx) {
// Retrieve the current counter value as part of the transaction
MyModel.findById(id, {
transaction: tx
}, function(err, data) {
if (err) {
console.log(err);
return tx.rollback(function(err) {
callback(err);
});
}
// Increment the counter as part of the transaction
var inc = data.counter + 1;
MyModel.updateAttributes({
counter: inc
}, {
transaction: tx
}, function(err, newData) {
if (err) {
console.log(err);
return tx.rollback(function(err) {
callback(err);
});
}
// Commit the transaction to make it happen
tx.commit(function(err) {
if (err) return callback(err);
// Counter should have been incremented
callback();
});
});
});
});
};
I haven't tested it but it should work, let me know
extented operators
In the future, you will be able to use the $inc (increment) extended operator but so far it's only supported by MongoDB connector, not MySQL.
Just for reference, here is the syntax (works only with MongoDB)
PUT api/Model/:id?filter={"$inc":{"name":propertyToIncrement, "count":incrementAmount}}
There is an ongoing PR that I am trying to get landed to get MySQL support, but there are many things to be done before it can get merged.
Yes you can do it in a single loopback remote method call. Assume you are sending an Id in your request
yourModel.remoteMethod = function(data, cb){
yourModel.findById(data.id, function(err, object){
if(err){
cb(err, null);
}
object.counter += 1;
object.save(function(saveErr, afterSaveObj){
cb(saveErr, afterSaveObj.counter);
})
});
}
here cb is a callback which loopback passes to your remoteMethod.

Using node-mysql in a function

I'm very new to nodejs and have a question.
Trying to create a function that will call the value of any field where I mention its ID from a table:
function getUserInfo (userID, dynamicField) {
var query = connection.query('SELECT '+dynamicField+' from users WHERE userID = '+connection.escape(userID));
query.on('result', function(row) {
return(row.dynamicField);
});
};
console.log(getUserInfo(8, userEmail)) //this should get me the userEmail value of the user with userID=8
However, I get "undefined". If I use console.log rather than return, it logs the value but this has no use as a function to be used inside other functions to get a value.
I will be glad if I can get help for modifying the function.
This is a common mistake amongst async/nodejs beginners. You have essentially wrapped an async function inside a sync function which breaks down the nature of node's event loop. The return expression needs to be replaced with a callback. See below:
// Method
function getUserInfo (userID, dynamicField, callback) {
var query = connection.query('SELECT '+dynamicField+' from users WHERE userID = '+connection.escape(userID));
query.on('result', function(row) {
callback(null, row.dynamicField);
});
};
// Implementation
getUserInfo(8, userEmail, function(err, result){
console.log(err || result);
});
By convention, in Nodejs we always pass an error object first in the callback. In this case since there is no error to capture, we pass null in its place.