Async write after select Pouchdb - javascript

I'm trying to check if an element exist before inserting it in my bdd.
I have to do this in order to (in the future) modify this existing element.
I'm using PouchDb and PouchDb-find with Node 6.9.1.
Actually I'm doing this:
for(var i = 0; i < 10;i++ ){
(function(_count, _pdb){
var count = _count;
var db = _pdb;
db.find({
selector: {numeroCandidat: parseInt(results[count].no_apb)}
}).then((result) => {
if(result.docs.length != 0){
console.log("l'étudiant existe");
}else{
console.log("l'étudiant n'existe pas");
var etudiant = {
"numeroCandidat": results[count].no_apb,
"nom": results[count].nom,
"numeroGroupe": "gr" + results[count].groupe,
"filiere": results[count].libelle,
};
db.post(etudiant).then((response) =>{
// handle response
console.log("STUDENT CREATED");
}).catch(function (err) {
console.log(err);
});
}
}).catch(function (err) {
});
})(i, this.pdb);
};
But the problem is : Due to the asynchronous version of my select query... if an element exists two times it appends that the second select occurred BEFORE the insertion of the first element, and I have this element two times in my database. I don't know how to deal with this one.

SO.. I'v found a workaround !
Simply create a function that I call recursivly after writting into my database.
Goodbye for loop.
var createStudents = function(_count, _pdb, _students){
if(_count >= 10) return;
console.log(_count);
var count = _count;
var db = _pdb;
var students = _students.slice(0);
db.find({
selector: {numeroCandidat: parseInt(students[count].no_apb)}
}).then((result) => {
if(result.docs.length != 0){
console.log("l'étudiant existe");
createStudents(++count,db,results);
}else{
var etudiant = {
"numeroCandidat": students[count].no_apb,
"nom": students[count].nom,
"numeroGroupe": "gr" + students[count].groupe,
"filiere": students[count].libelle,
"etudiantComms": [
{"commentaire": students[count].commentaire}
]
};
db.post(etudiant).then((response) =>{
// handle response
console.log("STUDENT CREATED");
createStudents(++count,db,results);
}).catch(function (err) {
console.log(err);
});
}
}).catch(function (err) {
});
}
createStudents(0,this.pdb,results);

Related

Replace and update IDs using JavaScript stored procedure

Background:
I need to create a stored procedure in JavaScript (within CosmosDB) where: For every Feedback document, replace/update Feedback.id with new id
var NewID = "5678"
{
"Feedbacks" : [
{
"id": "1234"
}
{
"id": "1234"
}
]
}
This is what I am doing:
I have created a function called UpdateID and set the parameters to OldID and NewID. I am saying iterate through the document, and for every OldID value,, replace with the NewID. I am moreso familiar with Python so this is a bit different for me and I am not sure this is the correct approach.
For every iteration in doc.Feedbacks:
function UpdateID(OldID, NewID) {
if (Feedbacks.id = "OldID")
}
Any suggestion will be helpful
There are a handful of ways to write the javascript, but here's one way that should get on the right track:
function UpdateID(oldID, newID) {
//get just the records that need updating
var isAccepted = __.filter(
function(doc) {
return doc.Feedbacks && doc.Feedbacks.findIndex(function(feedback){
return feedback.id == oldID
}) > -1;
},
function (err, feed, options) {
if (err) throw err;
// Check the feed and if empty, set the body to 'no docs found'
if (!feed || !feed.length) {
var response = getContext().getResponse();
response.setBody('no docs found');
}
else {
//update the documents that have the old id
UpdateDoc(oldID, newID, feed)
}
});
if (!isAccepted) throw new Error('The query was not accepted by the server.');
}
//function based on https://stackoverflow.com/questions/36009939/documentdb-updating-multiple-documents-fails
function UpdateDoc(oldID, newID, documents) {
console.log("updating " + documents.length + " docs")
if (documents.length > 0) {
var document = documents[0];
// DocumentDB supports optimistic concurrency control via HTTP ETag.
var requestOptions = { etag: document._etag};
document.Feedbacks = document.Feedbacks.map(function(feedback){
if(feedback.id === oldID) {
feedback.id = newID;
}
return feedback;
});
// Update the document.
var isAccepted = __.replaceDocument(document._self, document, requestOptions, function(err, updatedDocument, responseOptions) {
if (err) {
responseBody.error = err;
throw err;
}
});
// If we hit execution bounds - throw an exception.
if (!isAccepted) {
responseBody.log += "Update not accepted";
response.setBody(responseBody);
}
else {
documents.shift();
if(documents.length > 0){
UpdateDoc(oldID, newID, documents);
}
}
}
}

Mongoose function shows variable as undefined?

