I have images model and users model.
every image has a user_id field of a user and I want to get the picture of the user and name, add it to the image object and return an array of images.
When I am trying to add author_image field to ONE image I don't have any errors,
But when I am looping over all the images the app crashes the output is that imagesData is undefined as well as userData.
I tried using promises but again I get an error.
What is the best way I can do that without the undefined error?
router.route('/images/all')
.get(function(req,res){
var response = {};
var imagesData = {};
images.find({}).lean().exec(function(err,data){
// console.log(data);
imagesData = data;
if (!err) {
for (var i = 0; i < imagesData.length; i++) {
users.find(({'_id': imagesData[i].user_id}),function(err,userData){
console.log(userData);
imagesData[i].author_pic = userData[0].profile_image;
});
}
}
res.json(imagesData);
});
});
What you missed out is that find operation is not a synchronous operation. So all your find operation immediately move on to the next line.
Although there are multiple ways to handle such situation, I tend to use promises (Q library).
The code would look like this
var Q = require('q');
var defer = Q.defer();
images.find({}).lean().exec(function (err, data) {
// console.log(data);
imagesData = data;
var promiseArr = [];
if (!err) {
for (var i = 0; i < imagesData.length; i++) {
var innerDefer = Q.defer();
users.find(({'_id': imagesData[i].user_id}), function (err, userData) {
console.log(userData);
defer.resolve(userData[0].profile_image);
});
promiseArr.push(innerDefer);
}
}
Q.all(promiseArr).then(function (results) {
for (var i = 0; i < imagesData.length; i++) {
if (Q.isPromise(results[i])) {
results[i] = results[i].valueOf();
}
imagesData[i].author_pic = results[i];
}
res.json(imagesData);
})
});
In this case I am using the Q.all method which basically waits for all the find to finish, and executes only then.
Related
I'm trying to read multiple data from database, put them into an array, and deal with the array. The code looks like this:
var array = [];
// first for loop
for (var i = 0; i < 10; i++) {
db.read(i, function(rs) { // read data from database and put it into array
array.push(rs);
}
}
// second for loop
for (int i = 0; i < 10; i++) {
console.log(array[i]);
}
However, this piece of code will not work because the second for loop will execute before the first loop ends. Is there any good solutions? BTW, I've used promise like this:
var array = [];
var promise = new Promise(function(resolve, reject) {
for (var i = 0; i < 10; i++) {
db.read(i, function(rs) { // read data from database and put it into array
array.push(rs);
}
}
resolve(array);
};
promise.then(function(array) {
for (int i = 0; i < 10; i++) {
console.log(array[i]);
}
};
It doesn't work either, it seems that the resolve will not wait until all the db read operations finish. So when will the resolve wait until all the previous code finish?
the best way to complete your requirement use async.
var name = ["shekhar", 'vishal', "param", "abhishek", 'amit'];
for (var i = 0; i < 5; i++) {
var item = name[i];//execute your first loop here
}
async.eachSeries(name, processData, function (err) {
if (err) {
res.json({status: 0, msg: "OOPS! How is this possible?"});
}
res.json("Series Processing Done");
})
function processData(item, callback) {
//execute your second loop here
});
Using promises, the asynchronous block of code should return a promise.
The database read function should return a promise, and can easily be rewritten as
function readFromDatabase(i){
return new Promise(function(resolve, reject){
db.read(i, function(rs) { // read data from database and put it into array
resolve(rs);
}
});
}
You then run your loop and get all the values from the database, await the promises and access the result in the .then() function where you can iterate of them.
//We create an empty array to store our promises
var arrayOfPromises = [];
for (var i = 0; i < 10; i++) {
//We push each promise to the array
arrayOfPromises.push(readFromDatabase(i));
}
//We wait until all promises have
Promise.all(arrayOfPromises)
.then(function(arrayOfResults){
//arrayOfResults is an array with the resolved values from the 'readFromDatabase' function
})
.catch(function(){
//Handle errors
});
So I have a function that works just fine.
Here's the code for context:
"use strict";
//Get the splunk sdk
var splunkjs = require('splunk-sdk');
//get promise dependencies
Promise = require("bluebird");
//get request dependencies
let request = require('request-promise');
exports.splunkQuery = Promise.coroutine(function*(){
try{
// Create a Service instance and log in
let service = Promise.promisifyAll(new splunkjs.Service({
username:"user",
password:"pass",
scheme:"https",
host:"mysplunk",
port:"8089",
version:"6.3.2"
}));
// Pass the search query
let searchQuery = yield Promise.resolve("search index=edex FIDELITY.CCB.LPS.PROD.MORTFILE* PNOD=CD.HNBNOLA RECI=CTRC");
// Search params (No Idea what these do)
let searchParams = yield Promise.resolve({
exec_mode: "blocking"
});
//get the job object from splunk
let job = yield service.searchAsync(searchQuery,searchParams);
//add the fetchJson function to the job object
job.fetchJsonAsync = fetchJsonAsync;
//get the results
let results = yield Promise.resolve(job.fetchJsonAsync());
//return the resuling value
console.log(results.events[0]._raw)
} catch (e){
console.log(e)
}
});
//A function that returns a promise
function fetchJsonAsync(){
//Create a result object and return it
var resultsArrayObject = {}
//return a promise
return new Promise (function(resolve,reject){
//display the job id
console.log("Search job id: " + this.sid);
this.results({}, function(err, results) {
var fields = results.fields;
var rows = results.rows;
//Create an array to hold all of the event objects
var eventsArray = [];
for(var i = 0; i < rows.length; i++) {
var values = rows[i];
//Create an empty object to hold the atributes of an event
var eventObject = {}
for(var j = 0; j < values.length; j++) {
var field = fields[j];
var value = values[j];
eventObject[field] = value;
}
eventsArray.push(eventObject);
}
//populated the results object
resultsArrayObject["events"]=eventsArray;
if(err){
//reject the promise if there is an error
reject(err);
console.error(err)
}else{
//fulfill the promise
resolve(resultsArrayObject);
}
})
}.bind(this)); //This needs to be binded to this function so that it can get the rest of the functions properly
}
And call the function like this:
var test = Promise.coroutine(function*(request,response){
let result = yield Promise.resolve(splunk.splunkQuery());
console.log(result);
});
test();
I know I'm probably doing something sketchy when I add my own function to the job object yielded by the search query, but it works.
I wanted to break every step into individual methods in a single class, because I didn't want to create a new connection every time I wanted to make a query. So I wrote this:
"use strict";
//Get the splunk sdk
var splunkjs = require('splunk-sdk');
//get promise dependencies
Promise = require("bluebird");
//Create Class
let Splunk = {};
//Define object variables
Splunk._service = {}
Splunk._serviceDetails = {}
Splunk._query = ""
Splunk._searchParameters = {}
//Define object functions
//Set the login information
Splunk.setServiceDetails = function(serviceDetails){
return new Promise (function(resolve,reject){
this._serviceDetails = serviceDetails;
resolve(true)
}.bind(this));
}
Splunk.getServiceDetails = function(serviceDetails){
return new Promise (function(resolve,reject){
resolve(this._serviceDetails)
}.bind(this));
}
//Start the service
Splunk.startService = function(){
return new Promise (function(resolve,reject){
this._service = Promise.promisifyAll(new splunkjs.Service(this._serviceDetails));
resolve(true)
}.bind(this));
}
//Start the service
Splunk.getService = function(){
return new Promise (function(resolve,reject){
resolve(this._service)
}.bind(this));
}
Splunk.setQuery = function(query){
return new Promise (function(resolve,reject){
this._query = query;
resolve(true)
}.bind(this));
}
Splunk.getQuery = function(query){
return new Promise (function(resolve,reject){
console.log("Getting Query")
resolve(this._query)
}.bind(this));
}
Splunk.setSearchParameters = function (searchParameters){
return new Promise (function(resolve,reject){
this._searchParameters = searchParameters;
resolve(true)
}.bind(this));
}
Splunk.getSearchParameters = function (searchParameters){
return new Promise (function(resolve,reject){
console.log("Getting Search Parameters")
resolve(this._searchParameters)
}.bind(this));
}
Splunk.execute = Promise.coroutine(function*(){
try{
// Create a Service instance and log in
let service = yield this.getService();
// Pass the search query
let searchQuery = yield this.getQuery();
// Search params (No Idea what these do)
let searchParams = this.getSearchParameters();
//get the job object from splunk
let job = yield service.searchAsync(searchQuery,searchParams);
//add the fetchJson function to the job object
job.fetchJsonAsync = this._fetchJsonAsync;
//get the results
let results = yield Promise.resolve(job.fetchJsonAsync());
//return the resuling value
return results
} catch (e){
console.log(e)
}
});
Splunk._fetchJsonAsync = function(){
//Create a result object and return it
var resultsArrayObject = {}
//return a promise
return new Promise (function(resolve,reject){
//display the job id
console.log("Search job id: " + this.sid);
this.results({}, function(err, results) {
var fields = results.fields;
var rows = results.rows;
//Create an array to hold all of the event objects
var eventsArray = [];
for(var i = 0; i < rows.length; i++) {
var values = rows[i];
//Create an empty object to hold the atributes of an event
var eventObject = {}
for(var j = 0; j < values.length; j++) {
var field = fields[j];
var value = values[j];
eventObject[field] = value;
}
eventsArray.push(eventObject);
}
//populated the results object
resultsArrayObject["events"]=eventsArray;
if(err){
//reject the promise if there is an error
reject(err);
console.error(err)
}else{
//fulfill the promise
resolve(resultsArrayObject);
}
})
}.bind(this)); //This needs to be binded to this function so that it can get the rest of the functions properly
}
module.exports = Splunk;
And I call it like this (I plan to abstract the order of execution after its working):
var test = Promise.coroutine(function*(request,response){
//let result = yield Promise.resolve(splunk.splunkQuery());
try{
yield splunkClass.setServiceDetails({
username:"user",
password:"pass",
scheme:"https",
host:"mysplunk",
port:"8089",
version:"6.3.2"
});
console.log("set service details");
yield splunkClass.startService();
console.log("started service");
yield splunkClass.setQuery("search index=edex FIDELITY.CCB.LPS.PROD.MORTFILE* PNOD=CD.HNBNOLA RECI=CTRC");
console.log("set query async")
yield splunkClass.setSearchParameters({
exec_mode: "blocking"
});
console.log("set search parameters")
let result = yield splunkClass.execute()
console.log("executed")
yield Promise.resolve(console.log(result));
Promise.resolve(response.send(result))
}catch(exception){
console.log(exception)
}
});
test();
But I get the following error when I run this version:
Search job id: 1470853434.636329
/Users/nit/Documents/Progs/JavaScript/he_dashboard/services/splunkClass.js:109
for(var i = 0; i < rows.length; i++) {
^
TypeError: Cannot read property 'length' of undefined
at /Users/nit/Documents/Progs/JavaScript/he_dashboard/services/splunkClass.js:109:30
I know that its entering the _fetchJsonAsync method and making the connection correctly, because its logging the sid (job id) before the error. It seems like the actual job object is different this time around and I can't seem to figure out why.
Thanks in advance,
Maz
PS: I'm still in the process of learning node, while trying to use it for something useful. Please let me know if I'm doing other stupid things along the way. Thanks!!
I have a problem with call back functions and loops in nodejs how can I do so that the response should be send after the call back functions execution completion
app.post('/data', function(req,res){
var send = []
for (var i =0; i < p_objReq.body.quantity; i++) {
Instruments.count({//condetion}, function(err, count){
//using count and other parameters I am generating the code which is unique
Instruments.find({id: 'rondom_generated_code'},function(err, instrumentsCount){
if(instrumentsCount.length == 0){
send.push(rondom_generated_code)
if(send.length == p_objReq.body.quantity)
p_objRes.json({success : true, data : send})
}
else{
Instruments.count({//condetion}, function(err, count){
//using count and other parameters I am generating the code which is unique
send.push(rondom_generated_code)
if(send.length == p_objReq.body.quantity)
p_objRes.json({success : true, data : send})
})
}
})
})
}
})
when i wrote like this its sending the same random code that is last generated one. I tried removing the whole thing and written in function and called back but its also not working
One solution is to use Q.js, which is one of Promise library. More APIs of Q.js, please refer to the link in it. Here is one sample codes may could help you, If I catch you correctly for your question.
var Promise = require('Q');
app.post('/data', function(req,res){
var send = [];
var p = function () {
var deferred = Q.defer();
Instruments.count({condetion}, function(err, count){
//using count and other parameters I am generating the code which is unique
if (err) {
deferred.reject(new Error(error));
} else {
send.push(randomnumber)
}
});
return deferred.promise;
}
var ps = [];
for (var i =0; i < p_objReq.body.quantity; i++) {
ps.push(p);
}
Q.all(ps).then(function(){ res.json({success : true, data : send}); });
});
I am trying to run a find inside another find and I am not getting any results from the second find operation.
User.find({}, function (err, docs) {
for (i = 0; i < docs.length; i++) {
var tmp = '';
UserGroups.find({userName: docs[i].userName}, function (errin, groups) {
for (g = 0; g < groups.length; g++) {
tmp += ", " + groups[g].groupName;
//console.log(groups[g].groupName);
}
});
console.log(tmp);
//docs[i].group = that;
docs[i].username = decrypt(docs[i].username);
docs[i].password = '';
}
res.render('users', {users: docs});
});
Your UserGroups.find is going to be run asynchronously therefore console.log(tmp) is going to be run before your UserGroups.find has a chance to finish and your call is going to return before you get any results. If you want the results of the UserGroup.find you need to move all of your logic into that callback.
EDIT
This is I believe a far better approach in terms of predictability and query performance. Your previous approach the UserGroup.find being called n number of times. N being the number of users in your database. This approach the database is only queried twice. Once to get all the users and second to get all the groups.
User.find({}, function (err, docs) {
//Get all the usernames before executing the UserGroups query
var userNames = [];
users.forEach(function(element) {
userNames.push(element.userName);
});
UserGroups.find({userName: {$in : userNames}}, function (errin, groups) {
for (var i = 0; i < docs.length; i++) {
//get all the groups that belong to this user
var userGroups = groups.filter(function(value) {
return value.userName === docs[i].userName;
});
var tmp = "";
userGroups.forEach(function(element){
tmp += "," + element.groupName
});
//docs[i].group = that;
docs[i].username = decrypt(user[i].username);
docs[i].password = '';
}
res.render('users', {users: docs});
});
});
Also since it appears you are using Mongoose you can use the built in populate feature in Mongoose to "join" collections together
So let's say I have the following for loop
for(var i = 0; i < array.length; i++){
Model.findOne({ _id = array[i].id}, function(err, found){
//Some stuff
});
}
How do I make this code work? Every time I run it I get array[i] = undefinedbecause the mongo-db query is asynchronous and the loop has already iterated 5 times by the time the first query is even completed. How do I go about tackling this issue and waiting for the query to complete before going on to the next iteration?
This doesn't specifically answer your question, but addresses your problem.
I'd use an $in query and do the filtering all at once. 20 calls to the db is pretty slow compared to 1:
// grab your ids
var arrayIds = myArray.map(function(item) {
return item._id;
});
// find all of them
Model.find({_id: {$in: arrayIds}}, function(error, foundItems) {
if (error) {
// error handle
}
// set up a map of the found ids
var foundItemsMap = {};
foundItems.forEach(function(item) {
foundItemsMap[item._id] = true;
});
// pull out your items that haven't been created yet
var newItems = [];
for (var i = 0; i < myArray.length; i++) {
var arrayItem = myArray[i];
if ( foundItemsMap[arrayItem._id] ) {
// this array item exists in the map of foundIds
// so the item already exists in the database
}
else {
// it doesn't exist, push it into the new array
newItems.push(arrayItem);
}
}
// now you have `newItems`, an array of objects that aren't in the database
});
One of the easiest ways to accomplish something like you want is using promises. You could use the library q to do this:
var Q = require('q');
function fetchOne(id) {
var deferred = Q.defer();
Model.findOne({ _id = id}, function(err, found){
if(err) deferred.reject(err);
else deferred.resolve(found);
});
return deferred.promise;
}
function fetch(ids, action) {
if(ids.length === 0) return;
var id = ids.pop();
fetchOne(id).then(function(model) {
action(model);
fetch(ids, action);
});
}
fetch([1,2,3,4,5], function(model) { /* do something */ });
It is not the most beautiful implementation, but I'm sure you get the picture :)
Not sure if this is the right way, it could be a bit expensive but this how i did it.
I think the trick is to pull all your data and then looking for an id match.
Model.find(function(err, data) {
if (err) //handle it
for (var i=0; i<array.length; i++) {
for (var j=0; ij<data.length; j++) {
if(data[j].id == array[i].id) {
// do something
}
}
}
}