Mongoose/ express how to retrieve only the values of the array - node.js

I am new to node.js and want to do the following thing.
Write a query to fetch the annotation(array values) key from mongoDb and pass this array values [only ] as an argument to the second query.
Here is my code
// create the carousel based on the associated stills using keyword annotations
function findAssociatedArchivalStills(carousels, taskCb){
async.waterfall([
// 1st query
function findAssociatedAnnotations(archiveId, taskCb) {
Archive.findAnnotations(archiveId, function onResult(err,annotations){
console.log(annotations);
taskCb(err,annotations);
});
},
// 2nd query
function findAssociatedStills(annotations,taskCb) {
Still.findAssociatedStills(annotations,function onResult(err,stills){
taskCb(err,stills);
});
},
function buildCarousel(stills,taskCb) {
return taskCb(null, new Carousel({
title: 'Related Stills',
items: stills,
}));
},
], function onFinish(err) {
taskCb(null, carousels);
});
},
// done building the associated Episodes carousel
], function onFinish(err, carousels) {
handleResponse(err, res, carousels);
});
});
The methods are defined as follows
1st query definition in model
schema.statics.findAnnotations = function findAnnotations(archiveId, cb) {
this.findOne()
.where('_id', types.ObjectId(archiveId))
.select({'keywords':1, '_id':0})
.exec(cb);
};
2nd query definition in model
schema.statics.findAssociatedStills = function
findAssociatedStills(Annotations, cb) {
this.find()
.where({$text: { $search:Annotations}},{score:{$meta:"textScore"}})
.sort({score:{$meta:"textScore"}})
.limit(2)
.exec(cb);
};
THE PROBLEM IS
When I ran the 1st query , it is returning following
{ keywords:
[ 'IRELAND',
'ELECTIONS',
'PARTY_POLITICAL_BROADCASTS',
'FINE_GAEL' ] }
But the input to the next query should be only the values such as
'IRELAND', 'ELECTIONS', 'PARTY_POLITICAL_BROADCASTS', 'FINE_GAEL'
How to filter from the result only the values of the array without key
I know what will be the query in MongoDb
that is as follows
db.archives.episodes.find({_id:ObjectId("577cd9558786332020aff74c")}, {keywords:1, _id:0}).forEach( function(x) { print(x.keywords); } );
Is it good to filter it in the query or is it right way to filter in the returned script.
Please advice.Thanks for your time.

You're using series, not waterfall. And your archiveId cannot be set in the first function. You need to setup it before async.waterfall.
Here's the right syntax (with waterfall) :
function findAssociatedArchivalStills(carousels, masterCallback){
var archiveId = 'yourArchiveId';
async.waterfall([
// 1st query
function findAssociatedAnnotations(taskCallback) {
Archive.findAnnotations(archiveId, taskCallback);
},
// 2nd query
function findAssociatedStills(annotations, taskCallback) {
Still.findAssociatedStills(annotations,taskCallback);
},
function buildCarousel(stills, taskCallback) {
return taskCallback(null, new Carousel({
title: 'Related Stills',
items: stills
}));
}
], function onFinish(err) {
if (err){
return masterCallback(err);
}
masterCallback(null, carousels);
});
}
Documentation : http://caolan.github.io/async/docs.html#.waterfall
PS : Always use different names for your function's callback and your async functions callbacks.

Related

Mongoose updateOne with parameter {new:true} not showing actual updated value

I am struggling for a couple of hours to show the final value of an updated document (via mongoose updateOne). I successfully modify it as I can see "nModified: 1" when I call the endpoint on Postman, but I am not able to output the actual final document - even when using the parameter {new:true}
This is the code for the route:
// 3. We check if blockid is in this project
Block.findById(req.params.blockid)
.then(block => {
if (!block) {
errors.noblock = "Block not found";
return res.status(404).json(errors);
}
// 4. We found the block, so we modify it
Block.updateOne(
{ _id: req.params.blockid },
{ $set: blockFields }, // data to be updated
{ new: true }, // flag to show the new updated document
(err, block) => {
if (err) {
errors.noblock = "Block not found";
return res.status(404).json(errors);
}
console.log(block);
res.json(block);
}
);
})
.catch(err => console.error(err));
Instead, this is the output I am getting (Mongoose is on debug mode)
Any ideas?
Many thanks
{ new : true } will return the modified document rather than the original. updateOne doesn't have this option. If you need response as updated document use findOneAndUpdate.
Below are the mongoosejs function where you can use { new : true }
findByIdAndUpdate()
findOneAndUpdate()
findOneAndDelete()
findOneAndRemove()
findOneAndReplace()
Thank you #sivasankar for the answer. Here is the updated working version with findOneAndUpdate
And here the expected result:
you should give second param as object of keys value paris of data,
don't pass as $Set : blockfields, just add like below, if it is object containing parameters,
{ $set: blockFields }
Because code should be like this
Block.updateOne(
{ _id: req.params.blockid },
blockFields, // if blockfields is object containing parameters
{ new: true },
(err, block) => {
// lines of code
}
);
For more detail here is link to updateOne function detail updateOne

