Mongoose: unable to add / push new object to array using $ addToSet or $ push

I use Nodejs, Hapijs and Mongoose.

I have a schematic and model as follows.

var schema = { name: { type: String, required: true }, lectures: {} }; var mongooseSchema = new mongoose.Schema(schema, { collection: "Users" }); mongoose.model("Users", mongooseSchema); 

For some reason I need to keep “lectures” like a mixed type.

When saving / creating a document, I create the attached property lectures.physics.topic [] , where the theme is an array.

Now I'm trying to add / click a new object to "lectures.physics.topic" using $ addToSet or $ push.

 userModel.findByIdAndUpdateAsync(user._id, { $addToSet: { "lectures.physics.topic": { "name": "Fluid Mechanics", "day": "Monday", "faculty": "Nancy Wagner" } } }); 

But the document just does not update. I also tried using $ push. Nothing succeeded. What could be the problem?

I tried using a different approach, using mongoclient, to update db immediately. This works, please find the code below that works

 db.collection("Users").update({ "_id": user._id }, { $addToSet: { "lectures.physics.topic": { "name": "Fluid Mechanics", "day": "Monday", "faculty": "Nancy Wagner" } } }, function(err, result) { if (err) { console.log("Superman!"); console.log(err); return; } console.log(result); }); 

I have to start the mongo client every time the request hits. This is not a feasible solution.

+7
mongodb mongoose
source share
2 answers

I would call it a "mistake." Mongoose is clearly not doing what the magazine can prove, as shown below. But here is the list that calls .findOneAndUpdate() from the native driver with the same update you are trying to do:

 var async = require('async'), mongoose = require('mongoose'), Schema = mongoose.Schema; mongoose.connect('mongodb://localhost/school'); mongoose.set('debug',true); var userSchema = new Schema({ name: { type: String, required: true }, lectures: { type: Schema.Types.Mixed } }); var User = mongoose.model( "User", userSchema ); function logger(data) { return JSON.stringify(data, undefined, 2); } async.waterfall( [ function(callback) { User.remove({},function(err) { callback(err); }); }, function(callback) { console.log("here"); var user = new User({ "name": "bob" }); user.save(function(err,user) { callback(err,user); }); }, function(user,callback) { console.log("Saved: %s", logger(user)); User.collection.findOneAndUpdate( { "_id": user._id }, { "$addToSet": { "lectures.physics.topic": { "name": "Fluid Mechanics", "day": "Monday", "faculty": "Nancy Wagner" } } }, { "returnOriginal": false }, function(err,user) { callback(err,user); } ); } ], function(err,user) { if (err) throw err; console.log("Modified: %s", logger(user)); mongoose.disconnect(); } ); 

This works fine with the result:

 Saved: { "__v": 0, "name": "bob", "_id": "55cda1f5b5ee8b870e2f53bd" } Modified: { "lastErrorObject": { "updatedExisting": true, "n": 1 }, "value": { "_id": "55cda1f5b5ee8b870e2f53bd", "name": "bob", "__v": 0, "lectures": { "physics": { "topic": [ { "name": "Fluid Mechanics", "day": "Monday", "faculty": "Nancy Wagner" } ] } } }, "ok": 1 } 

