UNPKG

mongoose

Version:

Mongoose MongoDB ODM

721 lines (611 loc) 22.2 kB
/** * Test dependencies. */ var start = require('./common') , assert = require('assert') , mongoose = start.mongoose , random = require('../lib/utils').random , Query = require('../lib/query') , Schema = mongoose.Schema , SchemaType = mongoose.SchemaType , CastError = SchemaType.CastError , ValidatorError = SchemaType.ValidatorError , ValidationError = mongoose.Document.ValidationError , ObjectId = Schema.ObjectId , DocumentObjectId = mongoose.Types.ObjectId , DocumentArray = mongoose.Types.DocumentArray , EmbeddedDocument = mongoose.Types.Embedded , MongooseArray = mongoose.Types.Array , MongooseError = mongoose.Error; /** * Setup. */ var Comments = new Schema; Comments.add({ title : String , date : Date , body : String , comments : [Comments] }); var BlogPost = new Schema({ title : String , author : String , slug : String , date : Date , meta : { date : Date , visitors : Number } , published : Boolean , mixed : {} , numbers : [Number] , owners : [ObjectId] , comments : [Comments] }, { strict: false }); BlogPost.virtual('titleWithAuthor') .get(function () { return this.get('title') + ' by ' + this.get('author'); }) .set(function (val) { var split = val.split(' by '); this.set('title', split[0]); this.set('author', split[1]); }); BlogPost.method('cool', function(){ return this; }); BlogPost.static('woot', function(){ return this; }); mongoose.model('BlogPostForUpdates', BlogPost); var collection = 'blogposts_' + random(); var strictSchema = new Schema({ name: String, x: { nested: String }}); strictSchema.virtual('foo').get(function () { return 'i am a virtual FOO!' }); mongoose.model('UpdateStrictSchema', strictSchema); describe('model: update:', function(){ var post , title = 'Tobi ' + random() , author = 'Brian ' + random() , newTitle = 'Woot ' + random() , id0 , id1 before(function(done){ var db = start() , BlogPost = db.model('BlogPostForUpdates', collection) id0 = new DocumentObjectId id1 = new DocumentObjectId post = new BlogPost; post.set('title', title); post.author = author; post.meta.visitors = 0; post.date = new Date; post.published = true; post.mixed = { x: 'ex' }; post.numbers = [4,5,6,7]; post.owners = [id0, id1]; post.comments = [{ body: 'been there' }, { body: 'done that' }]; post.save(function (err) { assert.ifError(err); done(); }); }); it('works', function(done){ var db = start() , BlogPost = db.model('BlogPostForUpdates', collection) BlogPost.findById(post._id, function (err, cf) { assert.ifError(err); assert.equal(title, cf.title); assert.equal(cf.author,author); assert.equal(cf.meta.visitors.valueOf(),0); assert.equal(cf.date.toString(), post.date.toString()); assert.equal(true, cf.published); assert.equal('ex', cf.mixed.x); assert.deepEqual(cf.numbers.toObject(), [4,5,6,7]); assert.equal(cf.owners.length, 2) assert.equal(cf.owners[0].toString(), id0.toString()); assert.equal(cf.owners[1].toString(), id1.toString()); assert.equal(cf.comments.length, 2); assert.equal(cf.comments[0].body, 'been there'); assert.equal(cf.comments[1].body, 'done that'); assert.ok(cf.comments[0]._id); assert.ok(cf.comments[1]._id); assert.ok(cf.comments[0]._id instanceof DocumentObjectId) assert.ok(cf.comments[1]._id instanceof DocumentObjectId); var update = { title: newTitle // becomes $set , $inc: { 'meta.visitors': 2 } , $set: { date: new Date } , published: false // becomes $set , 'mixed': { x: 'ECKS', y: 'why' } // $set , $pullAll: { 'numbers': [4, 6] } , $pull: { 'owners': id0 } , 'comments.1.body': 8 // $set } BlogPost.update({ title: title }, update, function (err) { assert.ifError(err); BlogPost.findById(post._id, function (err, up) { assert.ifError(err); assert.equal(up.title,newTitle); assert.equal(up.author,author); assert.equal(up.meta.visitors.valueOf(), 2); assert.equal(up.date.toString(),update.$set.date.toString()); assert.equal(up.published, false); assert.equal(up.mixed.x, 'ECKS'); assert.equal(up.mixed.y,'why'); assert.deepEqual(up.numbers.toObject(), [5,7]); assert.equal(up.owners.length, 1); assert.equal(up.owners[0].toString(), id1.toString()); assert.equal(up.comments[0].body,'been there'); assert.equal(up.comments[1].body,'8'); assert.ok(up.comments[0]._id); assert.ok(up.comments[1]._id); assert.ok(up.comments[0]._id instanceof DocumentObjectId) assert.ok(up.comments[1]._id instanceof DocumentObjectId); var update2 = { 'comments.body': 'fail' } BlogPost.update({ _id: post._id }, update2, function (err) { assert.ok(err); assert.ok(/^can't append to array using string field name \[body\]/.test(err.message)); BlogPost.findById(post, function (err, p) { assert.ifError(err); var update3 = { $pull: 'fail' } BlogPost.update({ _id: post._id }, update3, function (err) { assert.ok(err); assert.ok(/Invalid atomic update value/.test(err.message)); var update4 = { $inc: { idontexist: 1 } } // should not overwrite doc when no valid paths are submitted BlogPost.update({ _id: post._id }, update4, function (err) { assert.ifError(err); BlogPost.findById(post._id, function (err, up) { db.close(); assert.ifError(err); assert.equal(up.title,newTitle); assert.equal(up.author,author); assert.equal(up.meta.visitors.valueOf(),2); assert.equal(up.date.toString(),update.$set.date.toString()); assert.equal(up.published,false); assert.equal(up.mixed.x,'ECKS'); assert.equal(up.mixed.y,'why'); assert.deepEqual(up.numbers.toObject(),[5,7]); assert.equal(up.owners.length, 1); assert.equal(up.owners[0].toString(), id1.toString()); assert.equal(up.comments[0].body, 'been there'); assert.equal(up.comments[1].body, '8'); // non-schema data was still stored in mongodb assert.strictEqual(1, up._doc.idontexist); done(); }); }); }); }); }); }); }); }); }); it('casts doc arrays', function(done){ var db = start() , BlogPost = db.model('BlogPostForUpdates', collection) var update = { comments: [{ body: 'worked great' }] , $set: {'numbers.1': 100} , $inc: { idontexist: 1 } } BlogPost.update({ _id: post._id }, update, function (err) { assert.ifError(err); // get the underlying doc BlogPost.collection.findOne({ _id: post._id }, function (err, doc) { db.close(); assert.ifError(err); var up = new BlogPost; up.init(doc); assert.equal(up.comments.length, 1); assert.equal(up.comments[0].body, 'worked great'); assert.strictEqual(true, !! doc.comments[0]._id); assert.equal(2,up.meta.visitors.valueOf()); assert.equal(up.mixed.x,'ECKS'); assert.deepEqual(up.numbers.toObject(),[5,100]); assert.strictEqual(up.numbers[1].valueOf(),100); assert.equal(2, doc.idontexist); assert.equal(100, doc.numbers[1]); done(); }); }); }) it('handles $pushAll array of docs', function(done){ var db = start() , BlogPost = db.model('BlogPostForUpdates', collection) var update = { $pushAll: { comments: [{ body: 'i am number 2' }, { body: 'i am number 3' }] } } BlogPost.update({ _id: post._id }, update, function (err) { assert.ifError(err); BlogPost.findById(post, function (err, ret) { db.close(); assert.ifError(err); assert.equal(3, ret.comments.length); assert.equal(ret.comments[1].body,'i am number 2'); assert.strictEqual(true, !! ret.comments[1]._id); assert.ok(ret.comments[1]._id instanceof DocumentObjectId) assert.equal(ret.comments[2].body,'i am number 3'); assert.strictEqual(true, !! ret.comments[2]._id); assert.ok(ret.comments[2]._id instanceof DocumentObjectId) done(); }) }); }) it('handles $pull of object literal array of docs (gh-542)', function(done){ var db = start() , BlogPost = db.model('BlogPostForUpdates', collection) var update = { $pull: { comments: { body: 'i am number 2' } } } BlogPost.update({ _id: post._id }, update, function (err) { assert.ifError(err); BlogPost.findById(post, function (err, ret) { db.close(); assert.ifError(err); assert.equal(2, ret.comments.length); assert.equal(ret.comments[0].body,'worked great'); assert.ok(ret.comments[0]._id instanceof DocumentObjectId) assert.equal(ret.comments[1].body,'i am number 3'); assert.ok(ret.comments[1]._id instanceof DocumentObjectId) done(); }) }); }); it('handles weird casting (gh-479)', function(done){ var db = start() , BlogPost = db.model('BlogPostForUpdates', collection) function a () {}; a.prototype.toString = function () { return 'MongoDB++' } var crazy = new a; var update = { $addToSet: { 'comments.$.comments': { body: 'The Ring Of Power' } } , $set: { 'comments.$.title': crazy } } BlogPost.update({ _id: post._id, 'comments.body': 'worked great' }, update, function (err) { assert.ifError(err); BlogPost.findById(post, function (err, ret) { db.close(); assert.ifError(err); assert.equal(2, ret.comments.length); assert.equal(ret.comments[0].body, 'worked great'); assert.equal(ret.comments[0].title,'MongoDB++'); assert.strictEqual(true, !! ret.comments[0].comments); assert.equal(ret.comments[0].comments.length, 1); assert.strictEqual(ret.comments[0].comments[0].body, 'The Ring Of Power'); assert.ok(ret.comments[0]._id instanceof DocumentObjectId); assert.ok(ret.comments[0].comments[0]._id instanceof DocumentObjectId); assert.equal(ret.comments[1].body,'i am number 3'); assert.strictEqual(undefined, ret.comments[1].title); assert.ok(ret.comments[1]._id instanceof DocumentObjectId) done(); }) }); }) var last; it('handles date casting (gh-479)', function(done){ var db = start() , BlogPost = db.model('BlogPostForUpdates', collection) var update = { $inc: { 'comments.$.newprop': '1' } , $set: { date: (new Date).getTime() } // check for single val casting } BlogPost.update({ _id: post._id, 'comments.body': 'worked great' }, update, function (err) { assert.ifError(err); BlogPost.findById(post, function (err, ret) { db.close(); assert.ifError(err); assert.equal(1, ret._doc.comments[0]._doc.newprop); assert.strictEqual(undefined, ret._doc.comments[1]._doc.newprop); assert.ok(ret.date instanceof Date); assert.equal(ret.date.toString(), update.$set.date.toString()); last = ret; done(); }) }); }); it('handles $addToSet (gh-545)', function(done){ var db = start() , BlogPost = db.model('BlogPostForUpdates', collection) var owner = last.owners[0]; var update = { $addToSet: { 'owners': owner } } BlogPost.update({ _id: post._id }, update, function (err) { assert.ifError(err); BlogPost.findById(post, function (err, ret) { db.close(); assert.ifError(err); assert.equal(1, ret.owners.length); assert.equal(ret.owners[0].toString(), owner.toString()); last = ret; done(); }) }); }); it('handles $addToSet with $each (gh-545)', function(done){ var db = start() , BlogPost = db.model('BlogPostForUpdates', collection) var owner = last.owners[0] , newowner = new DocumentObjectId var update = { $addToSet: { 'owners': { $each: [owner, newowner] }} } BlogPost.update({ _id: post._id }, update, function (err) { assert.ifError(err); BlogPost.findById(post, function (err, ret) { db.close(); assert.ifError(err); assert.equal(2, ret.owners.length); assert.equal(ret.owners[0].toString(), owner.toString()); assert.equal(ret.owners[1].toString(), newowner.toString()); last = newowner; done(); }) }); }); it('handles $pop and $unset (gh-574)', function(done){ var db = start() , BlogPost = db.model('BlogPostForUpdates', collection) var update = { $pop: { 'owners': -1 } , $unset: { title: 1 } } BlogPost.update({ _id: post._id }, update, function (err) { assert.ifError(err); BlogPost.findById(post, function (err, ret) { db.close(); assert.ifError(err); assert.equal(1, ret.owners.length); assert.equal(ret.owners[0].toString(), last.toString()); assert.strictEqual(undefined, ret.title); done(); }); }); }); it('works with nested positional notation', function(done){ var db = start() , BlogPost = db.model('BlogPostForUpdates', collection) var update = { $set: { 'comments.0.comments.0.date': '11/5/2011' , 'comments.1.body': 9000 } } BlogPost.update({ _id: post._id }, update, function (err) { assert.ifError(err); BlogPost.findById(post, function (err, ret) { db.close(); assert.ifError(err); assert.equal(2, ret.comments.length, 2); assert.equal(ret.comments[0].body, 'worked great'); assert.equal(ret.comments[1].body, '9000'); assert.equal(ret.comments[0].comments[0].date.toString(), new Date('11/5/2011').toString()) assert.equal(ret.comments[1].comments.length, 0); done(); }) }); }); it('handles $pull with obj literal (gh-542)', function(done){ var db = start() , BlogPost = db.model('BlogPostForUpdates', collection) BlogPost.findById(post, function (err, last) { assert.ifError(err); var update = { $pull: { comments: { _id: last.comments[0].id } } } BlogPost.update({ _id: post._id }, update, function (err) { assert.ifError(err); BlogPost.findById(post, function (err, ret) { db.close(); assert.ifError(err); assert.equal(1, ret.comments.length); assert.equal(ret.comments[0].body, '9000'); done(); }) }); }); }); it('handles $pull of obj literal and nested $in', function(done){ var db = start() , BlogPost = db.model('BlogPostForUpdates', collection) BlogPost.findById(post, function (err, last) { assert.ifError(err); var update = { $pull: { comments: { body: { $in: [last.comments[0].body] }} } } BlogPost.update({ _id: post._id }, update, function (err) { assert.ifError(err); BlogPost.findById(post, function (err, ret) { db.close(); assert.ifError(err); assert.equal(0, ret.comments.length); last = ret; done(); }) }); }); }); it('handles $pull and nested $nin', function(done){ var db = start() , BlogPost = db.model('BlogPostForUpdates', collection) BlogPost.findById(post, function (err, last) { assert.ifError(err); last.comments.push({body: 'hi'}, {body:'there'}); last.save(function (err) { assert.ifError(err); BlogPost.findById(post, function (err, ret) { assert.ifError(err); assert.equal(2, ret.comments.length); var update = { $pull: { comments: { body: { $nin: ['there'] }} } } BlogPost.update({ _id: ret._id }, update, function (err) { assert.ifError(err); BlogPost.findById(post, function (err, ret) { db.close(); assert.ifError(err); assert.equal(1, ret.comments.length); done(); }) }); }) }); }) }) it('updates numbers atomically', function(done){ var db = start() , BlogPost = db.model('BlogPostForUpdates', collection) , totalDocs = 4 , saveQueue = []; var post = new BlogPost; post.set('meta.visitors', 5); post.save(function(err){ assert.ifError(err); for (var i = 0; i < 4; ++i) { BlogPost .update({ _id: post._id }, { $inc: { 'meta.visitors': 1 }}, function (err) { assert.ifError(err); --totalDocs || complete(); }); } function complete () { BlogPost.findOne({ _id: post.get('_id') }, function (err, doc) { db.close(); assert.ifError(err); assert.equal(9, doc.get('meta.visitors')); done(); }); }; }); }); describe('honors strict schemas', function(){ it('(gh-699)', function(){ var db = start(); var S = db.model('UpdateStrictSchema'); db.close(); var doc = S.find()._castUpdate({ ignore: true }); assert.equal(false, doc); var doc = S.find()._castUpdate({ $unset: {x: 1}}); assert.equal(1, Object.keys(doc.$unset).length); }); it('works', function(done){ var db = start(); var S = db.model('UpdateStrictSchema'); var s = new S({ name: 'orange crush' }); s.save(function (err) { assert.ifError(err); S.update({ _id: s._id }, { ignore: true }, function (err, affected) { assert.ifError(err); assert.equal(0, affected); S.findById(s._id, function (err, doc) { assert.ifError(err); assert.ok(!doc.ignore); assert.ok(!doc._doc.ignore); S.update({ _id: s._id }, { name: 'Drukqs', foo: 'fooey' }, function (err, affected) { assert.ifError(err); assert.equal(1, affected); S.findById(s._id, function (err, doc) { db.close(); assert.ifError(err); assert.ok(!doc._doc.foo); done(); }); }); }); }); }); }); }); it('passes number of affected docs', function(done){ var db = start() , B = db.model('BlogPostForUpdates', 'wwwwowowo'+random()) B.create({ title: 'one'},{title:'two'},{title:'three'}, function (err) { assert.ifError(err); B.update({}, { title: 'newtitle' }, { multi: true }, function (err, affected) { db.close(); assert.ifError(err); assert.equal(3, affected); done(); }); }); }); it('updates a number to null (gh-640)', function(done){ var db = start() var B = db.model('BlogPostB') var b = new B({ meta: { visitors: null }}); b.save(function (err) { assert.ifError(err); B.findById(b, function (err, b) { assert.ifError(err); assert.strictEqual(b.meta.visitors, null); B.update({ _id: b._id }, { meta: { visitors: null }}, function (err, docs) { db.close(); assert.strictEqual(null, err); done(); }); }); }); }) it('handles $pull from Mixed arrays (gh-735)', function(done){ var db = start(); var schema = new Schema({ comments: [] }); var M = db.model('gh-735', schema, 'gh-735_'+random()); M.create({ comments: [{ name: 'node 0.8' }] }, function (err, doc) { assert.ifError(err); M.update({ _id: doc._id }, { $pull: { comments: { name: 'node 0.8' }}}, function (err, affected) { assert.ifError(err); assert.equal(1, affected); db.close(); done(); }); }); }); it('handles $push with $ positionals (gh-1057)', function(done){ var db = start(); var taskSchema = new Schema({ name: String }) var componentSchema = new Schema({ name: String , tasks: [taskSchema] }); var projectSchema = new Schema({ name: String , components: [componentSchema] }); var Project = db.model('1057-project', projectSchema, '1057-'+random()); Project.create({ name: 'my project' }, function (err, project) { assert.ifError(err); var pid = project.id; var comp = project.components.create({ name: 'component' }); Project.update({ _id: pid }, { $push: { components: comp }}, function (err) { assert.ifError(err); var task = comp.tasks.create({ name: 'my task' }); Project.update({ _id: pid, 'components._id': comp._id }, { $push : { 'components.$.tasks': task }}, function (err) { assert.ifError(err); Project.findById(pid, function (err, proj) { assert.ifError(err); assert.ok(proj); assert.equal(1, proj.components.length); assert.equal('component', proj.components[0].name); assert.equal(comp.id, proj.components[0].id); assert.equal(1, proj.components[0].tasks.length); assert.equal('my task', proj.components[0].tasks[0].name); assert.equal(task.id, proj.components[0].tasks[0].id); done(); }) }); }); }); }) });