How to update a specific value in object of MongoDb via Post?

I have a schema with sub objects, i want to be able to update a specific key inside of it. If i update only a specific key - like in the Post example - it's empty all the other keys..
for example :
{
"_id": "32323323",
"names":{
"firstname":"John",
"lastname":"foo",
"workers":{
"position":"manager",
"address":"1 st"
}
}
}
I want to update Only "position" key via Post request , for example :
$.post({
url: 'workers/information/',
data: {
user_id: user_id,
names: {
workers: {
position: some data,
}
}
},
success: function (result) {
alert('Your information updated successfully')
}
});
Here is the update method in NodeJs server :
UserDataController.updateWorkersInformation = function (userID, workersInformation, cb) {
if (userID) {
user.findOneAndUpdate({_id: userID}, workersInformation, function (err, result) {
if (err) return cb(err);
return cb(null, result);
});
}
};
You may want to look into mongoose. It provides a more simple interface than the native client does.
https://www.npmjs.com/package/mongoose
However, as the comment mentioned, you are missing the $set operator. {$set:workersInformation}
If update is called without the $set operator, the entire document will be replaced with your update object.
http://mongodb.github.io/node-mongodb-native/2.2/tutorials/crud/

Nodejs and MongoDB : Unable to return value from a function

var config = require('config.json');
var mongo = require('mongoskin');
var db = mongo.db(config.connectionString, { native_parser: true });
module.exports.getNextSequence = function (name) {
var temp;
db.collection("counters").findAndModify(
{ _id: name }, // query
[], // represents a sort order if multiple matches
{ $inc: { seq: 1 } }, // update statement
{ new: true }, // options - new to return the modified document
function (err, doc) {
temp = doc.value.seq;
console.log(temp); // <-- here the temp is getting printed correctly
}
);
return temp;
}
Using the above code, I am not able to return the value of doc.value.seq. When doing console.log(obj.getNextSequence) it prints undefined.
I want the function to return the value of doc.value.seq.
I'm not familiar with mongoskin so I'm not positive this is correct, but a database query is typically asynchronous, so you need to access the queried value via a callback.
I'm guessing your "getNextSequence" function is returning the "temp" variable before the database query completes (i.e. before the "temp = doc.value.seq" statement).
Try something like this:
module.exports.getNextSequence = function (name, callback) {
var temp;
db.collection("counters").findAndModify(
{ _id: name }, // query
[], // represents a sort order if multiple matches
{ $inc: { seq: 1 } }, // update statement
{ new: true }, // options - new to return the modified document
function (err, doc) {
temp = doc.value.seq;
callback(temp);
}
);
}
Then access "temp" from within the callback passed to getNextSequence.
findAndModify is an asynchronous function. Your console.log line will run after you return temp, which will therefore be undefined. In order to get this to work, you'll want to use an asynchronous approach of your own. There are two available approaches in your situation.
Callbacks:
You're already using a callback, which you provide as the final argument to findAndModify. You could extend this approach and feed this into a callback of your own, as follows:
module.exports.getNextSequence = function (name, callback) {
db.collection("counters").findAndModify(
{ _id: name },
[],
{ $inc: { seq: 1 } },
{ new: true },
function (err, doc) {
if (err) {
return callback(err);
}
callback(null, doc.value.seq);
}
);
}
Of course, this will require you to pass a callback into getNextSequence and follow the callback pattern upstream. You might also want to handle the error from mongoskin and do some handling of your own.
Promises:
If you don't provide a callback to findAndModify, it will return a promise, which you can chain on to, as follows:
module.exports.getNextSequence = function (name) {
return db.collection("counters").findAndModify(
{ _id: name },
[],
{ $inc: { seq: 1 } },
{ new: true }
).then(function (doc) {
return doc.value.seq;
});
}
Again, this will require you to follow the promise pattern upstream. You'll want to read up on promises if you choose this approach, so that you can correctly handle errors, which I have not addressed in the example above.

Can't find a easy way out of multiple async for each node js (sails)

