Callbacks with node.js/MongoDB/Mongoose - mongodb

I'm trying to learn the Mongoose ORM for MongoDB and node.js. I want to query documents in my database, perform an action on each one, and then perform another action after all of the previous actions are complete. In other words, I want to do something like this, assuming I have a model called 'User:'
var userArray = [];
User.find({})
.each(function(user) {
user.age += 1;
userArray.push(user);
})
.then(function() {
//do something with userArray
//happens after all the users' ages are modified
});
What is the correct syntax for doing something like this in Mongoose?

If you only need to perform synchronous actions for each document, the solution is fairly simple. (console.warn() is synchronous, so you can use it to verify that all user objects are processed first.)
User.find({}).execFind(function(err, users) {
users.forEach(function(user) {
user.age += 1;
userArray.push(user);
console.warn('x');
});
// then...
console.warn('y');
});
If you need to perform some sort of asynchronous action (perhaps another db query), the solution becomes more complex. I encountered this issue recently and briefly debated using a module such as Step or rolling my own barebones solution. Step provided more functionality than I needed, so I decided that the added overhead was not worth it. Here is my solution:
var count = 0;
A.find({ b : c }, ['d', 'e', 'f']).sort(['d'], -1).execFind(function(err, g) {
g.forEach(function(h) {
h.a = [];
B.find({ b : c }, ['d', 'e', 'f', 'g']).execFind(function(err, z) {
g.v = z;
if (++count == g.length)
res.render('z.jade', { locals : { title : 'Q', j : g } });
});
});
});
Please pardon the obfuscation (I yanked that right from the source of a project that is under-wraps). Essentially, you wait until the final async operation to complete.

