Returning empty array in NodeJs using mongoose - javascript

I am trying to fill an array with records from a mongoDB database using mongoose. When I am trying to fill the records. It shows an empty array outside the function even though I am declaring the outside the function. Below is the code.
var saveMessageToVariable = function(){
var records = [];
var spark_ids = [];
var obj = new Object();
Message.find().distinct("spark_id").exec(function(err,data) {
data.forEach(function (id) {
if(id != null)
spark_ids.push(id);
});
// console.log(spark_ids.length);
spark_ids.forEach(function(spark_id){
Message.findOne({"spark_id":spark_id}).sort({"date":-1}).exec(function(err,data){
obj.spark_id = data.spark_id;
obj.meesage = data.message;
obj.date = data.date;
obj.message_id = data._id;
records.push(obj);
});
});
});
console.log(records);
}
When I run this, the log is showing an empty array. How do I resolve this issue?

It's an asynchronous call and as soon as data is fetched from database control shifts to next line and therefore prints the initial value, I would prefer you to use a callback like this:
function(spark_id,callback){
Message.findOne({"spark_id":spark_id}).sort({"date":-1}).exec(function(err,data){
obj.spark_id = data.spark_id;
obj.meesage = data.message;
obj.date = data.date;
obj.message_id = data._id;
callback(obj);
});
}
function(obj)
{
records.push(obj);
}
You two other approachs for this:
1) use try and catch block.
2) use async and await keyword.
Cheers!