You should be careful, as the native driver methods are not aware of the connection status, for example mongoose methods. Thus, you need to make sure that the connection was made using the "mongoose" method, which fires earlier or terminates your application in the connection event as follows:

 mongoose.connection.on("connect",function(err) { // start app in here }); 

Regarding the “error”, look at the log output from this list:

 var async = require('async'), mongoose = require('mongoose'), Schema = mongoose.Schema; mongoose.connect('mongodb://localhost/school'); mongoose.set('debug',true); var userSchema = new Schema({ name: { type: String, required: true }, lectures: { type: Schema.Types.Mixed } }); var User = mongoose.model( "User", userSchema ); function logger(data) { return JSON.stringify(data, undefined, 2); } async.waterfall( [ function(callback) { User.remove({},function(err) { callback(err); }); }, function(callback) { console.log("here"); var user = new User({ "name": "bob" }); user.save(function(err,user) { callback(err,user); }); }, function(user,callback) { console.log("Saved: %s", logger(user)); User.findByIdAndUpdate( user._id, { "$addToSet": { "lectures.physics.topic": { "name": "Fluid Mechanics", "day": "Monday", "faculty": "Nancy Wagner" } } }, { "new": true }, function(err,user) { callback(err,user); } ); } ], function(err,user) { if (err) throw err; console.log("Modified: %s", logger(user)); mongoose.disconnect(); } ); 

And logged output with mongoose logging:

 Mongoose: users.remove({}) {} here Mongoose: users.insert({ name: 'bob', _id: ObjectId("55cda2d2462283c90ea3f1ad"), __v: 0 }) Saved: { "__v": 0, "name": "bob", "_id": "55cda2d2462283c90ea3f1ad" } Mongoose: users.findOne({ _id: ObjectId("55cda2d2462283c90ea3f1ad") }) { new: true, fields: undefined } Modified: { "_id": "55cda2d2462283c90ea3f1ad", "name": "bob", "__v": 0 } 

So, in the truth, "What kind of fiction?" style, is there a call there .findOne() ? This is not what was asked. Moreover, in the database, of course, nothing changes, because the wrong call is made. Thus, even { "new": true } is redundant here.

This happens at all levels with types of mixed types.

Personally, I could not embed in "objects" like this, and just make your "Object keys" part of the standard array with additional properties. Both MongoDB and mongoose are much happier with this, and it is much easier to request information with such a structure.

 var async = require('async'), mongoose = require('mongoose'), Schema = mongoose.Schema; mongoose.connect('mongodb://localhost/school'); mongoose.set('debug',true); var lectureSchema = new Schema({ "subject": String, "topic": String, "day": String, "faculty": String }); var userSchema = new Schema({ name: { type: String, required: true }, lectures: [lectureSchema] }); var User = mongoose.model( "User", userSchema ); function logger(data) { return JSON.stringify(data, undefined, 2); } async.waterfall( [ function(callback) { User.remove({},function(err) { callback(err); }); }, function(callback) { console.log("here"); var user = new User({ "name": "bob" }); user.save(function(err,user) { callback(err,user); }); }, function(user,callback) { console.log("Saved: %s", logger(user)); User.findByIdAndUpdate( user._id, { "$addToSet": { "lectures": { "subject": "physics", "topic": "Fluid Mechanics", "day": "Monday", "faculty": "Nancy Wagner" } } }, { "new": true }, function(err,user) { callback(err,user); } ); } ], function(err,user) { if (err) throw err; console.log("Modified: %s", logger(user)); mongoose.disconnect(); } ); 

Output:

 Mongoose: users.remove({}) {} here Mongoose: users.insert({ name: 'bob', _id: ObjectId("55cda4dc40f2a8fb0e5cdf8b"), lectures: [], __v: 0 }) Saved: { "__v": 0, "name": "bob", "_id": "55cda4dc40f2a8fb0e5cdf8b", "lectures": [] } Mongoose: users.findAndModify({ _id: ObjectId("55cda4dc40f2a8fb0e5cdf8b") }) [] { '$addToSet': { lectures: { faculty: 'Nancy Wagner', day: 'Monday', topic: 'Fluid Mechanics', subject: 'physics', _id: ObjectId("55cda4dc40f2a8fb0e5cdf8c") } } } { new: true, upsert: false, remove: false } Modified: { "_id": "55cda4dc40f2a8fb0e5cdf8b", "name": "bob", "__v": 0, "lectures": [ { "faculty": "Nancy Wagner", "day": "Monday", "topic": "Fluid Mechanics", "subject": "physics", "_id": "55cda4dc40f2a8fb0e5cdf8c" } ] } 

So this works fine and you don’t have to dig into your own methods to make it work.

The array properties make it very easy to query and filter, as well as “aggregate” data information, which for all those MongoDB likes the “strict path” to reference all the information. Otherwise, you will differ only in “specific keys”, and they cannot be indexed or really distorted without mentioning all possible “key combinations”.

Properties like this are the best way. And there are no errors.

+2
source share

Mongoose loses its ability to automatically detect and save changes made to mixed types, so you need to “say” that the value of the mixed type has changed by calling the .markModified(path) document method, passing the path to the mixed type that you just changed:

 doc.mixed.type = 'changed'; doc.markModified('mixed.type'); doc.save() // changes to mixed.type are now persisted 

In your case, you can use findById() to make changes by calling addToSet() on the themes array and then calling save() to save the changes:

 userModel.findById(user._id, function (err, doc){ var item = { "name": "Fluid Mechanics", "day": "Monday", "faculty": "Nancy Wagner" }; doc.lectures.physics.topic.addToSet(item); doc.markModified('lectures'); doc.save() // changes to lectures are now persisted }); 
+1
source share

All Articles