Been staring at this for awhile and not sure why it is not working. I am trying to create an object within an embedded object, however, it seems like I am not passing the params down correctly as it says listings[j] is undefined. What do I need to pass into that piece for it to work?
function createBid(req, res) {
//get current listing object info
db.Listing.findById(req.params.listingId, function(err, foundListing) {
// console.log(foundListing._id );
if (err) {
console.log('Error', err);
}
res.json(foundListing);
// get array of all users
db.User.find({}, function(err, users) {
// for loop iterates through all users' listings
for (var i = 0; i < users.length; i++) {
var listings = users[i].listings
// for loop iterates through all listings ids
for (var j = 0; j < listings.length; j++) {
// finds match
// comparing _id with _id returning false. Not sure why, will return later
if (listings[j].topic === foundListing.topic && listings[j].created === foundListing.created) {
console.log("Found match: " + foundListing.topic);
// get current user id to add to bid object
db.User.findById(req.user, function(err, user) {
if (err) {
console.log(err);
}
var newBid = new db.Bid(req.body); // add data validation later
newBid.uid = user._id
// pushes new bid object into embedded listing
listings[j].bids.push(newBid);
listings[j].save(function(err, savedBid) {
console.log('newBid created: ', newBid);
res.json(newBid);
});
});
}
}
}
})
if (err) {
console.log(err);
}
});
};

NodeJS for loops with Mongo / HTTP call firing late

I'm fairly new to NodeJS and am still trying to wrap my head around the async nature of it, as well as when things are fired.
I've labeled each of the loops in the order that I expect them to fire with console.log() messages below. There are 4 loops in total that I expect to iterate in order.
When I hit the 'test call' end point, I begin by iterating through a list of data.
exports.testCall = function (req, res) {
var list = myData;
// Iterate through each list item and update MongoDB
for (var i=0; i<list.length; i++) {
var country = list[i].country;
var codeGroups = list[i].codes;
console.log('Loop #1');
for (var j=0; j<codeGroups.length; j++) {
console.log('Loop #2');
queryAndUpdateCodeGroup(codeGroups[j], country);
}
}
}
For each of the above 'codeGroups', I want to run a process that makes a call to my HTTP client, retrieves the data for that code group, and then update my MongoDB with that data.
function queryAndUpdateCodeGroup(group, country) {
myHTTPClient.itemLookup({
browseGroup: group,
country: country
}).then(function success (results) {
console.log('Loop #3');
for (var i=0; i<results.length; i++) {
var childrenArray = results[i].childArray;
var subCatArray = [];
var id = results[i].currentID;
var icon = '';
var name = getNameByCode(country, results[i].currentID);
for (var j=0; j<childrenArray.length; j++) {
subCatArray.push({name: childrenArray[j].name, itemID: id, icon: ''})
}
checkItem(name, id, icon, subCatArray);
}
}).catch(function error (err) {
console.log('ERR: ', JSON.stringify(err));
return err;
handleError(res, err);
});
}
Once the HTTP request is successful, I run this checkItem function which will either create a new entry in Mongo or will update an existing entry. Once this is complete, I want to start the process over for the remaining results in "loop #3". When Loop #3 has completed, I want to return to Loop #2 and repeat until this has completed for loop #2. When loop #2 has completed, I want to repeat this process in loop #1.
function checkItem(name, id, icon, subcats) {
console.log('Loop #4');
MyCategories.findOne({itemID: id}, function (err, cat) {
if (cat === null) {
console.log('ADDING NEW ITEM: ', name);
var newItem = new MyCategories({
name: name,
itemID: id,
icon: icon,
subCats: subcats
});
newItem.save(function (err) {
if (err) {
handleError(err);
return;
}
})
.then(function (res) {
console.log('New Item Success: ', newCatSuccess);
});
} else {
//Update item with latest info
var conditions = { itemID: currNodeId };
var update = { $set: { subCats: subcats }};
MyCategories.update(conditions, update, function (err, updated) {
if (err) {
handleError(err);
} else {
console.log('ITEM HAS BEEN UPDATED SUCCESSFULLY.');
}
});
}
});
}
Loop #1, and loop #2 seem to fire back and forth until completion, but loop #3 fires after #1 and #2 have completed, and #4 doesn't fire at all. How can I 'wait' until each step has completed before I continue on?

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.

node.js return data from find collection in loop

I am trying to return data from this function. Console.log(documents) successfully shows the data in console. But this works only in body of the function. I can't return this data to the template. What should I do? Should I use some async package for node.js, or can be accomplished somehow like this?
Thank you.
var projects = req.user.projects;
var docs = [];
db.collection('documents', function(err, collection) {
for (i = 0; i < projects.length; i++) {
collection.find({'_projectDn': projects[i].dn},function(err, cursor) {
cursor.each(function(err, documents) {
if(documents != null){
console.log(documents);
//or docs += documents;
}
});
});
}
});
console.log(documents); // undefined
res.render('projects.handlebars', {
user : req.user,
documents: docs
});
Those db functions are async, which means that when you try to log it, the function hasn't finished yet. You can log it using a callback, for example:
function getDocuments(callback) {
db.collection('documents', function(err, collection) {
for (i = 0; i < projects.length; i++) {
collection.find({
'_projectDn': projects[i].dn
}, function(err, cursor) {
cursor.each(function(err, documents) {
if (documents !== null) {
console.log(documents);
callback(documents);// run the function given in the callback argument
}
});
});
}
});
}
//use the function passing another function as argument
getDocuments(function(documents) {
console.log('Documents: ' + documents);
});

Categories