Sequelize transaction stops executing during loop - javascript

I am trying to do a transaction that loops through multiple items and inserts them into the database. If i just have 1 item in each array, the code executes fine, it inserts the deployment, the crew and the equipment. However if I have 2 or more items in either equipment or crew, the application just freezes, no error or anything.
The console output looks like this:
Executing (e337b7de-e95f-4d18-a2e9-1216cb8b7d61): START TRANSACTION;
----------------------CREATE DEPLOYMENT---------------
Executing (e337b7de-e95f-4d18-a2e9-1216cb8b7d61): INSERT INTO `deployments` (`id
`,`dateDeployed`,`dateReturned`,`city`,`province`,`country`,`fireName`,`fireNumb
er`,`fireCenter`,`unitNumber`,`comments`,`finalSold`,`status`,`createdAt`,`updat
edAt`,`contractId`,`deploymentTypeId`,`productId`) VALUES (DEFAULT,'2018-03-01',
'Invalid date','1','BC','CAN','1','1','1','1','test','','active','2018-03-08 22:
36:44','2018-03-08 22:36:44','5','1','1');
----------------------CREATE EQUIPEMENT---------------
----------------------CREATE EQUIPEMENT---------------
Executing (default): INSERT INTO `deploymentEquipments` (`createdAt`,`updatedAt`
,`deploymentId`,`equipmentId`) VALUES ('2018-03-08 18:09:31','2018-03-08 22:36:4
4',17,1);
Executing (default): INSERT INTO `deploymentEquipments` (`createdAt`,`updatedAt`
,`deploymentId`,`equipmentId`) VALUES ('2018-03-08 18:09:39','2018-03-08 22:36:4
4',17,2);
My code is like this:
app.post('/deployment', function(req,res,next){
var deployment = req.body;
var crew = req.body.deploymentCrew;
var equipment = req.body.deploymentEquipment;
var deploymentId = "";
//insert new deployment - start transaction, add deployment, get ID, loop through crew, loop through equipment
models.sequelize.transaction(t =>
{
var equipPromises = [];
console.log('----------------------CREATE DEPLOYMENT---------------');
return models.deployment.create(req.body, {transaction: t})
.then(function(newDeployment) {
deploymentId = newDeployment.dataValues.id;
for (var i = 0; i < equipment.length; i++) {
console.log('----------------------CREATE EQUIPEMENT---------------');
var equip = equipment[i];
equip.deploymentId = deploymentId;
equip.equipmentId = equipment[i].id;
var equipPromise = models.deploymentEquipment.create(equip, equipPromises.push(equipPromise));
}
return Promise.all(equipPromises);
})
.then(function() {
console.log('----------------------CREATE STAFF---------------');
var crewPromises = [];
for (var i = 0; i < crew.length; i++) {
var staff = crew[i];
staff.deploymentId = deploymentId;
staff.staffId = crew[i].staff.id;
var crewPromise = models.deploymentCrew.create(staff, crewPromises.push(crewPromise));
}
return Promise.all(crewPromises);
});
}).then(result => {
console.log('deployment added');
res.send(result);
}).catch(err => {
console.log('deployment creation failed');
res.status(401).send({'message':err, 'redirect': '/'});
});
});
Any thought's or ideas why this might be happening would be appreciated.
Thanks

It was actually quite simple, I wasn't adding the transaction into the looped create statements. So now it is like:
//this is the old statement
//var equipPromise = models.deploymentEquipment.create(equip, equipPromises.push(equipPromise));
//this is the new correct way to do it
equipPromises.push(models.deploymentEquipment.create(equip, {transaction:t}));

Related

why the order of loop changes by parsing data in sqlite database

an xml document is edited using a function. First, all duplicate files are removed. Then the content is entered into a sql database. For this I run a loop that corresponds to the length of the xml elements (here already converted to json). When I look at the content of the table in the database, I see that the order of the entries does not correspond to the order of the array. So within the database query of my server I printed the run variable i and saw that it starts at 0 but 2,3,1 are appended to the end in this order.
Unfortunately I don't understand at all why this happens and hope that someone can help me here.
function extractquestions(filename){
let questions = [];
let sql_insert = 'INSERT INTO Fragen (Titel, Frage) values (?,?)';
fs.readFile('./files/'+filename, function(err, data){
if(err){
throw err;
}
const xmlDataStr = data;
const options = {
ignoreAttributes: false,
attributeNamePrefix : "attr_"
};
const parser = new XMLParser(options);
const output = parser.parse(xmlDataStr);
for(let i = 0; i<output.quiz.question.length; i++){
//Here the needed Questions are filtered out of the xml (Json) data
if(output.quiz.question[i].attr_type == "coderunner"){
questions.push(output.quiz.question[i]);
}
}
//Delete Duplicates from array
const filteredQuestions = questions.reduce((acc, current) => {
const x = acc.find(item => item.name.text === current.name.text);
if (!x) {
return acc.concat([current]);
} else {
return acc;
}
}, []);
for(let i = 0; i<filteredQuestions.length; i++){
db.run(sql_insert, filteredQuestions[i].name.text, filteredQuestions[i], (err, result)=>{
console.log(i);
if(err){
throw err;
}
});
}
});
}

ForEach only looping through first item on DataSnapshot

I´m trying to loop through the content of a DataSnapshot and then depending on a condition do some work FOR EACH one of the elements but currently, the ForEach is only doing the work in the first item. The "serverStatus" sometimes is waiting and sometimes in "onCall". When the first item is "onCall" does not go through the rest of the items as I think is supposed to do. Below a snapchot of where I get the information from:
And here is my function:
exports.manageCallRequests = functions.database.ref('/resquests/{userId}').onCreate((snap, context) => {
const event = snap.val();
console.log("function manageCallRequests is being called")
var rootPath = admin.database().ref();
var userOnCall = context.params.userId;
var serversRef = rootPath.child('servers');
var callRequest = event;
var userTime = callRequest["time"];
var waiting= "waiting";
//We first get all the servers in ascending order depending on the last time they were used
var serversSorted = serversRef.orderByChild('lastTimeUsed')
//Gets the children on the "serversSorted" Query
return serversSorted.once("value").then(allServers =>{
//Checks if there is any child
if(allServers.hasChildren()){
allServers.forEach(async function(server) {
//we extract the value from the server variable, this contains all the information
//about each one of the servers we have
var serverInfo = server.val();
var serverKey = server.key;
var serverNumber = serverInfo["serverNumber"];
var serverStatus = serverInfo["serverStatus"];
console.log("server status "+serverStatus)
if(serverStatus === waiting){
const setCallRequest = await serversRef.child(serverKey).child("current").child("callRequest").set(callRequest);
const removeUserOnCall = await rootPath.child("resquests").child(userOnCall).remove();
const setServerStatus = await serversRef.child(serverKey).child("serverStatus").set("onCall");
}
});
}else{
console.log("No servers available")
}
});
});
I had the same behavior because my cloud function was exited before that all iterations were executed in the forEach loop.I get rid of it using this snippet of code:
for (const doc of querySnapshot.docs) {
// Do wathever you want
// for instance:
await doc.ref.update(newData);
}
I found 2 ways of getting this done. The first one is useful if we have a DataSnapshot without any OrderBy* call, in this case, would be:
var allServers = await serversRef.once("value");
for (let serverKey of Object.keys(allServers.val())){
var server = allServers[serverKey];
//Do some work
}
We need to first get the keys of the object to then be able to extract it from within the for loop, as explained here otherwise we´ll get a "TypeError: 'x' is not iterable"
Now the problem with this particular case is that a have a DataSnapshot that was previously sorted at var serversSorted = serversRef.orderByChild('lastTimeUsed') so when we call Object.keys(allServers.val()) the value returned is no longer sorted and that´s where forEach() comes in handy. It guarantees the children of a DataSnapshot will be iterated in their query order as explained here however for some reasons when doing some async work within the forEach loop this seems not to work, that´s why I had to do this:
var serversSorted = serversRef.orderByChild('lastTimeUsed')
var allServers = await serversSorted.once("value");
//Checks if there is any children
if (allServers.hasChildren()) {
//if there is iterate through the event that was passed in containing all
// the servers
var alreadyOnCall = false;
var arrayOfServers = []
var arrayOfKeys = []
allServers.forEach(function(individualServer){
arrayOfKeys.push(individualServer.key)
arrayOfServers.push(individualServer)
})
for (var serveIndex = 0; serveIndex < arrayOfServers.length;serveIndex++){
var serverObj = arrayOfServers[serveIndex]
var serverObject = serverObj.val()
var serverKey = arrayOfKeys[serveIndex]
var serverStatus = serverObject["serverStatus"];
var serverNumber = serverObject["serverNumber"];
console.log("server info "+serverStatus+" "+serverKey);
if (serverStatus === waiting && alreadyOnCall === false) {
const setCallRequest = await serversRef.child(serverKey).child("current").child("callRequest").set(callRequest);
const removeUserOnCall = await rootPath.child("resquests").child(userOnCall).remove();
const setServerStatus = await serversRef.child(serverKey).child("serverStatus").set("onCall");
alreadyOnCall= true
console.log("Call properly set");
}
}
}

How can I make this code works with async

router.post("/cart/paycash/add-order",(req,res) => {
req.checkBody("emri","Ju lutem vendosni emrin").notEmpty();
req.checkBody("mbiemri","Ju lutem vendosni mbiemrin").notEmpty();
req.checkBody("numritelefonit","Ju lutem vendosni numrin e telefonit").notEmpty();
req.checkBody("qyteti","Ju lutem vendosni qytetin").notEmpty();
var emri = req.body.emri;
var mbiemri = req.body.mbiemri;
var telefoni = req.body.numritelefonit;
var email = req.body.email;
var qyteti = req.body.qyteti;
var adresa = req.body.adresa;
var Cart = req.session.cart;
var errors = req.validationErrors();
if(errors) {
res.redirect("/cart/checkout", {
errors:errors
});
}
else {
Orders.find({}, function(err,orders) {
if(err) {
console.log(err);
}
else {
var order=new Orders({
emri:emri,
mbiemri:mbiemri,
telefoni:telefoni,
email:email,
qyteti:qyteti,
adresa:adresa,
});
console.log(Cart.length);
Cart.forEach(function(product) {
var cart = Cart.length;
var productTitle = product.title;
console.log(productTitle);
for (var i = 0; i < 1; i++) {
Products.findOne({title:product.title}, function(err,foundproduct) {
console.log(foundproduct.title)
order.products.push(foundproduct);
order.save();;
});
}
});
}
});
delete req.session.cart;
delete req.session.promocode;
res.redirect("/dyqani");
}
});
I want to make this code works, but for this I need async. I have tried some methods but I couldn't made it work. Can anyone help me? I want to be able to add in my order database all the products that are in the cart, but because mongoose is async and JavaScript is not, some of the queries get loaded before and the results in my database are not the ones that are in my cart.
It seems like you're trying to asynchronously request for every product that's in your cart. You don't need the nested for loop in your forEach function, because that loop doesn't seem to do anything.
You need to first map all the async requests in an array and run Promise.all to request them all asynchronously
let products = Cart.map((product) => {
return Products.findOne({title:product.title},(err,foundproduct) => {
//your save product to order logic
});
})
Promise.all(products).then((complete) => {
console.log('product added')
})
Here's a detailed explanation on how promises work in javascript Javascript Promises
Your application design pattern is also uncommon, but that's outside the scope of this question.

Understanding promises a little more

I'm struggling to make this works. i'm dealing with sequelize promisses and i want to return the queried elements to the view, but instead its returning null. I know its because promises are async requests and it does not return a result immediatly after you call it, ok, but how to return the values, put it into an array and than return the array?
this is the code i have so far.
router.post('/register', function(req, res, next) {
var sales = req.body.sales;
var person = req.body.personID;
var promisses = [];
var delivery = req.body.delivery;
for(var i =0;i<sales.length;i++){
var product_id = sales[i].product_id;
var amount = sales[i].amount;
var price = sales[i].produto_price;
var salePromisse = Sale.create({
'product_id': product_id,
'person_id': person,
'amount': amount,
'price': price,
'total': amount*price
});
//i couldnt find a word which describes what movimentacao means...lets keep it.
var movimentacao = Movimentacao.create({
"tipo": 0,
"id_product": product_id,
"id_provider": "",
"data": new Date(),
"price": amount*price
});
promisses.push(salePromisse);
promisses.push(movimentacPromisse);
}
Promise.all(promisses).then(function (promissesArray){
var name = "";
var suc = 0;
var total = 0;
var salesResult = [];
var salesObject = {};
if(!delivery){
res.send({msg: "aaaaa"});
}else{
promissesArray.forEach(function(pro){
if(pro!==null && pro !== undefined){
if(pro['$modelOptions'].tableName === undefined){
Product.findById(pro.product_id).then(function (product){
salesObject.product = product.name;
salesObject.price= pro.price;
salesObject.amount = pro.amount;
salesObject.total = pro.total;
total += salesObject.total;
salesResult.push(salesObject);
salesObject = {};
return Person.findById(per);
}).then(function (person) {
name = person.name;
});;
}
}
});
//here is where i would like to return to the view salesResult and name.
//res.render("folder/view", {sales: salesResult, person: name});
console.log(salesResult);
}
});
});
Well, the promisses array has the CREATE instance for each of my models, and i'm creating a few types of it.
I want to insert on the database, check if the promise resolved is dealing with an specific table (the field modelOptions is undefined on it, i already debugged), query all the other results because on the promisses array i have just the id, and than put into the array to be able to return to the view, but on the console.log on the last line, it returns null. How can i improve the code to be able to do all i want and than return to the view?
Dont worry, all the model related variables are beeing declared above.
Thanks.

closing mongodb connection and get correct results with multiple parallel asynchronous query

I am new to Mongo and Node. I am currently using Mongoskin and Bluebird to handle the db connection and queries (as suggested here: https://stackoverflow.com/a/23687958/2701348 ).
I have three collections: Users, Binders and Cards.
The Binders collection contains the information about Cards for each User.
Each document in Binders has as properties:
User Id <--- that refers to the User owning the Card
Card Code <--- that refers to a Card
Count <--- that refers to the number of cards owned by the User
I prefer to have a separate Cards collection so that when a Card changes, it changes for all the Users Binders.
Now I am willing to retrieve an array for a given user such as:
[{card: {card document}, count: 4}, ...]
I have the following problems:
the db connection should be closed after all the async db callbacks are called
the cards array should be returned after the last db.collection('cards').find gives back the results
I know my following code is wrong but can be a starting point for a discussion:
var getAllBinderCards = function(req, res){
var db = req.db;
var userId = req.userId;
var promiseBinders = db.collection('binders').find({userId: userId}).toArrayAsync();
promiseBinders.then(function(binderCards) {
if (binderCards) {
var promiseCards;
//console.log("------ binderCards: ", binderCards);
var cards = [];
for (var i = 0; i < binderCards.length; i++) {
var binderCard = binderCards[i];
promiseCards = db.collection('cards').find({Code: binderCard.Code}).toArrayAsync();
promiseCards.then(function(cardsDB){
if(cardsDB){
//console.log("Cards found: ",binderCard.Code, cardsDB);
for (var i = 0; i < cardsDB.length; i++) {
cardsDB[i].count = binderCard.count;
};
cards.concat(cardsDB);
}
});
}
promiseCards.then(function(){
db.close();
console.log("Binder Cards: " , cards);
res.json(cards);
});
}
});
}
I am struggling trying to figure out how to handle the promisfied asynchronous call correctly in order to send back the whole array and close the db connection.
I think I should try to build a promise before the for loop and use it to chain the query on Cards promises and lastly chain the db.close() and res.json(cards) statements.
[EDIT] Maybe the easiest solution is to simply use the $in filter inside a single db.collection('cards').find({Code: {$in: [bindersCodeArray] }}).toArrayAsync(); and avoid that for loop:
var getAllBinderCards = function(req, res){
var db = req.db;
var userId = req.userId;
var promiseBinders = db.collection('binders').find({userId: userId}).toArrayAsync();
promiseBinders.then(function(binderCards) {
if (binderCards) {
var binderCodes = binderCards.map(function(element){
return element.Code;
});
var promiseCards = db.collection('cards').find({Code: {$in: binderCodes} }).toArrayAsync();
promiseCards.then(function(cards){
var bindersDictionary = {};
for (var i = 0; i < binderCards.length; i++) {
bindersDictionary[binderCards[i].Code] = binderCards[i].count;
};
for (var i = 0; i < cards.length; i++) {
cards[i].count = bindersDictionary[cards[i].Code];
};
db.close();
console.log("Binder Cards: " , cards);
res.json(cards);
});
}
});
}
Still I am curious if there is an elegant way to solve this riddle using promises.
I would expect that using $in and array may have constraints on the number of binders you can pass and affect query performance. You can also try doing this with async#map. e.g.:
...
function(binders) {
async.map(binders, cardsForBinders, function(err, bindersWithCards) {
// TODO: close connection here.
}
function cardsForBinders(binder, callback) {
// 1. find cards for binder.
// 2. prepare transformed response: binderWithCards.
callback(null, binderWithCards);
}
}
...

Categories

Resources