Skip to content

Commit

Permalink
added; removing subdoc with _id value support
Browse files Browse the repository at this point in the history
closes #1278
  • Loading branch information
aheckmann committed Jan 23, 2013
1 parent ea881d8 commit 5ff32cc
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 10 deletions.
14 changes: 13 additions & 1 deletion lib/types/array.js
Original file line number Diff line number Diff line change
Expand Up @@ -344,13 +344,25 @@ MongooseArray.prototype.shift = function () {
};

/**
* Removes items from an array atomically
* Removes items from an array atomically.
*
* ####Examples:
*
* doc.array.remove(ObjectId)
* doc.array.remove({ _id: 'someId' })
* doc.array.remove(36)
* doc.array.remove('tag 1', 'tag 2')
*
* To remove a document from a subdocument array we may pass an object with a matching `_id`.
*
* doc.subdocs.push({ _id: 4815162342 })
* doc.subdocs.remove({ _id: 4815162342 }) // removed
*
* Or we may passing the value directly and let mongoose take care of it.
*
* doc.subdocs.push({ _id: 4815162342 })
* doc.subdocs.remove(4815162342); // works
*
* @param {Object} [args...] values to remove
* @see mongodb http://www.mongodb.org/display/DOCS/Updating/#Updating-%24pull
* @api public
Expand Down
12 changes: 11 additions & 1 deletion lib/types/documentarray.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ var MongooseArray = require('./array')
, driver = global.MONGOOSE_DRIVER_PATH || '../drivers/node-mongodb-native'
, ObjectId = require(driver + '/objectid')
, ObjectIdSchema = require('../schema/objectid')
, utils = require('../utils')
, util = require('util')

/**
Expand Down Expand Up @@ -56,8 +57,17 @@ MongooseDocumentArray.prototype.__proto__ = MongooseArray.prototype;
*/

MongooseDocumentArray.prototype._cast = function (value) {
if (value instanceof this._schema.casterConstructor)
if (value instanceof this._schema.casterConstructor) {
return value;
}

// handle cast('string') or cast(ObjectId) etc.
// only objects are permitted so we can safely assume that
// non-objects are to be interpreted as _id
if (Buffer.isBuffer(value) ||
value instanceof ObjectId || !utils.isObject(value)) {
value = { _id: value };
}

return new this._schema.casterConstructor(value, this);
};
Expand Down
124 changes: 116 additions & 8 deletions test/types.array.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1373,24 +1373,42 @@ describe('types array', function(){
})

describe('removing from an array atomically using MongooseArray#remove', function(){
it('works', function(done){
var db = start()
, BlogPost = db.model('BlogPost', collection);
var db;
var B;

before(function(done){
var schema = Schema({
numbers: ['number']
, numberIds: [{ _id: 'number', name: 'string' }]
, stringIds: [{ _id: 'string', name: 'string' }]
, bufferIds: [{ _id: 'buffer', name: 'string' }]
, oidIds: [{ name: 'string' }]
})

db = start();
B = db.model('BlogPost', schema);
done();
})

var post = new BlogPost();
after(function(done){
db.close(done);
})

it('works', function(done){
var post = new B;
post.numbers.push(1, 2, 3);

post.save(function (err) {
assert.ifError(err);

BlogPost.findById(post._id, function (err, doc) {
B.findById(post._id, function (err, doc) {
assert.ifError(err);

doc.numbers.remove('1');
doc.save(function (err) {
assert.ifError(err);

BlogPost.findById(post.get('_id'), function (err, doc) {
B.findById(post.get('_id'), function (err, doc) {
assert.ifError(err);

assert.equal(doc.numbers.length, 2);
Expand All @@ -1399,8 +1417,7 @@ describe('types array', function(){
doc.save(function (err) {
assert.ifError(err);

BlogPost.findById(post._id, function (err, doc) {
db.close();
B.findById(post._id, function (err, doc) {
assert.ifError(err);
assert.equal(0, doc.numbers.length);
done();
Expand All @@ -1411,6 +1428,97 @@ describe('types array', function(){
});
});
})

describe('with subdocs', function(){
function docs (arr) {
return arr.map(function (val) {
return { _id: val }
});
}

it('supports passing strings', function(done){
var post = new B({ stringIds: docs('a b c d'.split(' ')) })
post.save(function (err) {
assert.ifError(err);
B.findById(post, function (err, post) {
assert.ifError(err);
post.stringIds.remove('b');
post.save(function (err) {
assert.ifError(err);
B.findById(post, function (err, post) {
assert.ifError(err);
assert.equal(3, post.stringIds.length);
assert.ok(!post.stringIds.id('b'));
done();
})
})
})
})
})
it('supports passing numbers', function(done){
var post = new B({ numberIds: docs([1,2,3,4]) })
post.save(function (err) {
assert.ifError(err);
B.findById(post, function (err, post) {
assert.ifError(err);
post.numberIds.remove(2,4);
post.save(function (err) {
assert.ifError(err);
B.findById(post, function (err, post) {
assert.ifError(err);
assert.equal(2, post.numberIds.length);
assert.ok(!post.numberIds.id(2));
assert.ok(!post.numberIds.id(4));
done();
})
})
})
})
})
it('supports passing objectids', function(done){
var OID = mongoose.Types.ObjectId;
var a = new OID;
var b = new OID;
var c = new OID;
var post = new B({ oidIds: docs([a,b,c]) })
post.save(function (err) {
assert.ifError(err);
B.findById(post, function (err, post) {
assert.ifError(err);
post.oidIds.remove(a,c);
post.save(function (err) {
assert.ifError(err);
B.findById(post, function (err, post) {
assert.ifError(err);
assert.equal(1, post.oidIds.length);
assert.ok(!post.oidIds.id(a));
assert.ok(!post.oidIds.id(c));
done();
})
})
})
})
})
it('supports passing buffers', function(done){
var post = new B({ bufferIds: docs(['a','b','c','d']) })
post.save(function (err) {
assert.ifError(err);
B.findById(post, function (err, post) {
assert.ifError(err);
post.bufferIds.remove(new Buffer('a'));
post.save(function (err) {
assert.ifError(err);
B.findById(post, function (err, post) {
assert.ifError(err);
assert.equal(3, post.bufferIds.length);
assert.ok(!post.bufferIds.id(new Buffer('a')));
done();
})
})
})
})
})
})
})
})

0 comments on commit 5ff32cc

Please sign in to comment.