Access array elements via mongoose - javascript

With this document :
{
"_id" : 8,
"updatedAt" : ISODate("2016-04-13T05:07:38.905Z"),
"createdAt" : ISODate("2016-04-13T04:15:37.612Z"),
"author" : "test#test.com",
"urgency" : "slow",
"state" : "pending",
"comment" : "",
"requests" : [
{
"value" : 1,
"product" : "Slethoxynal",
"_id" : ObjectId("570dc7e91d15852f1c2ae66a")
},
{
"value" : 1,
"product" : "Thyrploxynénol",
"_id" : ObjectId("570dc7e91d15852f1c2ae66b")
}
],
"__v" : 0
}
I use this javascript function :
function closeRequest(req, res, next) {
Request.findOne({ '_id': req.body.id}, function(err, request){
debug(request);
debug("Length is %s", request.requests.length);
for(var i=0;i<request.requests.length;i++){
debug("We validate the element %s of the request %s", i, req.body.id);
console.log(i);
Material.findOne({'title': request.requests[i].product}).exec(function(err, material){
debug("Inside i is : %i",i);
debug("Product is %s", request.requests[i].product);
debug("Material found is %s", material.title);
material.amount -= request.requests[i].value;
material.save(function(err) {
if(err)
res.send(err);
});
});
}
request.state='accepted';
request.save(function(err) {
if(err)
res.send(err);
});
});
res.redirect('back');
}
And my console prints :
0
1
Inside i is 2
Then the function crashes with
TypeError: Cannot read property 'product' of undefined
At line
debug("Product is %s", request.requests[i].product);
The goal of this function is to validate one request (we put its state from 'pending' to 'accepted'). But as we do that, the stocks in the warehouse needs to be decremented (the 'amount' parameters).
It seems pretty logical that we cannot access the product property since i had a value of 2 and there is no third element in our array.
But why is my iteration variable going out of its range ? And how can i solve that ? The fact that the console prints "Inside" only once makes me wonder if it's not another asynchronous problem.

This is a problem about async execution of your code. In particular the for loop "triggers" multiple async functions and continuous execution. After looping through your loop i===2. As the first Material.findOne callback gets executed requests[2] === undefined (as requests.length === 2) throws your error.
I'd suggest to go like this:
var updateMaterialFns = request.requests.map(function(req, i) {
return function(done) {
debug("We validate the element %s of the request %s", i, req.body.id);
console.log(i);
Material.findOne({'title': req.product}).exec(function(err, material){
if (err) {
done(err)
}
debug("Inside i is : %i",i);
debug("Product is %s", request.requests[i].product);
debug("Material found is %s", material.title);
material.amount -= request.requests[i].value;
material.save(done)
})
}
})
async.parallel(updateMaterialFns, function(err) {
if (err) {
res.send(err)
}
})
async is a node/npm module featuring many functions to deal with the execution and control flow of async programming.

Related

How Do I Update An Object in Mongoose?

