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) {
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.