Updating Nested subdocument using id mongoose - node.js

I need to edit my prescriptions quantity .Here is my sample code.
collection.update(
{ "_id" : 1,"medications._id" : 23,"medications.prescriptions._id":77 },
{ $set : { "medications.$0.prescriptions.$1.quantity" : 30 } },
false,
true
)
How to edit this. It does not shown any error .

Instead you can follow a more easy to understand way.
var updateData = {
medications.prescriptions.quantity: 0
}
collection.update({ "_id" : 1,"medications._id" : 23,"medications.prescriptions._id":77 }, updateData, function(err, response){
if(err){
res.json(err);
}
//your success handler
});

you can use only one positional operator
And see this documentation:
https://docs.mongodb.com/manual/reference/operator/update/positional/

I have faced the same issue for the as array inside Array update require much performance impact. So, mongo db doest not support it. Redesign your database as shown in the given link below.
https://pythonolyk.wordpress.com/2016/01/17/mongodb-update-nested-array-using-positional-operator/

Related

How do I update data in mongo

So I am currently working on a project with mongodb and nodejs and I was wondering, how can you update data in mongodb via nodejs? My problem is that I want to keep the old data and add new. For example, here is the data currently in my mongodb
{
"_id" : ObjectId("5a1c0c1c3b147ec2e31cceb3"),
"event_id" : "1",
"event_medium" : "null",
"event_tags" : ["#JustTesting"]
}
So I want to add new data to the event_tags array and still keep the old data.
So for example the end result would be this:
{
"_id" : ObjectId("5a1c0c1c3b147ec2e31cceb3"),
"event_id" : "1",
"event_medium" : "null",
"event_tags" : ["#JustTesting", "#Test", "#Something"]
}
You should use the update function of MongoDB for that. MongoDB knows different update operators, in your case you may use $push or $pushAll (the second is deprecated):
update one after the other with $push
YourCollection.update({ _id: 'xxx' }, { $push: { event_tags: '#Test' } });
YourCollection.update({ _id: 'xxx' }, { $push: { event_tags: '#Something' } });
or both at once with $pushAll (deprecated now)
YourCollection.update({ _id: 'xxx' }, { $pushAll: { event_tags: ['#Test', '#Something'] } });
To interact with MongoDB form your NodeJS app, I would use a library like this one.
Your starting point is the Update function in CRUD (Create,Read, Update, Delete) operations in Mongodb.
Your node program should have among others the update function where you set the _id field you want to update and load the content fields to be update in 'data' for example as below:
myModel.prototype.update = function (_id, data, callback) {
const query = { _id: this.mongo.ObjectId(_id) };
debug(' update:' + JSON.stringify(query));
this.mongo.collection('mycollection').update(query, data, callback);
};
This piece of code should be put in your Model, if you use MVC pattern.
There is a lot to go.
Honestly I recommend a more deep tutorial like parts 3 and 4 of this one for nodejs and Mongoose (mongo db driver):
MDN tutorial for mongo/node/express
I assume you are using mongoose..
eventModel.findOne({event_id:1 },
function(err, eventObj){
if(err){
//handle error
} else {
if(eventObj === null) {
//event doesnot exist
}
var tagList = eventObj.event_tags;
tagList.push('new_tag1');
tagList.push('new_tag2');
eventObj.event_tags = tagList;
eventObj.save(function(err){
if(err){
//handle error
} else {
//success
}
})

MongoDb Node.js Driver - findOneAndUpdate() behavior without a field given

I noticed a strange behavior with the mongodb node.js driver findOneAndUpate()...
I mistakenly gave it just a objectId string....thinking it would default to searching by _id field of a document.... so, when I used
User.prototype.updatePetArray = function(user, petElement) {
return this.collection.findOneAndUpdate(user,
{ $push: { pets: petElement } },
{ returnOriginal: false,
maxTimeMS: QUERY_TIME});
}
it pulled up and modified this document, which does not have this number at all:
{ "_id" : ObjectId("56d4e2a381c9c28b3056f792"), "username" : "bob123", "location" : "AT", ...}
Why did it modify this document when 56d4d35881c9c28b3056f78a is not in the document?
After I test it following your code with one non-exist ObjectID,
var col = db.collection('users');
col.findOneAndUpdate('56cd129222222', {fname: 'Daved'}, function(err, r) {
if (err)
console.log(err);
else
console.log(r);
db.close();
});
As a result the first document in the collection was changed .
Per this doc
findOneAndUpdate(filter, update, options, callback)
filter: Specify an empty document { } to update the first document returned in the collection.
So I think this non-exist ObjectId is consider to the same behavior with {}
After reviewing the source code of findAndModify, eventually, the
// Execute the command
self.s.db.command(queryObject
is invoked, and the queryObject is
var queryObject = {
'findandmodify': self.s.name
, 'query': query
};
So I test runCommand in mongo shell with non-exist ObjectId as below, as result, the first document is returned.
> db.users.runCommand({findandmodify: 'users', query: '123ddae344', update: true
})
{
"lastErrorObject" : {
"updatedExisting" : true,
"n" : 1
},
"value" : {
"_id" : ObjectId("56c687275d81735019263d1f"),
"fname" : "Daved"
},
"ok" : 1
}
The docs keep saying the filter parameter should be an object.
The wrong behavior is likely to some side effect of mis-interpreting the value, being a string not an object (and maybe a truthy value, non-empty string).

What is the equivalent of Model.visits.$inc() (Mongoose 2.7.x) in the current (3.8.x) version?

Mongooose ODB docs from 2.7.x say the following is valid:
Model.findOne({ name: 'borne' }, function (err, doc){
doc.name = 'jason borne';
doc.visits.$inc();
doc.save();
});
That was taken from mongoosejs.com (very bottom of page) for version 2.7.x. I'm learning Node.js right now and using Mongoose and the above doesn't seem to work.
I have a schema where I have a field with mongoose set to:
visits : Number
and on an object by default I set the visits = 0;
When I try something like:
Model.findOne({'email' : req.query.email}, function(error, mdl) {
mdl.visits.$inc();
mdl.save();
}
I get the following error in console:
TypeError: Object 0 has no method '$inc' ?
Any suggestions?
Thanks.
Those number extensions were causing problems and have been removed in Mongoose 3.x.
This is better handled by an atomic update with the $inc operator anyway:
Model.update(
{email: req.query.email},
{$inc: {visits: 1}},
function(error, numAffected) { ... }
);

MongooseJS - How to find the element with the maximum value?

I am using MongoDB , MongooseJS and Nodejs.
I have a Collection ( called Member ) with the following Fields -
Country_id , Member_id , Name, Score
I want to write a query which returns the Member with the max Score where Country id = 10
I couldnt find suitable documentation for this in MongooseJS.
I found this at StackOVerflow ( this is MongoDB code )
Model.findOne({ field1 : 1 }).sort(last_mod, 1).run( function(err, doc) {
var max = doc.last_mod;
});
But how do I translate the same to MongooseJS ?
Member
.findOne({ country_id: 10 })
.sort('-score') // give me the max
.exec(function (err, member) {
// your callback code
});
Check the mongoose docs for querying, they are pretty good.
If you dont't want to write the same code again you could also add a static method to your Member model like this:
memberSchema.statics.findMax = function (callback) {
this.findOne({ country_id: 10 }) // 'this' now refers to the Member class
.sort('-score')
.exec(callback);
}
And call it later via Member.findMax(callback)
You do not need Mongoose documentation to do this.
Plain MongoDb will do the job.
Assume you have your Member collection:
{ "_id" : ObjectId("527619d6e964aa5d2bdca6e2"), "country_id" : 10, "name" : "tes2t", "score" : 15 }
{ "_id" : ObjectId("527619cfe964aa5d2bdca6e1"), "country_id" : 10, "name" : "test", "score" : 5 }
{ "_id" : ObjectId("527619e1e964aa5d2bdca6e3"), "country_id" : 10, "name" : "tes5t", "score" : -6 }
{ "_id" : ObjectId("527619e1e964aa5d2bdcd6f3"), "country_id" : 8, "name" : "tes5t", "score" : 24 }
The following query will return you a cursor to the document, you are looking for:
db.Member.find({country_id : 10}).sort({score : -1}).limit(1)
It might be faster to use find() than findOne().
With find().limit(1) an array of the one document is returned. To get the document object, you have to do get the first array element, maxResult[0].
Making Salvador's answer more complete ...
var findQuery = db.Member.find({country_id : 10}).sort({score : -1}).limit(1);
findQuery.exec(function(err, maxResult){
if (err) {return err;}
// do stuff with maxResult[0]
});
This is quick and easy using the Mongoose Query Helpers.
The general form for this could be:
<Your_Model>.find()
.sort("-field_to_sort_by")
.limit(1)
.exec( (error,data) => someFunc(error,data) {...} );
tldr:
This will give you an array of a single item with the highest value in 'field_to_sort_by'. Don't forget to access it as data[0], like I did for an hour.
Long-winded: Step-by-step on what that string of functions is doing...
Your_Model.find() starts the query, no args needed.
.sort("-field_to_sort_by") sorts the everything in descending order. That minus-sign in front of the field name specifies to sort in descending order, you can discard it to sort in ascending order and thus get the document with the minimum value.
.limit(1) tells the database to only return the first document, because we only want the top-ranked document.
.exec( (error,data) => someFunc(error,data) {...} ) finally passes any error and an array containing your document to into your function. You'll find your document in data[0]
You can also use the $max operator:
// find the max age of all users
Users.aggregate(
{ $group: { _id: null, maxAge: { $max: '$age' }}}
, { $project: { _id: 0, maxAge: 1 }}
, function (err, res) {
if (err) return handleError(err);
console.log(res); // [ { maxAge: 98 } ]
});

How to compose a complex query using resultset from another query in Mongodb?

I am writing an application using Mongo (using Mongo native driver), Node and Express.
I have Students, Courses and Professors documents in mongo.
I want to retrieve a list of all 'Professor' documents whose courses a student is currently taking or has taken in the past.
Students: {courseid, ....}
Course: {professors, ....}
Professors: {....}
This is what I intend on doing:
I first issue a query to retrieve all the course ids for a student.
Then I have to compose and issue another query to get the professor id for all those courses.
And then finally I have to get all the "professor" documents associated with the professor ids.
Step 1 is not a problem and now I have all the course ids. But I am not sure how to do step2. Step2 and 3 are similar, once I figure out step 2, step3 will be easy.
Basically I want to issue one query in step2 to retrieve all the professor ids. I don't want to issue 10 separate queries for 10 course ids.
Here is what I have:
function getProfsByStudent(req, res, next)
{
db.collection('students', function(err, stuColl)
{
stuId = new ObjectID.createFromHexString(req.params.stuId);
stuColl.find({_id : userId}, { 'current_course_id' : 1 , 'past_courses.course_id' : 1 , _id : 0 })
{
db.collection('courses', function(err, courseColl)
{
courseColl.find({$or : []}) // THIS IS WHERE I AM STUCK
});
res.send(posts);
});
});
}
Update
Question updated based on the answer.
So, this is the JSON I end up with after the stuColl.find call:
[{"current_course_id":"4f7fa4c37c06191111000005","past_courses":[{"course_id":"4f7fa4c37c06191111000003"},{"course_id":"4f7fa4c37c06191111000002"}]}]
Now I want to use the above to do another find to get all professor IDs. But all I get is a null result. I think I am very close. What am I doing wrong?
stuColl.find({_id : userId}, { 'current_course_id' : 1 , 'past_courses.course_id' : 1 , _id : 0 }).toArray(function(err, courseIdsArray)
{
db.collection('courses', function(err, courseColl)
{
courseColl.find({$or : [ {_id : 'courseIdsArray.current_courses_id' }, {_id : {$in : courseIdsArray.past_courses}} ]}, {'professor_ids' : 1}).toArray(function(err, professorIdsArray)
{
res.send(professorIdsArray);
});
});
});
I think that instead of $or you should be using the $in operator
http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24in
and:
stuColl.find....
should be returning its result toArray to be used in the $in operator.
Update
I think this is what you're looking for:
db.collection("Students").find({_id : "8397c60d-bd7c-4f94-a0f9-f9db2f14e8ea"}, {CurrentCourses:1, PastCourses : 1, _id : 0}).toArray(function(err, allCourses){
if(err){
//error handling
}
else{
var courses = allCourses[0].CurrentCourses.concat(allCourses[0].PastCourses);
db.collection("Courses").find({ _id : { $in: courses}}, {"Professors" : 1, _id : 0}).toArray(function(err, professors){
if(err){
//error handling
}
var allProfs = [];
for(var i = 0; i < professors.length; i++){
allProfs = allProfs.concat(professors[i].Professors);
}
db.collection("Professors").find({ _id : { $in: allProfs }}).toArray(function(err, results){
console.log(results);
});
});
}
});
It goes through the students collection and finds the student and then through all his/her courses to finally load all the teachers.

Resources