I have been facing some problem while updating the data in the db using mongoose, Therefore thanks in advance for the help.
I have been sending some data from a dynamic form that contains different set of fields each time... but when i update the array in the db it changes those fields.
This was my controller function earlier:-
exports.savePageContent = (req, res, next) => {
const id = req.body.dest; // getting page data document id
delete req.body.dest;
var arr = [];
// When we are returning the same page each time after we submit the data for the particuklar section form then how are we supposed to redirect the user to next page once he is done doing all
var fieldKeys = Object.keys(req.body);
for(let i = 0; i < fieldKeys.length; i++)
{
arr.push({fieldRef : fieldKeys[i], fieldValue : req.body[fieldKeys[i]]});
}
pageDataModel.findByIdAndUpdate(id, {pageData : arr}).exec(function(err, result) {
if(err){
console.log(err);
}
else {
console.log("data is inserted");
req.flash('message', "Data Saved Successfully");
}
})
}
I also tried a few updates and then moved to this portion
This is my new controller function :
exports.savePageContent = (req, res, next) => {
const pageid = req.body.dest; // getting page data document id
delete req.body.dest;
var fieldRefData = "";
var fieldValueData = "";
var fieldKeys = Object.keys(req.body);
for(let i = 0; i < fieldKeys.length; i++)
{
fieldRefData = fieldKeys[i];
fieldValueData = req.body[fieldKeys[i]];
try{
pageDataModel.update({id : pageid, "pageData.fieldRef" : fieldRefData}, {$set : {"pageData.$.fieldValue" : fieldValueData }}, {upsert : true}).exec(function(err, result) {
if(err){
console.log(err);
}
else {
console.log("data is inserted", result);
}
});
req.flash('message', "Data Saved Successfully");
}catch(e){
console.log(e);
req.flash('message', "error Occurred updating Data ");
}
}
}
To elaborate the case for better understanding:-
This is the data that comes first time in the body
[
{ fieldRef: 'sec1.1.h1', fieldValue: 'this is the field value 1' },
{ fieldRef: 'sec1.1.p', fieldValue: 'this is the field value 1' },
{ fieldRef: 'sec1.1.a', fieldValue: 'this is the field value 1' },
{ fieldRef: 'sec1.2.h2', fieldValue: 'this is the field value 1' }
]
and This is the data that comes the second time :-
[
{ fieldRef: 'sec2.1.h1', fieldValue: 'this the field value 2' },
{ fieldRef: 'sec2.1.p', fieldValue: 'this the field value 2' },
{ fieldRef: 'sec2.1.a', fieldValue: 'this the field value 2' },
{ fieldRef: 'sec2.2.h1', fieldValue: 'this the field value 2' }
]
when i want both the data's in the db but when i send one the second data the first one gets updated to the second one and vice versa..
this is the db images of the scenario
this is the data in the document on the first operation
this is the data in the document on the second operation
i am not able to keep and update them both... so please help me ...
This is the error that I am getting most of the time which says that the query is wrong
MongoServerError: The positional operator did not find the match needed from the query.
at /home/pushkar/Desktop/NodejsWebApp1/node_modules/mongodb/lib/operations/update.js:80:33
at /home/pushkar/Desktop/NodejsWebApp1/node_modules/mongodb/lib/cmap/connection_pool.js:272:25
at handleOperationResult (/home/pushkar/Desktop/NodejsWebApp1/node_modules/mongodb/lib/sdam/server.js:370:9)
at MessageStream.messageHandler (/home/pushkar/Desktop/NodejsWebApp1/node_modules/mongodb/lib/cmap/connection.js:479:9)
at MessageStream.emit (events.js:400:28)
at processIncomingData (/home/pushkar/Desktop/NodejsWebApp1/node_modules/mongodb/lib/cmap/message_stream.js:108:16)
at MessageStream._write (/home/pushkar/Desktop/NodejsWebApp1/node_modules/mongodb/lib/cmap/message_stream.js:28:9)
at writeOrBuffer (internal/streams/writable.js:358:12)
at MessageStream.Writable.write (internal/streams/writable.js:303:10)
at Socket.ondata (internal/streams/readable.js:731:22) {
index: 0,
code: 2
}
Also if I was unable to make my scenario clear please leave a comment and I will try to explain it more clearly..
thanks In Advance
I hope this should solve the issue :
pageDataModel.updateOne({_id : "61d73f31cb5681b85618995a", "pageData.fieldRef" : "sec2.c" },{"$set" : {"pageData.$.fieldValue" : "Mkc" }}).exec(function(err, result) {
if(err) throw err;
else {
console.log(result);
}
});
$set will replace a new value for the key. If you use the 3rd value then the $set will replace the 3rd value, use $push instead of $set operator.https://docs.mongodb.com/manual/reference/operator/update/push/
Try using _id instead of id.
pageDataModel.update({_id : pageid, "pageData.fieldRef"}) {
Or even pageData._id instead of id.
pageDataModel.update({'pageData._id' : pageid, "pageData.fieldRef"}) {
Furthermore, your id is a string in the query, but an object id in database, so call a helper.
pageDataModel.update({'pageData._id' : mongoose.Types.ObjectId(pageid), "pageData.fieldRef"}) {

How I can get ID of a new record in mongoDB?

There is such a document structure in MongoDB
{
"_id" : ObjectId("58f7d556aa52ce456672a67e"),
"created" : ISODate("2017-04-19T21:23:34.315Z"),
"context_task" : [
{
"task" : "some text ",
"status" : false,
"_id" : ObjectId("58f7d559aa52ce456672a67f")
}
],
"head" : {
"userID" : "58f48037fc894e19a3f7b81b",
"head_task" : "test record "
},
"__v" : 0
}
I add data to the context_task.task using the following query:
task.findOneAndUpdate({"_id": req.body.id_project},
{$push: {'context_task': {'task': req.body.input_task,'status': false}}},{new: true},
function (err, doc) {
if (err) return next(err);
var body = req.body.id_project+","+req.body.input_task;
console.log(doc);
res.status(200).send(body);
});
Tell me, how can I get the context_task._id of a new record after inserting it? In this case, the entire document is returned.
Every mongo document is associated with a default _id, in case not provided it will be ObjectId.
In case you want to use _id value for any other purpose, why don't you have your own _id value, which has to be unique throughout collection. The value can be of any datatype as well as nested also.

Unable to retireve objects from Mongoose-Node

I have a Mongodb database with resume objects like below. I am using a node-express server. And I am querying the mongo database to get objects based on a specific skill. For example: If I query for a skill: jquery, only objects with that skill is returned. The problem is with the get function that is returning objects from the database.
In the highlighted code: If I directly insert the object like:
Resume.find({skills.jQuery : 2},function(err, results){...}
then I get the expected results.
However if I insert it dynamically (skillSet), then it doesnot work. Iit checked the value for skillSet and it give me what I expect('skills.JQuery')
var skillSet = ("'"+'skills.' + req.params.skill +"'");
console.log('skillSet',skillSet) //'skills.jQuery'
Resume.find({skillSet : 2},function(err, results){
Below is the code snippet:
{
"_id" : ObjectId("56031b4353b32084651173fb"),
"uuid" : "acd06792-87c3-4b0e-827a-8bd19d7f9c03",
"creationDate" : ISODate("2015-09-23T21:36:03.728Z"),
"status" : "3",
"name" : "resume_dev",
"__v" : 0,
"skills" : {
"node" : 2,
"react" : 2,
"React" : 3,
"JQUERY" : 2,
"JavaScript" : 15,
"JQuery" : 5,
"Backbone" : 3,
"Node" : 2,
"Angular" : 4,
"Javascript" : 2,
"jQuery" : 17,
"javascript" : 3
}
}
router.get('/skills/:skill', function(req, res){
console.log("req.params.skill",req.params.skill);
var skillSet = ("'"+'skills.' + req.params.skill +"'");
console.log('skillSet',skillSet) //skills.react
Resume.find({skillSet : 2},function(err, results){
console.log('hi');
console.log(skillSet)
console.log(results);
if (err) {
res.status(500).json(err);
}
else {
// console.log("====>",results);
res.status(200).json(results);
// res.render('status',{Resume: JSON.stringify(results)});
}
});
});
When I understand your question correctly, you can do something like this:
var query = {};
query['skills.' + req.params.skill] = 2;
Resume.find(query,function(err, results){
// Do something with the callback
};
EDIT:
If you want to get all numbers greater or equal to 1, you will need $gte. Here are the docs. This is the updated query:
var query = {};
query['skills.' + req.params.skill] = {$gte: 1};
Resume.find(query,function(err, results){
// Do something with the callback
};

Mongoose - Loop an array of embedded docs to .push new value to a field en masse?

I have a doc with an array of embedded docs ("comments"), and an example that looks like this:
{
"_id" : ObjectId("539e9213209e743d107e7202"),
"article" : "article1",
"comments" : [
{
"comment" : "comment1",
"created" : ISODate("2014-06-16T06:43:38Z"),
"_id" : ObjectId("539e921a209e743d107e7203"),
"read" : {
"marked" : false
},
"timesent" : {
"datetime" : "Mon Jun 16 2014 02:43:38 GMT-0400 (EDT)",
"hour" : 2,
"minute" : "43",
"second" : 38,
"am" : true,
"month" : 5,
"day" : 16,
"year" : 2014
}
}
]
}
For each comment in the comments array, is there a way to batch update the field "read" : {"marked" : true}?
Am working with node.js, and have something like this in mind (the questionable portion begins with
if (req.body.readComment) {..
// update the article with this id (accessed by PUT at
// http://localhost:4200/api/v1/articles/:article_id)
.put(function(req, res) {
Article.findById(req.params.article_id, function(err, article) {
if (err)
res.send(err);
if (req.body.comment) {
article.comments.push({
comment : req.body.comment,
timesent :
{
datetime : req.body.datetimeNow,
hour : req.body.hourNow,
minute : req.body.minuteNow,
second : req.body.secondNow,
am : req.body.amNow,
month : req.body.monthNow,
day : req.body.dayNow,
year : req.body.yearNow
},
read :
{
marked : req.body.readComment,
datetime : req.body.readCommentDatetime
},
created : req.body.datetimeNow
});
} // if newComment
if (req.body.readComment) {
var comments = // some sort of .find ?
var embeddedDoc;
for (var i=0, length=comments.length; i < length; i++){
embeddedDoc = comments[i];
embeddedDoc_id = // something to find the embedded doc_id ?
console.log(i);
article.comments.push({ // maybe push to the embedded doc_id
read :
{
marked : req.body.readComment,
datetime : req.body.readCommentDatetime
}
});
};
} // if readComment == true (from ajax .put)
// save the article, and check for errors
article.save(function(err) {
if (err)
res.send(err);
res.json({ message: 'Update "' + req.params.article_id });
});
});
})
Well as each comment would need to be identified within the array, the term "Bulk" doesn't really apply as they would be inherently separate. As for being able to just say "Update all of these 'comments' and mark them as true", that is not directly supported.
But on the other hand you can streamline this with Bulk update operations. So for a "list" of "comment" _id values you could do this:
var bulk = collection.initializeOrderedBulkOp();
comments.forEach(function(commentId) {
bulk.find({ "comments._id": commentId }).updateOne({
"$set": { "comments.$.read.marked": false }
});
counter++;
if ( counter % 500 == 0 ) {
bulk.execute(function(err,result) {
// do something with the result
bulk = collection.initializeOrderedBulkOp();
counter = 0;
});
}
});
// Catch any under or over the 500's
if ( counter > 0 )
bulk.execute(function(err,result) {
// do something with the result here
});
That at least avoids you sending an update "over the wire" to the server instance for every single "comment" _id you send into the API. By batching the results this results in less traffic and less time waiting for the response in the callback.
You can likely better this example with "async" so even looping the input list is a non-blocking operation. The batch sizes can vary, but this is just a safe example to stay under the 16MB BSON hard limit, as the whole "request" is equal to one BSON document.

Mongodb/Mongoose: Storing Subdocument of a Variable to a Variable

I am trying to create a shotgun npm command for deleting all topics ever created by a user
This is a sample user entry (users are stored in a collection called 'users'):
{
"__v" : 0,
"_id" : ObjectId("536c4c8fafec055606f01840"), //the id I want to store to a variable and use the variable to find all topics with that id in the 'creator' document
"joinDate" : ISODate("2014-05-09T18:13:28.079Z"),
"lastActiveDate" : ISODate("2014-05-09T18:13:48.918Z"),
"lastSocketId" : null,
"password" : "Johndoe6",
"roles" : [],
"username" : "johndoe6"
}
This is a sample topic entry (topics are stored in a collection called 'topics'):
{
"__v" : 4,
"_id" : 202, //unreliable as these change all the time
"body" : "example topic text",
"commentCount" : 0,
"creator" : ObjectId("536c4c8fafec055606f01840"), //this is the id I want to be found with my variable from a found user
"date" : ISODate("2014-05-14T13:58:13.668Z"),
"editedBy" : ObjectId("536f0392ca01fb0e39364c02"),
"editedDate" : ISODate("2014-05-14T13:59:27.607Z"),
"lastCommentDate" : ISODate("2014-05-14T13:58:13.670Z"),
"tags" : [],
"title" : "test",
"views" : [],
}
Here is a snippet of my code:
exports.invoke = function (shell, options) {
if (!options.confirm) {
shell.warn("Are you sure you want to delete all topics made by {{0}}? (Y/N)".format(options.username));
return shell.setPrompt('confirm', 'purgeTopic', options, 'Are you sure?');
}
shell.db.User.findOne({ username: options.username }, function (err, user) {
var userid = something //this is where I want it to pluck out the user's ID and store it for later
if (err) return shell.error(err);
if (!user) return shell.error("No user {{0}} exists.".format(options.username));
//otherwise
shell.db.Topic.where('creator').equals(userid).remove(function (err) {
As you can see, options.username is a variable that has been typed in by the user doing the command. On the last line I want it to remove topics that have a subdocument 'creator' with the id of the 'user'. How can this be accomplished?
It would simply be:
var userid = user._id;
But you'd want to put that after your if (!user) check in case user is null.

Categories

Resources