So here's the deal :
I have an array of objects with a child array of objects
askedAdvices
askedAdvice.replayAdvices
I'm looping trough the parent and foreach looping trough the childs and need to populate() two obejcts (I'm using sails)
The child looks like :
askedAdvices = {
replayAdvices : [{
bookEnd : "<ID>",
user : "<ID>"
}]
}
So my goal is to cycle and populate bookEnd and user with two findOne query, but I'm going mad with the callback hell.
Here's the Models code :
AskedAdvices Model
module.exports = {
schema : false,
attributes: {
bookStart : {
model : 'book'
},
replayAdvices : {
collection: 'replybookend'
},
user : {
model : 'user',
required : true
},
text : {
type : "text"
}
}
};
ReplyBookEnd Model
module.exports = {
schema : false,
attributes: {
bookEnd : {
model : 'book'
},
user : {
model : 'user',
required : true
},
text : {
type : "text"
}
}
};
Here's the Method code :
getAskedAdvices : function(req, res) {
var queryAskedAdvices = AskedAdvices.find()
.populate("replayAdvices")
.populate("user")
.populate("bookStart")
queryAskedAdvices.exec(function callBack(err,askedAdvices){
if (!err) {
askedAdvices.forEach(function(askedAdvice, i){
askedAdvice.replayAdvices.forEach(function(reply, i){
async.parallel([
function(callback) {
var queryBook = Book.findOne(reply.bookEnd);
queryBook.exec(function callBack(err,bookEndFound) {
if (!err) {
reply.bookEnd = bookEndFound;
callback();
}
})
},
function(callback) {
var queryUser = User.findOne(reply.user)
queryUser.exec(function callBack(err,userFound){
if (!err) {
reply.user = userFound;
callback();
}
})
}
], function(err){
if (err) return next(err);
return res.json(200, reply);
})
})
})
} else {
return res.json(401, {err:err})
}
})
}
I can use the async library but need suggestions
Thanks folks!
As pointed out in the comments, Waterline doesn't have deep population yet, but you can use async.auto to get out of callback hell. The trick is to gather up the IDs of all the children you need to find, find them with single queries, and then map them back onto the parents. The code would look something like below.
async.auto({
// Get the askedAdvices
getAskedAdvices: function(cb) {
queryAskedAdvices.exec(cb);
},
// Get the IDs of all child records we need to query.
// Note the dependence on the `getAskedAdvices` task
getChildIds: ['getAskedAdvices', function(cb, results) {
// Set up an object to hold all the child IDs
var childIds = {bookEndIds: [], userIds: []};
// Loop through the retrieved askedAdvice objects
_.each(results.getAskedAdvices, function(askedAdvice) {
// Loop through the associated replayAdvice objects
_.each(askedAdvice.replayAdvices, function(replayAdvice) {
childIds.bookEndIds.push(replayAdvice.bookEnd);
childIds.userIds.push(replayAdvice.user);
});
});
// Get rid of duplicate IDs
childIds.bookEndIds = _.uniq(childIds.bookEndIds);
childIds.userIds = _.uniq(childIds.userIds);
// Return the list of IDs
return cb(null, childIds);
}],
// Get the associated book records. Note that this task
// relies on `getChildIds`, but will run in parallel with
// the `getUsers` task
getBookEnds: ['getChildIds', function(cb, results) {
Book.find({id: results.getChildIds.bookEndIds}).exec(cb);
}],
getUsers: ['getChildIds', function(cb, results) {
User.find({id: results.getChildIds.userIds}).exec(cb);
}]
}, function allTasksDone(err, results) {
if (err) {return res.serverError(err);
// Index the books and users by ID for easier lookups
var books = _.indexBy(results.getBookEnds, 'id');
var users = _.indexBy(results.getUsers, 'id');
// Add the book and user objects back into the `replayAdvices` objects
_.each(results.getAskedAdvices, function(askedAdvice) {
_.each(askedAdvice.replayAdvices, function(replayAdvice) {
replayAdvice.bookEnd = books[replayAdvice.bookEnd];
replayAdvice.user = users[replayAdvice.bookEnd];
});
});
});
Note that this is assuming Sails' built-in Lodash and Async instances; if you're using newer versions of those packages the usage of async.auto has changed slightly (the task function arguments are switched so that results comes before cb), and _.indexBy has been renamed to _.keyBy.

How to retrieve query data output from Dynamodb using Node.js

I am having difficulty to understand where is my query stored in which array variable. I know the "var params" contains all the query conditions and "db.query" will perform the actual query action. But how do I get my query result which match my search condition?
At the end, I want to pass my query output parameters (title, postedby) to html or jade for display so I need to understand where all the query data are stored at first place and how to retrieve them. Some sample code to demo this part would be greatly appreciated. Thanks
var params = {
TableName: tablename,
AttributesToGet: [
'title',
'postedBy'
],
Limit: 20,
ScanIndexForward: true,
KeyConditions: { /* required */
key : {
ComparisonOperator: 'EQ', /* required */
AttributeValueList: [
{
'N' : 'some value'
}
/* more items */
},
},
};
db.query(params, function(err, data) {
if (err) {
console.log('show error here');
} else {
console.log('Success');
}
});
I found out that all my query data is stored inside "data" from function(err, data). But i am having problem passing "data" to the following export function which later can be used as a local variable inside my webpage. Then I realized the "data" part is always empty {} inside export function even i am sure the above db.query can return the right result. So i believe somehow the export.list function is executed before the db.query happen so I am not getting any data assigned. How can I overcome this part? Thanks.
exports.list = function(req, res){
res.render('post', {title:'Title', posts : data });
console.log("Result: %j", data);
};
Note: Console.log("Result: ", data) is showing {}
I figured it out. Use the callback function due to the nature of javascript. Thanks.

Resources