Skip to content

Commit

Permalink
fixed; setting populated paths
Browse files Browse the repository at this point in the history
Setting a populated path (or manipulate a populated array) with
works when the schema path does not have a `ref` declared and
an adhoc model was used for population.

relates to #570
  • Loading branch information
aheckmann committed Mar 7, 2013
1 parent 18dbe62 commit 87b2a33
Show file tree
Hide file tree
Showing 13 changed files with 675 additions and 217 deletions.
23 changes: 18 additions & 5 deletions lib/document.js
Original file line number Diff line number Diff line change
Expand Up @@ -279,18 +279,18 @@ Document.prototype.init = function (doc, opts, fn) {

this.isNew = false;

init(this, doc, this._doc);
this._storeShard();

// handle docs with populated paths
if (opts && opts.populated && opts.populated.length) {
var id = this.id;
if (doc._id && opts && opts.populated && opts.populated.length) {
var id = String(doc._id);
for (var i = 0; i < opts.populated.length; ++i) {
var item = opts.populated[i];
this.populated(item.path, item._docs[id], item.model);
}
}

init(this, doc, this._doc);
this._storeShard();

this.emit('init', this);
if (fn) fn(null);
return this;
Expand Down Expand Up @@ -1630,6 +1630,19 @@ Document.prototype.populated = function (path, /* internal */ val, model) {
return val;
}

/**
* Returns the full path to this document.
*
* @param {String} [path]
* @return {String}
* @api private
*/

Document.prototype.$fullPath = function (path) {
// overridden in SubDocuments
return path || '';
}

/*!
* Module exports.
*/
Expand Down
4 changes: 4 additions & 0 deletions lib/internal.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,8 @@ function InternalCache () {
this.populated = undefined;
this.scope = undefined;
this.activePaths = new ActiveRoster;

// embedded docs
this.ownerDocument = undefined;
this.fullPath = undefined;
}
30 changes: 20 additions & 10 deletions lib/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,38 +263,48 @@ function operand (self, where, delta, data, val, op) {
* @param {Object} where
* @param {Object} delta
* @param {Object} data
* @param {Array} val
* @param {Array} value
*/

function handleAtomics (self, where, delta, data, val) {
function handleAtomics (self, where, delta, data, value) {
if (delta.$set && delta.$set[data.path]) {
// $set has precedence over other atomics
return;
}

var atomics = val._atomics
if ('function' == typeof value.$__getAtomics) {
value.$__getAtomics().forEach(function (atomic) {
var op = atomic[0];
var val = atomic[1];
operand(self, where, delta, data, val, op);
})
return;
}

// legacy support for plugins

var atomics = value._atomics
, ops = Object.keys(atomics)
, schema = data.schema
, path = data.path
, i = ops.length
, val
, op;

if (0 === i) {
// $set

if (isMongooseObject(val)) {
val = val.toObject({ depopulate: 1 });
} else if (val.valueOf) {
val = val.valueOf();
if (isMongooseObject(value)) {
value = value.toObject({ depopulate: 1 });
} else if (value.valueOf) {
value = value.valueOf();
}

return operand(self, where, delta, data, val);
return operand(self, where, delta, data, value);
}

while (i--) {
op = ops[i];
val = atomics[op];

if (isMongooseObject(val)) {
val = val.toObject({ depopulate: 1 })
} else if (Array.isArray(val)) {
Expand Down
40 changes: 34 additions & 6 deletions lib/schema/buffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ var SchemaType = require('../schematype')
, CastError = SchemaType.CastError
, MongooseBuffer = require('../types').Buffer
, Binary = MongooseBuffer.Binary
, Query = require('../query');
, Query = require('../query')
, utils = require('../utils')
, Document

/**
* Buffer SchemaType constructor
Expand All @@ -33,8 +35,12 @@ SchemaBuffer.prototype.__proto__ = SchemaType.prototype;
* @api private
*/

SchemaBuffer.prototype.checkRequired = function (value) {
return !!(value && value.length);
SchemaBuffer.prototype.checkRequired = function (value, doc) {
if (SchemaType._isRef(this, value, doc, true)) {
return null != value;
} else {
return !!(value && value.length);
}
};

/**
Expand All @@ -47,9 +53,31 @@ SchemaBuffer.prototype.checkRequired = function (value) {
*/

SchemaBuffer.prototype.cast = function (value, doc, init) {
var populated = init || doc && doc.populated && !! doc.populated(this.path);
if (SchemaType._isRef(this, value, populated)) {
return value;
if (SchemaType._isRef(this, value, doc, init)) {
// wait! we may need to cast this to a document

// lazy load
Document || (Document = require('./../document'));

if (value instanceof Document || null == value) {
return value;
}

// setting a populated path
if (Buffer.isBuffer(value)) {
return value;
} else if (!utils.isObject(value)) {
throw new CastError('buffer', value, this.path);
}

// Handle the case where user directly sets a populated
// path to a plain object; cast to the Model used in
// the population query.
var path = doc.$fullPath(this.path);
var owner = doc.ownerDocument ? doc.ownerDocument() : doc;
var pop = owner.populated(path, true);
var ret = new pop.model(value);
return ret;
}

// documents with Buffer _ids
Expand Down
33 changes: 28 additions & 5 deletions lib/schema/number.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

var SchemaType = require('../schematype')
, CastError = SchemaType.CastError
, utils = require('../utils')
, Document

/**
* Number SchemaType constructor.
Expand All @@ -30,8 +32,8 @@ SchemaNumber.prototype.__proto__ = SchemaType.prototype;
* @api private
*/

SchemaNumber.prototype.checkRequired = function checkRequired (value) {
if (SchemaType._isRef(this, value, true)) {
SchemaNumber.prototype.checkRequired = function checkRequired (value, doc) {
if (SchemaType._isRef(this, value, doc, true)) {
return null != value;
} else {
return typeof value == 'number' || value instanceof Number;
Expand Down Expand Up @@ -110,9 +112,30 @@ SchemaNumber.prototype.max = function (value, message) {
*/

SchemaNumber.prototype.cast = function (value, doc, init) {
var populated = init || doc && doc.populated && !! doc.populated(this.path);
if (SchemaType._isRef(this, value, populated)) {
return value;
if (SchemaType._isRef(this, value, doc, init)) {
// wait! we may need to cast this to a document

// lazy load
Document || (Document = require('./../document'));

if (value instanceof Document || null == value) {
return value;
}

// setting a populated path
if ('number' == typeof value) {
return value;
} else if (Buffer.isBuffer(value) || !utils.isObject(value)) {
throw new CastError('number', value, this.path);
}

// Handle the case where user directly sets a populated
// path to a plain object; cast to the Model used in
// the population query.
var path = doc.$fullPath(this.path);
var owner = doc.ownerDocument ? doc.ownerDocument() : doc;
var pop = owner.populated(path, true);
return new pop.model(value);
}

var val = value && value._id
Expand Down
36 changes: 29 additions & 7 deletions lib/schema/objectid.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
var SchemaType = require('../schematype')
, CastError = SchemaType.CastError
, driver = global.MONGOOSE_DRIVER_PATH || './../drivers/node-mongodb-native'
, oid = require('../types/objectid');

, oid = require('../types/objectid')
, utils = require('../utils')
, Document

/**
* ObjectId SchemaType constructor.
Expand All @@ -33,8 +34,8 @@ ObjectId.prototype.__proto__ = SchemaType.prototype;
* @api private
*/

ObjectId.prototype.checkRequired = function checkRequired (value) {
if (SchemaType._isRef(this, value, true)) {
ObjectId.prototype.checkRequired = function checkRequired (value, doc) {
if (SchemaType._isRef(this, value, doc, true)) {
return null != value;
} else {
return value instanceof oid;
Expand All @@ -51,9 +52,30 @@ ObjectId.prototype.checkRequired = function checkRequired (value) {
*/

ObjectId.prototype.cast = function (value, doc, init) {
var populated = init || doc && doc.populated && !! doc.populated(this.path);
if (SchemaType._isRef(this, value, populated)) {
return value;
if (SchemaType._isRef(this, value, doc, init)) {
// wait! we may need to cast this to a document

// lazy load
Document || (Document = require('./../document'));

if (value instanceof Document || null == value) {
return value;
}

// setting a populated path
if (value instanceof oid) {
return value;
} else if (Buffer.isBuffer(value) || !utils.isObject(value)) {
throw new CastError('ObjectId', value, this.path);
}

// Handle the case where user directly sets a populated
// path to a plain object; cast to the Model used in
// the population query.
var path = doc.$fullPath(this.path);
var owner = doc.ownerDocument ? doc.ownerDocument() : doc;
var pop = owner.populated(path, true);
return new pop.model(value);
}

if (value === null) return value;
Expand Down
35 changes: 29 additions & 6 deletions lib/schema/string.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
*/

var SchemaType = require('../schematype')
, CastError = SchemaType.CastError;
, CastError = SchemaType.CastError
, utils = require('../utils')
, Document

/**
* String SchemaType constructor.
Expand Down Expand Up @@ -177,8 +179,8 @@ SchemaString.prototype.match = function match (regExp) {
* @api private
*/

SchemaString.prototype.checkRequired = function checkRequired (value) {
if (SchemaType._isRef(this, value, true)) {
SchemaString.prototype.checkRequired = function checkRequired (value, doc) {
if (SchemaType._isRef(this, value, doc, true)) {
return null != value;
} else {
return (value instanceof String || typeof value == 'string') && value.length;
Expand All @@ -192,9 +194,30 @@ SchemaString.prototype.checkRequired = function checkRequired (value) {
*/

SchemaString.prototype.cast = function (value, doc, init) {
var populated = init || doc && doc.populated && !! doc.populated(this.path);
if (SchemaType._isRef(this, value, populated)) {
return value;
if (SchemaType._isRef(this, value, doc, init)) {
// wait! we may need to cast this to a document

// lazy load
Document || (Document = require('./../document'));

if (value instanceof Document || null == value) {
return value;
}

// setting a populated path
if ('string' == typeof value) {
return value;
} else if (Buffer.isBuffer(value) || !utils.isObject(value)) {
throw new CastError('string', value, this.path);
}

// Handle the case where user directly sets a populated
// path to a plain object; cast to the Model used in
// the population query.
var path = doc.$fullPath(this.path);
var owner = doc.ownerDocument ? doc.ownerDocument() : doc;
var pop = owner.populated(path, true);
return new pop.model(value);
}

if (value === null) {
Expand Down
Loading

0 comments on commit 87b2a33

Please sign in to comment.