nodejs, mongodb - How do I operate on data from multiple queries? - javascript

I'm new to JS in general, but I am trying to query some data from MongoDB. Basically, my first query retrieves information for the session with the specified session id. The second query does a simple geospacial query for documents with a location near the specified location.
I'm using the mongodb-native javascript driver. All of these query methods return their results in callbacks, so they're non-blocking. This is the root of my troubles. What I'm needing to do is retrieve the results of the second query, and create an Array of sessionIds of all the returned documents. Then I'm going to pass those to a function later. But, I can't generate this array and use it anywhere outside the callback.
Does anyone have any idea how to properly do this?
db.collection('sessions', function(err, collection) {
collection.findOne({'sessionId': client.sessionId}, function(err, result) {
collection.find({'geolocation': {$near: [result.geolocation.latitude, result.geolocation.longitude]}}, function(err, cursor) {
cursor.toArray(function(err, item) {
console.log(item);
});
});
});

Functions are the only thing on javascript that "enclose" scope.
This means that the variable items in your inner callback function are not accessible on the outer scope.
You can define a variable in the outer scope so it will be visible to all the inner ones:
function getItems(callback) {
var items;
function doSomething() {
console.log(items);
callback(items);
}
db.collection('sessions', function(err, collection) {
collection.findOne({'sessionId': client.sessionId}, function(err, result) {
collection.find({'geolocation': {$near: [result.geolocation.latitude, result.geolocation.longitude]}}, function(err, cursor) {
cursor.toArray(function(err, docs) {
items = docs;
doSomething();
});
});
});
});
}

Node.js is asynchronous, so your code should be written to match it.
I've found this model useful. Each nested callback jumble is wrapped in helper function that calls the argument callback 'next' with error code and result.
function getSessionIds( sessionId, next ) {
db.collection('sessions', function(err, collection) {
if (err) return next(err);
collection.findOne({sessionId: sessionId}, function(err, doc) {
if (err) return next(err);
if (!doc) return next(false);
collection.find({geolocation: {$near: [doc.geolocation.latitude, result.geolocation.longitude]}}.toArray(function(err, items) {
return next(err, items);
});
});
});
}
Then in your calling code
getSessionIds( someid, _has_items);
function _has_items(err, items) {
if( err ) // failed, do something
console.log(items);
}

Related

Async await call

I am having trouble understanding how to use async/await in my case scenario
MY CODE IS IMPLEMENTED AS FOLLOWS
getUserInfo = function () {
async.waterfall([
getActiveUser, getUsername, update],
function(err , result){
console.log(result)
});
function getActiveUser(callback) {
//i have a db search to find who is active right now
user.find({status: 'active'}, function (err , users){
callback(err, users)
})
}
function getUsername(users, callback) {
async.map(users, getUserInfoXmlrpc, function (err, result) {
callback(err, result);
});
}
function getUserInfoXmlrpc(user, doneCallback) {
// i make a xmlrpc call based on dates
}
}
i am having difficulties make the xmlrpc call for more than one day.
I tried to implement async await creating a function that stores the dates i need in an array but i couldn't make it work.
Any ideas what can i do cause i want to update users one by one as i am doing right now but have to check their info updates based on a xmlrpc call

How can I execute one query after another and process that data?

I have an app.get that will return customer data and customer purchases. Inside this app.get I need run two mysql calls and build a an array to pass back.
How can I execute one query after another and process that data?
app.get('/customer', function (req,res) {
var response1 = [];
var response2 = [];
var processedData = [];
connection.query('QUERY HERE', function(err, rows, fields) {
if (!err){
response.push({rows});
} else {
res.status(400).send(err);
}
});
//for loop 'response' results and perform another query
for (var i = 0; i < response1.length; i++) {
var row = response1[i];
connection.query('QUERY HERE FOR row.customerid', function(err, rows, fields) {
if (!err){
processedData.push({'Customer Name:' : row.customername, 'purchases' : rows});
} else {
res.status(400).send(err);
}
});
}
//Send json back
res.setHeader('Content-Type', 'application/json');
res.status(200).send(JSON.stringify(processedData));
});
There is a very convenient module called async.js that provides a bunch of functions for doing complex async operations. Particularly,
async.waterfall() is great when you need to pass down results from one async operation/task to another.
async.mapSeries() is great when you need to create a new array with results from an array of async operation/tasks.
Let's use both.
If I understood your code correctly, the code would look something similar to
app.get('/customer', function (req, res) {
async.waterfall([
// each task is passed a callback 'cb' as last argument;
// you MUST call it at least and at most once within each task;
// if you pass an error into the callback as the first argument, it will stop the async function
function task1 (cb1) {
//connection.query('QUERY HERE', function(err, rows, fields) {
// if (err) return cb1(err); // stop waterfall() if an error occurred
// cb1(null, rows, fields); // pass results down to next task
//});
connection.query('QUERY HERE', cb1); // shorter version
},
function task2 (rows, fields, cb2) {
// iterate and run async operation over each element in array 'rows'
async.mapSeries(rows, function getPurchases (row, cb3) {
connection.query('QUERY HERE FOR row.customerid', function (err, purchases, fields) {
if (err) return cb3(err); // stop mapSeries() if an error occurred
cb3(null, { 'Customer Name': row.customername, 'purchases': purchases })
});
}, function (err, customers) {
// when mapSeries() is done iterating OR if an error occurred, it will come here
if (err) return cb2(err); // stop waterfall() if an error occurred
cb2(null, customers)
});
// }, cb2); // shorter version
}
], function (err, customers) {
// when waterfall() is done all its tasks OR if an error occurred, it will come here
// handle error and send response here
});
});

Mongodb find() returns undefined (node.js)

Ive been playing around with mongodb in node.js. I have made a basic collection with some data (i know its there ive checked). When I try to run a find() on the collection it returns undefined. I dont know why this is. The code is below:
function get_accounts(){
var MongoClient = mongodb.MongoClient;
var url = "url";
MongoClient.connect(url, function (err, db) {
if (err) {
console.log('Unable to connect to the mongoDB server. Error:', err);
} else {
//HURRAY!! We are connected. :)
console.log('Connection established to database');
var collection = db.collection('accounts');
collection.find().toArray(function(err, docs) {
console.log("Printing docs from Array")
docs.forEach(function(doc) {
console.log("Doc from Array ");
console.dir(doc);
});
});
console.log("mission complete");
}
db.close();
}
);
}
If you know why this is happening i would like to hear your thoughts. thanks! The database is a mongolab hosted database if that makes any difference.
You are getting an undefined value because of the asynchronous nature of node.js, nowhere in your code exists logic that tells the console.log statement to wait until the find() statement finishes before it prints out the documents. You have to understand the concept of callbacks in Node.js. There are a few problems here, though, that you could fix. A lot of people getting started with node have the tendency to nest lots of anonymous functions, creating the dreaded "pyramid of doom" or callback hell. By breaking out some functions and naming them, you can make it a lot cleaner and easier to follow:
var MongoClient = require("mongodb").MongoClient
// move connecting to mongo logic into a function to avoid the "pyramid of doom"
function getConnection(cb) {
MongoClient.connect("your-mongo-url", function(err, db) {
if (err) return cb(err);
var accounts = db.collection("accounts");
cb(null, accounts);
})
}
// list all of the documents by passing an empty selector.
// This returns a 'cursor' which allows you to walk through the documents
function readAll(collection, cb) {
collection.find({}, cb);
}
function printAccount(account) {
// make sure you found your account!
if (!account) {
console.log("Couldn't find the account you asked for!");
}
console.log("Account from Array "+ account);
}
// the each method allows you to walk through the result set,
// notice the callback, as every time the callback
// is called, there is another chance of an error
function printAccounts(accounts, cb) {
accounts.each(function(err, account) {
if (err) return cb(err);
printAccount(account);
});
}
function get_accounts(cb) {
getConnection(function(err, collection) {
if (err) return cb(err);
// need to make sure to close the database, otherwise the process
// won't stop
function processAccounts(err, accounts) {
if (err) return cb(err);
// the callback to each is called for every result,
// once it returns a null, you know
// the result set is done
accounts.each(function(err, account) {
if (err) return cb(err)
if (hero) {
printAccount(account);
} else {
collection.db.close();
cb();
}
})
}
readAll(collection, processAccounts);
})
}
// Call the get_accounts function
get_accounts(function(err) {
if (err) {
console.log("had an error!", err);
process.exit(1);
}
});
You might have to add an empty JSON object inside the find.
collection.find({})
Documentation can be found here.
You must enter this code in an async function and you will be fine here data is the your desired value and you must use promises to not make your code look messy.
var accountCollection = db.collection('accounts);
let data = await accountCollection.find().toArray.then(data=>data).catch(err=>err);

Issue with Nested query execution in mongoose

I am trying out the following query. I am writing this to get list of documents, for each document i need to run some logic to get the tags list. Using this tag list i update the document with tags list obtained.
I am getting the tags list for 'updateAndPrint' function as blank. I guess its an issue with promise coming into picture.
Here is the query:
DB.todoTable.find()
.limit(10)
.exec(function (err, todos) {
var tags = [];
for (var todo in todos) {
var text = todos[todo].text;
var id = todos[todo]._id;
console.log(text);
tags = getTagsList(text);
(function updateAndPrint(id, tags) {
DB.todoTable.update({_id: id}, {$addToSet: {tags: {$each: tags}}},
function (err, numberUpdated, result) {
if (err) throw err;
(function printResult(id) {
DB.todoTable.findOne({_id: id})
.exec(function (err, todo) {
if (err) throw err;
console.dir(todo.tags);
});
})(id);
});
})(id, tags);
}
console.dir(tags);
});
How can i get this query to run. Or is there a better way to do the same logic.
Edit
'tags = getTagsList(text)' has to be performed before i run update operation.
Use findAndModify instead of update and set the option {new: true}.
UPDATE
You can pass your function to be called as a callback to your findTags, so instead findTags(text) you will have findTags(text, callback), where your callback will be the updateAndPrint function. So when you got all your data in the findTags you can call the callback.

My callbacks are wrong - I am returning my response to my error object - Nodejs

I am trying to learn node and understand how callbacks are working. I am doing this by trying to use the async lib. I am trying to hit my db with 2 separate calls and use the async lib to let me know when my object is ready to build. Here is my aysnc code:
async.parallel({
one: function(callback) {
getUser(id, callback);
},
two: function(callback) {
getUserServices(id, callback);
}
}, function(err, results) {
if(err) {
console.log(err);
new Error();
}
res.json(result);
});
Here are what my functions look like where I am calling the db. There are both basically the same function:
var getUser = function(id, callback) {
var query = client.query('SELECT * FROM USERS WHERE USER_ID=$1', [id]);
query.on('row', function(row, result) {
result.addRow(row);
});
query.on('end', function(result) {
callback(result);
});
};
My code hits the db and returns the user, but when it goes back up to the async code the user is in the err object. What am I doing wrong? How do I properly set up callbacks?
As pointed by damphat, your code should be
//additionally, handle errors
query.on('error', function(err){
callback(err) // The first argument to callback should be an error object
})
query.on('end', function(result){
callback(null, result) //passing null error object
})

Categories

Resources