There are modules for this. Try using async (https://github.com/caolan/async). It allows for running commands and then executing a callback once all are done.
async.parallel([
function(){ ... },
function(){ ... }
], callback);

Related

dexiejs query get slower overtime

So I am using Svelte+Vite with Dexiejs as my offline Db and Routify for the routes, when I go to a page that is making query to Dexiejs the response is pretty quick on the first request, but when I go to another page and go back to to the same page the response time gets slower each query.
I've used the index like this answer suggest. but it still happens. what did i miss ? can anyone recommend any alternatives that have better performance than dexiejs or pouchDB for offline db ? I am currently trying pouchDB as alternative
here is my code
let taskDone;
let taskOngoing;
let clustercount;
let tasks = [];
onMount(async function () {
// #ts-ignore
let clusterQuery = await db.cluster.reverse().sortBy("id");
clustercount = clusterQuery.length;
for (const clusters of clusterQuery) {
// #ts-ignore
taskDone = await db.task
.where("[cluster_id+status]")
.anyOf([clusters.id, 1], [clusters.id, 2])
.toArray();
// #ts-ignore
taskOngoing = await db.task
.where({ cluster_id: clusters.id })
.toArray();
tasks = [
{
cluster_id: clusters.id,
count_done: taskDone.length,
count_ongoing: taskOngoing.length,
cluster_name: clusters.name,
},
...tasks,
];
count++;
}
dispatch("showList", tasks);
});
It looks like the tasks array is being filled more and more for every time onMount is called. Is it a globally declared variable? Was it meant to be local?

Element is not clickable inside of a for loop in protractor

I am using protractor-cucumber frame work with protractor 5.2.2 and mssql 3.3.0.We have a requirement of get db values,i am able to get db data values into my protractor code, But i am not able to click on a element with that db value.My code is given below.
function ConnectDB() {
var config = {
user: 'sam',
password: 'Passw0rd',
server: 'xxxxxx',
port: '50866',
database: 'testDB',
options: {
trustedConnection: true
}
}
sql.connect(config, function (err) {
if (err) console.log(err+" db connection failed");
var request = new sql.Request();
request.query('SELECT * FROM Locations WHERE ID = 2', function (err, loc){
if (err) console.log(err+" loc_name failed");
var loc_count= loc.length;
console.log(loc_count);
console.log(loc[0].Location_name);
var i;
for (i=0; i<loc_count; i++){
if(loc_count!=0 & loc_count%10==0){
element(by.css(.publish)).click();
}
element(by.cssContainingText("span", loc[i].Location_name)).click();
console.log(loc[i].Location_name);
browser.sleep(4000);
element(by.css(.post)).sendKeys("test");
element(by.css(.submit)).click();
}
});
});
}
Here the click() and senkeys() functions inside of the for loop is not working.But i am able to console the values of loc_count and loc[i].Location_name.How can i solve this?Thanks in advance.
The root cause of your issue is that the scripts of loop body almost executed async. All Protractor APIs are async. Thus the sendKeys click(), sleep() inside the loop are executed async. But the loop iterate sync.
This lead to when the i==loc_count, the click()/sendKeys()/sleep() of the i==0 have not been executed (They are async).
One solution is to use Closure to keep the i for each iteration. Another solution is use ES6 async/await.
Below is the code example of solution one:
var i;
for (i=0; i<loc_count; i++){
(function(j){
if(loc_count!=0 && loc_count%10==0){
element(by.css('.publish')).click();
}
element(by.cssContainingText("span", loc[j].Location_name)).click();
console.log(loc[j].Location_name);
browser.sleep(4000);
element(by.css('.post')).sendKeys("test");
element(by.css('.submit')).click();
})(i)
}
As yong stated it is because your for loop is synchronous and the protractor actions are async. You either need to implement async/await or use the then callbacks to make it work correctly.
Async/Await option:
request.query('SELECT * FROM Locations WHERE ID = 2', async function (err, loc){
if (err) console.log(err+" loc_name failed");
var loc_count= loc.length;
console.log(loc_count);
console.log(loc[0].Location_name);
var i;
for (i=0; i<loc_count; i++){
if(loc_count!=0 & loc_count%10==0){
await element(by.css(.publish)).click();
}
await element(by.cssContainingText("span", loc[i].Location_name)).click();
console.log(loc[i].Location_name);
await browser.sleep(4000);
await element(by.css(.post)).sendKeys("test");
await element(by.css(.submit)).click();
}
});
To use the then callbacks it would be a little more difficult to handle your if block correctly but essentially you will need to do something like this to get it to work.
element(by.cssContainingText("span", loc[i].Location_name)).click().then(() -> {
console.log(loc[i].Location_name);
browser.sleep(4000).then(() => {
element(by.css(.post)).sendKeys("test").then(() => {
element(by.css(.submit)).click();
});
});
});
The first option is far easier to read and implement correctly.
Try to use :-
var elm = element(by.id("myid"));
browser.executeScript("arguments[0].click();", elm.getWebElement());

Meteor - no more callbacks for "findOne" function

i'm working on a Meteor project, and I must say that isn't easy at all, especially for one thing: callbacks !
Everything is async, so I wonder how do I must do to get results from my mongodb.
var user = Meteor.users.findOne({username: "john"});
return (user); // sometimes returns "undefined"
...
var user = Meteor.users.findOne({username: "john"});
if (user) // so ok, I check if it exists!
return (user); // Cool, I got my user!
return (); // Ok and what should I return here? I want my user!
I don't want to be dirty and put like setTimeout everywhere.
Anybody has a solution for this ?
EDIT :
I noticed in router.js with console.log that my data is returned 4 times. 2 times with an undefined value and 2 other times with the expected value. In the view, it's still undefined.
Why the router passes like 4 times in this route ? Does it display the first result of the return value in the router ?
What should I return if the find() doesn't find anything ?
EDIT 2: Here is some code to understand.
this.route('profilePage', {
path: 'profil/:_id?',
waitOn: function() {
return [
Meteor.subscribe('article', { prop: this.params._id}), // id can be id or username
Meteor.subscribe('article', { userId: this.params._id}), // id can be id or username
Meteor.subscribe('params'),
Meteor.subscribe('profil', (this.params._id ? this.params._id : Meteor.userId()))
];
},
data: function() {
if (this.params._id) {
var user = Meteor.users.findOne(this.params._id);
if (!user)
user = Meteor.users.findOne({username: this.params._id});
console.log(user);
return user;
}
else if (Meteor.userId())
return Meteor.user();
else
Router.go("userCreate");
}
});
I get this on the console:
http://puu.sh/debdJ/69419911f7.png
(text version following)
undefined
undefined
Object_id: "o3mgLcechYTtHPELh"addresses: (....)
Object_id: "o3mgLcechYTtHPELh"addresses: (....)
findOne(yourId) is a sync method which is equivalent to find({ _id: yourId}, callback). The difference is that find() allows you to define a callback. If you don't pass a callback to find() this method will be sync.
check wrapAsync: http://docs.meteor.com/#/full/meteor_wrapasync
It allows you to code in a sync style with a async operations.
Free lesson on EventedMind: https://www.eventedmind.com/feed/meteor-meteor-wrapasync
My experience thus far is that the Meteor Mongodb package is that the functions do not generally provide callbacks (for some reason insert does...), the functions are atomic (thus sync).
There are meteor packages that can make Mongodb async if you want (I havn't tried any).
I guess this sync approach is in line with the simple maintenance goal of Mongodb. Thinking about it, one of my pet peeves using Node is working with async callback waterfalls/nests, they are a pain to create and maintain... and hopefully this will make my code easier to read and understand and change...
var future = new Future();
var _h = Hunts.findOne({huntId});
if(_h) {
future.return(_h)
} else {
return future.wait();
}
on server/startup.js you need:
Future = Npm.require('fibers/future');

AngularJS DOM update for loop progress

I have a number of AJAX calls that need to be run for every entry in an array, I'm trying to supply some visual feedback on the progress of the loop through the array, the model is being updated correctly but i'm not seeing anything updated in the view trying to call $digest in the loop has no effect on the DOM.
I've tried adding $apply to the function in the inner loop but I'm still seeing no change.
$scope.UploadEntry = function(item){
var oDBGet = new htmldb_Get(null,
$v('pFlowId'),
"APPLICATION_PROCESS=UploadTargetDates",
$v('pFlowStepId'));
oDBGet.add('EX_TRD',$scope.Ext.TRDDate.val);
oDBGet.add('EX_MAX_TRD',$scope.Ext.MaxTRDDate.val);
oDBGet.add('EX_READ',Ext.ReadDownloadCheck);
oDBGet.get();
};
$scope.ShowUploadModal = true;
$scope.UploadDone = 0;
for(i in submissionList)
{
$scope.UploadEntry(submissionList[i]);
$scope.UploadDone += 1;
}
$scope.ShowUploadModal = false;
But the view:
<div class="UploadModal" ng-show="ShowUploadModal">
Uploading entries: {{UploadDone}} complete
</div>
Never seen as the entries are uploaded, but does show at the end of the loop if I take the $scope.ShowUploadModal = false; out from the end of the loop.
UPDATE
After discussion, author states that the http request is synchronous. The problem is still about "sychronity", but a little bit tricker. Take a look at Angular digest concepts. It basically runs all watchers, expressions (binds) and process all $evalAsync over and over until there is no change in the watches result and expressions anymore. Just after this the DOM is updated.
So, the problem is that all your sync request are being resolved prior to the end of the digest cycle, and the DOM render will only happens after the digest cycle finishes processing.
The simplest way to solve your problem, as you state you can't change API to call async, is to ensure your requests are asynchronous, grab their promises and only hide uploadModal when all of them had been completed (this can be achieved with promises API, read promises API and $timeout). Like this:
var loadingPromises = [];
$scope.ShowUploadModal = true;
$scope.UploadDone = 0;
for(i in submissionList) {
loadingPromises.push($timeout((function(index) {
return function() {
$scope.UploadEntry(submissionList[index]);
$scope.UploadDone += 1;
};
})(i), 0));
}
$q.all(loadingPromises).then(function() {
$scope.ShowUPloadMOdel = false;
});
Note the closure I created to make sure the correct index is passed to the request, and you need to inject $q service to your controller. Although this is going to solve your problem, you should create a service and move your loading logic there, returning a promise (change your $scope references to parameters):
app.service('yourLoaderService', function($timeout) {
this.load = function(url) {
return $timeout(function() {
var oDBGet = new htmldb_Get(null,
$v('pFlowId'),
"APPLICATION_PROCESS=UploadTargetDates",
$v('pFlowStepId'));
oDBGet.add('EX_TRD',$scope.Ext.TRDDate.val);
oDBGet.add('EX_MAX_TRD',$scope.Ext.MaxTRDDate.val);
oDBGet.add('EX_READ',Ext.ReadDownloadCheck);
oDBGet.get();
}, 0);
};
});
And your controller:
var loadingPromises = [];
$scope.ShowUploadModal = true;
$scope.UploadDone = 0;
for(i in submissionList) {
var promise = yourLoaderService.load(submissionList[i]).then(function() {
$scope.UploadDone++;
});
loadingPromises.push(promise);
}
$q.all(loadingPromises).then(function() {
$scope.ShowUPloadMOdel = false;
});
First answer
This is a conception error. You seem to come from a synchronous server side background, maybe Python, I don't know. If this is your real code, then the problem is that your code is completely synchronous. You are showing upload model, looping all the entries and hiding the model and this entire code happens in milliseconds (or less) all before the DOM gets rendered even once.
This happens because when you ask for the upload, Javascript doesn't hang on the uploading process, it just asks and keep going. You can find something about async programming here, here, here and here.
You have to hook up your uploaded count in the upload callbacks. I don't know what you are using to upload, but your $scope.uploadEntry shall return a promise, then you wait it to be done and update the count.
$scope.ShowUploadModal = true;
$scope.UploadDone = 0;
for(i in submissionList)
{
$scope.UploadEntry(submissionList[i]).then(function() {
$scope.UploadDone += 1;
scope.ShowUploadModal = $scope.UploadDone !== submissionList.length;
});
}
If you're using $http for the uypload job, just return it returns, as it is already a promise and change .then(funciton per .success(function. If not, this is going to be a little more complicated, and you need to read the Angular docs on promises.
Just a side note, you should take a look at Javascript naming convetions. Javascript normally assume cammelCase variables, not PascalCase. Here's David Crackford's convetion.

How to return Mongoose results from the find method?

Everything I can find for rending a page with mongoose results says to do it like this:
users.find({}, function(err, docs){
res.render('profile/profile', {
users: docs
});
});
How could I return the results from the query, more like this?
var a_users = users.find({}); //non-working example
So that I could get multiple results to publish on the page?
like:
/* non working example */
var a_users = users.find({});
var a_articles = articles.find({});
res.render('profile/profile', {
users: a_users
, articles: a_articles
});
Can this be done?
You're trying to force a synchronous paradigm. Just does't work. node.js is single threaded, for the most part -- when io is done, the execution context is yielded. Signaling is managed with a callback. What this means is that you either have nested callbacks, named functions, or a flow control library to make things nicer looking.
https://github.com/caolan/async#parallel
async.parallel([
function(cb){
users.find({}, cb);
},
function(cb){
articles.find({}, cb);
}
], function(results){
// results contains both users and articles
});
I'll play the necromancer here, as I still see another, better way to do it.
Using wonderful promise library Bluebird and its promisifyAll() method:
var Promise = require('bluebird');
var mongoose = require('mongoose');
Promise.promisifyAll(mongoose); // key part - promisification
var users, articles; // load mongoose models "users" and "articles" here
Promise.props({
users: users.find().execAsync(),
articles: articles.find().execAsync()
})
.then(function(results) {
res.render('profile/profile', results);
})
.catch(function(err) {
res.send(500); // oops - we're even handling errors!
});
Key parts are as follows:
Promise.promisifyAll(mongoose);
Makes all mongoose (and its models) methods available as functions returning promises, with Async suffix (.exec() becomes .execAsync(), and so on). .promisifyAll() method is nearly-universal in Node.JS world - you can use it on anything providing asynchronous functions taking in callback as their last argument.
Promise.props({
users: users.find().execAsync(),
articles: articles.find().execAsync()
})
.props() bluebird method takes in object with promises as its properties, and returns collective promise that gets resolved when both database queries (here - promises) return their results. Resolved value is our results object in the final function:
results.users - users found in the database by mongoose
results.articles - articles found in the database by mongoose (d'uh)
As you can see, we are not even getting near to the indentation callback hell. Both database queries are executed in parallel - no need for one of them to wait for the other. Code is short and readable - practically corresponding in length and complexity (or rather lack of it) to wishful "non-working example" posted in the question itself.
Promises are cool. Use them.
The easy way:
var userModel = mongoose.model('users');
var articleModel = mongoose.model('articles');
userModel.find({}, function (err, db_users) {
if(err) {/*error!!!*/}
articleModel.find({}, function (err, db_articles) {
if(err) {/*error!!!*/}
res.render('profile/profile', {
users: db_users,
articles: db_articles
});
});
});
Practically every function is asynchronous in Node.js. So is Mongoose's find. And if you want to call it serially you should use something like Slide library.
But in your case I think the easiest way is to nest callbacks (this allows f.e. quering articles for selected previously users) or do it completly parallel with help of async libraries (see Flow control / Async goodies).
I have a function that I use quite a bit as a return to Node functions.
function freturn (value, callback){
if(callback){
return callback(value);
}
return value;
};
Then I have an optional callback parameter in all of the signatures.
I was dealing with a very similar thing but using socket.io and DB access from a client. My find was throwing the contents of my DB back to the client before the database had a chance to get the data... So for what it's worth I will share my findings here:
My function for retrieving the DB:
//Read Boards - complete DB
var readBoards = function() {
var callback = function() {
return function(error, data) {
if(error) {
console.log("Error: " + error);
}
console.log("Boards from Server (fct): " + data);
}
};
return boards.find({}, callback());
};
My socket event listener:
socket.on('getBoards', function() {
var query = dbConnection.readBoards();
var promise = query.exec();
promise.addBack(function (err, boards) {
if(err)
console.log("Error: " + err);
socket.emit('onGetBoards', boards);
});
});
So to solve the problem we use the promise that mongoose gives us and then once we have received the data from the DB my socket emits it back to the client...
For what its worth...
You achieve the desired result by the following code. Hope this will help you.
var async = require('async');
// custom imports
var User = require('../models/user');
var Article = require('../models/article');
var List1Objects = User.find({});
var List2Objects = Article.find({});
var resourcesStack = {
usersList: List1Objects.exec.bind(List1Objects),
articlesList: List2Objects.exec.bind(List2Objects),
};
async.parallel(resourcesStack, function (error, resultSet){
if (error) {
res.status(500).send(error);
return;
}
res.render('home', resultSet);
});