I don't have much experience with moongoose, but according to the docs it supports promises since Version 4.
Then this should work:
//I assume you'll need this more often
function notNull(value){ return value != null; }
//returns a promise of the records-Array
var saveMessageToVariable = function(){
//returns a promise of a formated message
function getMessage( spark_id ){
return Message.findOne({ spark_id })
.sort({ date: -1 })
//.exec()
.then( formatMessage )
}
function formatMessage( msg ){
return {
spark_id: msg.spark_id,
message: msg.message,
date: msg.date,
message_id: msg._id
}
}
return Message.find()
.distinct("spark_id")
//.exec()
.then(function( ids ){
//waits for all findOnes to complete, then returns an Array
return Promise.all(
ids.filter( notNull ).map( getMessage )
));
}
I'm not sure, wether you need exec() in this code or not. You should check that.
//usage
saveMessageToVariable.then(function(records){
console.log(records);
})
btw. saveMessageToVariable doesn't reflect at all what this function does. You should choose a better name.

Related

Modify external object in promise function

I have an array of objects (books) that are missing some fields, so I'm using node-isbn module to fetch the missing data. However, I'm having trouble persisting the updates to the objects. Here's some example code:
const isbn = require('node-isbn');
var books = []; // assume this is filled in elsewhere
books.forEach(function(item) {
if (item.publication_year == '' ||
item.num_pages == '') {
isbn.provider(['openlibrary', 'google'])
.resolve(item.isbn)
.then(function (book) {
item.num_pages = book.pageCount;
item.publication_year = book.publishedDate.replace( /^\D+/g, '');
}).catch(function (err) {
console.log('Book not found', err);
});
}
console.log(item)
});
However, the console log shows that the num_pages and publication_year fields are still empty. How can I fix this?
Try not to use Promise inside forEach
Put your console.log inside the then block , it will print the result for you.You are doing a asynchronous operation by resolving promise so it will take some time for the data to come back , however since your console.log is outside of that promise , that will get executed first.
So if you want to see those values, you should put your console.log inside your then block.
However, You can use await and for of syntax to achieve the result
for await (item of books) {
if (item.publication_year == '' || item.num_pages == '') {
const book = await isbn.provider(['openlibrary', 'google'])
.resolve(item.isbn);
item.num_pages = book.pageCount;
item.publication_year = book.publishedDate.replace( /^\D+/g,'');
}
console.log(item)
}
console.log(books)

In calling URLs iteratively using http.get() and resolving using $q.all()

I am implementing this scenario where I have to fetch data from multiple URLs iteratively and process it with some business logic and display on screen. I am implementing this in the controller as it is a requirement. All is well until part-1 and I am getting the 6 promise objects in the promises array. But, I am not getting the data into metricData. I am seeing a null in the console while running in the browser. I am sure that the data is coming in the URL response. I feel I am doing something silly in the $q.all method. Is this correct?
var calculateMutationsInDepth = function(){
//Part-1
var promises=[];
var metricData=[];
for(var depth=0 ; depth<6 ; depth++){
var resourceUrl = urlService(depth);
promises.push($http.get(resourceUrl)
.then(function(response){
return response.data;
},function(status){
return status;
}));
}
//Part-2 Resolving the promise array below
$q.all(promises).then(function(data){
for(var eachResult=0; eachResult < data.length; eachResult++){
if(null != data[eachResult]){
var eachDataObject = data[eachResult];
//For debugging console.log(eachDataObject);
for(var objCount=0; objCount < eachDataObject.length; objCount++){
if(eachDataObject[objCount].scope === "PRJ" || eachDataObject[objCount].scope === "FIL")
metricData.push(eachDataObject[objCount]);
}
}
}
});
if(metricData != null){
analyzeMutationData(metricData); //Calling a function with the aggregated data array where business logic is present
}
};
calculateMutationsInDepth(); //Calling the above function
Yes, something silly.
As written, analyzeMutationData(metricData) is called synchronously whereas metricData is populated asynchronously inside the $q.all(promises).then() callback.
Also, as written the error handler function(status){ return status; } is inappropriate. Either :
omit the error handler entirely and allow any single $http error to prevent further processing in Part 2, or
return null, allowing processing in Part 2, and the if(dataObject != null) test in part 2 to filter out any such error.
Here's the revised code with a few other tidies and a demonstration of what can be done if calculateMutationsInDepth() returns a promise.
var calculateMutationsInDepth = function() {
//Part-1
var depth, promises = [];
for(depth=0; depth<6; depth++) {
promises.push($http.get(urlService(depth))
.then(function(response) {
return response.data;
}, function(error) {
return null; // error recovery - `dataObject` below will be null
}));
}
//Part-2 Aggregate the promises, extract metric data and apply business logic
return $q.all(promises).then(function(data) { // note `return` here
var dataObject, i, j, metricData = [];
for(i=0; i<data.length; i++) {
dataObject = data[i];
if(dataObject != null) {
for(j=0; j<dataObject.length; j++) {
if(dataObject[j].scope === "PRJ" || dataObject[j].scope === "FIL") {
metricData.push(dataObject[j]);
}
}
}
}
// Analyse here, inside the .then()
if(metricData.length > 0) { // metricData is an array and will never be null, therefore test metricData.length.
return analyzeMutationData(metricData);
}
return null;
});
};
calculateMutationsInDepth().then(function(analysis) {
// all complete
// `analysis` is either null or whatever `analyzeMutationData(metricData)` returned.
}).catch(function(error) {
console.log(error);
});
Hope this helps you out! Let me know if it doesn't.

getting object Parse from a loop

In a Parse server function, it's getting Matches and profiles.
From a query to get matches another function is called to get Profiles by id but the result is :
{"_resolved":false,"_rejected":false,"_reso resolvedCallbacks":[],"_rejectedCallbacks":[]}
Main Query :
mainQuery.find().then(function(matches) {
_.each(matches, function(match) {
// Clear the current users profile, no need to return that over the network, and clean the Profile
if(match.get('uid1') === user.id) {
match.set('profile2', _processProfile(match.get('profile2')))
match.unset('profile1')
}
else if (match.get('uid2') === user.id) {
var profileMatch = _getProfile(match.get('profile1').id);
alert(">>>"+JSON.stringify(profileMatch));
match.set('profile1', _processProfile(match.get('profile1')))
match.unset('profile2')
}
})
the function to get Profile info:
function _getProfile(id){
var promise = new Parse.Promise();
Parse.Cloud.useMasterKey();
var queryProfile = new Parse.Query(Profile);
return queryProfile.equalTo("objectId",id).find()
.then(function(result){
if(result){
promise.resolve(result);
alert("!!!!"+result);
}
else {
console.log("Profile ID: " + id + " was not found");
promise.resolve(null);
}
},
function(error){
promise.reject(error)
});
return promise;
}
Just found this a little late. You've probably moved on, but for future readers: the key to solving something like this is to use promises as returns from small, logical asynch (or sometimes asynch, as in your case) operations.
The whole _getProfile function can be restated as:
function _getProfile(id){
Parse.Cloud.useMasterKey();
var queryProfile = new Parse.Query(Profile);
return queryProfile.get(id);
}
Since it returns a promise, though, you cannot call it like this:
var myProfileObject = _getProfile("abc123");
// use result here
Instead, call it like this:
_getProfile("abc123").then(function(myProfileObject) { // use result here });
Knowing that, we need to rework the loop that calls this function. The key idea is that, since the loop sometimes produces promises, we'll need to let those promises resolve at the end.
// return a promise to change all of the passed matches' profile attributes
function updateMatchesProfiles(matches) {
// setup mainQuery
mainQuery.find().then(function(matches) {
var promises = _.map(matches, function(match) {
// Clear the current users profile, no need to return that over the network, and clean the Profile
if(match.get('uid1') === user.id) {
match.set('profile2', _processProfile(match.get('profile2'))); // assuming _processProfile is a synchronous function!!
match.unset('profile1');
return match;
} else if (match.get('uid2') === user.id) {
var profileId = match.get('profile1').id;
return _getProfile(profileId).then(function(profileMatch) {
alert(">>>"+JSON.stringify(profileMatch));
match.set('profile1', _processProfile(match.get('profile1')))
match.unset('profile2');
return match;
});
}
});
// return a promise that is fulfilled when all of the loop promises have been
return Parse.Promise.when(promises);
}

Understanding JavaScript promises in Parse

I am developing an app in Parse and I'm trying to understand promises. I'm not finding very many working examples other than the very simple ones here: https://parse.com/docs/js/guide.
I'm querying the _User table. Then I loop through the users in an _.each loop. I'm running 2 cloud functions inside the loop for each iteration. At what point do I create the promise? Do I create one for each cloud function success within the loop? Or do I push each success return value onto an array and make that the promise value outside of the loop? I've tried both but I can't figure out the correct syntax to do either, it seems.
I'll break it down in pseudo-code because that may be easier than actual code:
var query = new Parse.Query(Parse.User);
query.find().then(function(users){
loop through each user in an _.each loop and run a cloud function for each that returns a number.
If the number > 0, then I push their username onto array1.
Then I run a 2nd cloud function on the user (still within the _.each loop) that returns a number.
If the number > 0, then I push their username onto array2.
}).then(function(promisesArray){
// I would like "promisesArray" to either be the 2 arrays created in the preceding section, or a concatenation of them.
// Ultimately, I need a list of usernames here. Specifically, the users who had positive number values from the cloud functions in the preceding section
concatenate the 2 arrays, if they're not already concatenated
remove duplicates
send push notifications to the users in the array
});
Questions:
- At what point do I create & return promises & what syntax should I use for that?
- Should .then(function(promisesArray){ be .when(function(promisesArray){ (when instead of then)?
Thank you both for your ideas! This is what ultimately worked:
var query = new Parse.Query(Parse.User);
query.find().then(function(users){
var allPromises = [];
var promise1, promise2;
_.each(users, function(user){
if(user.get("myvalue") != "undefined" && user.get("myvalue") != ""){
promise1 = Parse.Cloud.run("getBatch1", {param1: param1value, param2: param2value})
.then(function(numResult){
if(Number(numResult) > 0){
return Parse.Promise.as(user.getUsername());
}
});
}
allPromises.push(promise1);
if(user.get("anothervalue")==true){
promise2 = Parse.Cloud.run("getBatch2", {param1: param1value, param2: param2value})
.then(function(numResult2){
if(Number(numResult2) > 0){
return Parse.Promise.as(user.getUsername());
}
});
}
allPromises.push(promise2);
});
// Return when all promises have succeeded.
return Parse.Promise.when(allPromises);
}).then(function(){
var allPushes = [];
_.each(arguments, function(pushUser){
// Only add the user to the push array if it's a valid user & not already there.
if(pushUser != null && allPushes.indexOf(pushUser) === -1){
allPushes.push(pushUser);
}
});
// Send pushes to users who got new leads.
if(allPushes.length > 0){
Parse.Push.send({
channels: allPushes,
data: {
alert: "You have new leads."
}
}, {
success: function () {
response.success("Leads updated and push notifications sent.");
},
error: function (error) {
console.log(error);
console.error(error);
response.error(error.message);
}
});
}
response.success(JSON.stringify(allPushes));
}, // If the query was not successful, log the error
function(error){
console.log(error);
console.error(error);
response.error(error.message);
});
I'm not familiar with Parse API but I'd do it this way. Of course, I can't test my code so tell me if it works or not:
var query = new Parse.Query(Parse.User);
query.find()
.then(function(users) {
var promises = [];
users.forEach(function(user) {
// the first API call return a promise so let's store it
var promise = cloudFn1(user)
.then(function(result) {
if (result > 0) {
// just a way to say 'ok, the promise is resolved, here's the user name'
return Parse.Promise.as(user.name);
} else {
// return another promise for that second API call
return cloudFn2(user).then(function(res) {
if (result > 0) {
return Parse.Promise.as(user.name);
}
});
}
});
// store this promise for this user
promises.push(promise);
});
// return a promise that will be resolved when all promises for all users are resolved
return Parse.Promise.when(promises);
}).then(function(myUsers) {
// remove duplicates is easy with _
myUsers = _.uniq(myUsers);
// do your push
myUsers.forEach( function(user) {
});
});
First, you need to understand what Promises are. From what I understand of what you're trying to do it should look something like this:
//constructs the Parse Object
var query = new Parse.Query(Parse.User);
//find method returns a Promise
var res = query.find()
//good names will be a Promise of an array of usernames
//whose value is above 0
var goodNames = res
.then(function(data) {
//assumes the find method returns an array of
//objects, one of the properties is username
//we will map over it to create an Array of promises
//with the eventual results of calling the AJAX fn
var numberPromises = data.map(function(obj) {
//wrap the call to the cloud function in a new
//promise
return new Promise(resolve, reject) {
someCloudFn(obj.username, function(err) {
if (err) {
reject(err);
} else {
resolve(num);
}
});
}
};
//Promise.all will take the array of promises of numbers
//and return a promise of an array of results
return [data, Promise.all(numberPromises)];
})
.then(function(arr) {
//we only get here when all of the Promises from the
//cloud function resolve
var data = arr[0];
var numbers = arr[1];
return data
.filter(function(obj, i) {
//filter out the objects whose username number
//is zero or less
return numbers[i] > 0;
})
.map(function(obj) {
//get the username out of the query result obj
return obj.username;
});
})
.catch(function(err) {
console.log(JSON.stringify(err));
});
Now whenever you need to use the list of usernames whose number isn't zero you can call the then method of goodNames and get the result:
goodNames.then(function(listOfNames) {
//do something with the names
});

Angularjs load resources on after the other using promise

Inside a service, I would like to load a resource using $http. Once loaded resource, I want to store it in a variable. Then, I need to load a child resource and store it too. I know that the promise is designed for this kind of work, but there seems to be so much how to use it I get a little confusion. Here is my code:
var project = {};
var todo = {};
function init(){
var idProject = 21;
var idTodo = 6;
// If idProject is specified
if ( idProject != null ) {
// First, load project data
var promise = WorkspaceManager.getProject($rootScope.workspace, idProject);
// Then save project data
promise.then(function(response){
project = response.data;
return project;
});
if ( idTodo != null ) {
// Then load todo data
promise.then(function(project){
return ProjectManager.getTodo(project, idTodo);
});
// Then save todo data
promise.then(function(response){
todo = response.data;
return todo;
});
}
}
console.log(project); // returns {}
}
init()
Thanks in advance !
If I understand correctly, this is trickier than it appears at first glance. You appear to need a function that chains two asynchronous processes and returns a promise of a composite value comprising data acquired by the first and the second processes.
At least two approaches are available :
Easy but inelegant: In each asynchronous process, accumulate the required values as properties of an outer object.
Elegant but awkward: From each asynchronous process, accumulate the required values as properties of an object, a promise of which is returned.
The code below adopts the first approach :
function init() {
var idProject = 21;
var idTodo = 6;
var projectObj = {};//This object acts as a "bank" for asynchrounously acquired data.
if ( idProject != null ) {
return WorkspaceManager.getProject($rootScope.workspace, idProject).then(function(response) {
projectObj.project = response.data;//put `response.data` in the bank as .project.
if( idTodo != null ) {
return ProjectManager.getTodo(response.data, idTodo);
}
}).then(function(toDo) {
projectObj.toDo = toDo;//put `toDo` in the bank as .toDo .
return projectObj;
});
}
}
init().then(function(projectObj) {
console.log(projectObj.project);
console.log(projectObj.toDo);
});
Or (still the first approach) with error handlers :
function init() {
var idProject = 21;
var idTodo = 6;
var projectObj = {};//This object acts as a "bank" for asynchrounously acquired data.
if ( idProject != null ) {
return WorkspaceManager.getProject($rootScope.workspace, idProject).then(function(response) {
projectObj.project = response.data;//put `response.data` in the bank as .project.
if( idTodo != null ) {
return ProjectManager.getTodo(response.data, idTodo);
}
else {
$q.defer().reject('idTodo invalid');
}
}).then(function(toDo) {
projectObj.toDo = toDo;//put `toDo` in the bank as .toDo .
return projectObj;
});
}
else {
return $q.defer().reject('idProject invalid');
}
}
init().then(function(projectObj) {
console.log(projectObj.project);
console.log(projectObj.toDo);
}, function(errorMessage) {
console.log(errorMessage);
});
untested
The way you doing, you're creating "brothers" promise derived from the first promise. All the promises are going to be resolved as soon as WorkspaceManager.getProject promise has been resolved. What I believe you want is to chain them all, in way that when first promise gets resolved, yo asks for Todo data, when you got it, you asks to save it. If this is the case, you shall grab the derived promise from each promise.
// Then save project data
promise = promise.then(function(response){
project = response.data;
return project;
});
// Then load todo data
promise = promise.then(function(project){
return ProjectManager.getTodo(project, idTodo);
});
// Then save todo data
promise.then(function(response){
todo = response.data;
return todo;
});
Trying to illustrate a bit more, the first approach is like:
var mainPromise = ...;
mainPromise.then(function loadTodo(mainPromiseReturn){});
mainPromise.then(function saveTodo(mainPromiseReturn){});
The loadTodo and saveTodo are pararell, they're not chained to each other. They both receive the same data.
The approach I suggest is like:
var mainPromise = ...;
mainPromise
.then(function loadTodo(mainPromiseReturn){})
.then(function saveTodo(loadTodoReturn){});

Categories

Resources