Skip to content

Commit

Permalink
Merge branch 'master' into gh-pages
Browse files Browse the repository at this point in the history
Conflicts:
	docs/schematypes.jade
  • Loading branch information
vkarpov15 committed Oct 24, 2016
2 parents fe79e32 + 98f16e5 commit 5193c15
Show file tree
Hide file tree
Showing 23 changed files with 334 additions and 53 deletions.
17 changes: 17 additions & 0 deletions History.md
@@ -1,3 +1,20 @@
4.6.5 / 2016-10-23
==================
* docs: fix grammar issues #4642 #4640 #4639 [silvermanj7](https://github.com/silvermanj7)
* fix(populate): filter out nonexistant values for dynref #4637
* fix(query): handle $type as a schematype operator #4632
* fix(schema): better handling for uppercase: false and lowercase: false #4622
* fix(query): don't run transforms on updateForExec() #4621
* fix(query): handle id = 0 in findById #4610
* fix(query): handle buffers in mergeClone #4609
* fix(document): handle undefined with conditional validator for validateSync #4607
* fix: upgrade to mongodb driver 2.2.11 #4581
* docs(schematypes): clarify schema.path() #4518
* fix(query): ensure path is defined before checking in timestamps #4514
* fix(model): set version key in upsert #4505
* fix(document): never depopulate top-level doc #3057
* refactor: ensure sync for setting non-capped collections #2690

4.6.4 / 2016-10-16
==================
* fix(query): cast $not correctly #4616 #4592 [prssn](https://github.com/prssn)
Expand Down
2 changes: 1 addition & 1 deletion docs/guide.jade
Expand Up @@ -127,7 +127,7 @@ block content

h3#query-helpers Query Helpers
:markdown
You can also add query helper functions, which are like instance methods,
You can also add query helper functions, which are like instance methods
but for mongoose queries. Query helper methods let you extend mongoose's
[chainable query builder API](./queries.html).

Expand Down
13 changes: 13 additions & 0 deletions docs/populate.jade
Expand Up @@ -344,6 +344,19 @@ block content
Band.find({}).populate('members').exec(function(error, bands) {
/* `bands.members` is now an array of instances of `Person` */
});

:markdown
Keep in mind that virtuals are _not_ included in `toJSON()` output by
default. If you want populate virtuals to show up when using functions
that rely on `JSON.stringify()`, like Express'
[`res.json()` function](http://expressjs.com/en/4x/api.html#res.json),
set the `virtuals: true` option on your schema's `toJSON` options.

:js
// Set `virtuals: true` so `res.json()` works
var BandSchema = new Schema({
name: String
}, { toJSON: { virtuals: true } });

h3#next Next Up
:markdown
Expand Down
20 changes: 20 additions & 0 deletions docs/schematypes.jade
Expand Up @@ -244,6 +244,26 @@ block content
| To create your own custom schema take a look at
a(href="/docs/customschematypes.html") Creating a Basic Custom Schema Type
|.

h3#path The `schema.path()` Function
:markdown
The `schema.path()` function returns the instantiated schema type for a
given path.
:js
var sampleSchema = new Schema({ name: { type: String, required: true } });
console.log(sampleSchema.path('name'));
// Output looks like:
/**
* SchemaString {
* enumValues: [],
* regExp: null,
* path: 'name',
* instance: 'String',
* validators: ...
*/
:markdown
You can use this function to inspect the schema type for a given path,
including what validators it has and what the type is.

h3#next Next Up
:markdown
Expand Down
8 changes: 4 additions & 4 deletions docs/subdocs.jade
Expand Up @@ -4,7 +4,7 @@ block content
h2 Sub Docs
:markdown
[Sub-documents](./api.html#types-embedded-js) are docs with schemas of
their own which are elements of a parents document array:
their own which are elements of a parent document array:
:js
var childSchema = new Schema({ name: 'string' });

Expand All @@ -24,7 +24,7 @@ block content
parent.save(callback);

:markdown
If an error occurs in a sub-documents' middleware, it is bubbled up to the `save()` callback of the parent, so error handling is a snap!
If an error occurs in a sub-document's middleware, it is bubbled up to the `save()` callback of the parent, so error handling is a snap!

:js
childSchema.pre('save', function (next) {
Expand Down Expand Up @@ -104,8 +104,8 @@ block content
});
:markdown
A single embedded sub-document behaves similarly to an embedded array.
It only gets saved when the parent document gets saved, and its pre/post
document middleware get executed.
It only gets saved when the parent document gets saved and its pre/post
document middleware gets executed.
:js
childSchema.pre('save', function(next) {
console.log(this.name); // prints 'Leia'
Expand Down
7 changes: 0 additions & 7 deletions lib/cast.js
Expand Up @@ -196,13 +196,6 @@ module.exports = function cast(schema, obj) {
continue;
}

if ($cond === '$type') {
if (typeof nested !== 'number' && typeof nested !== 'string') {
throw new Error('$type parameter must be number or string');
}
continue;
}

if ($cond === '$not') {
if (nested && schematype && !schematype.caster) {
_keys = Object.keys(nested);
Expand Down
12 changes: 5 additions & 7 deletions lib/document.js
Expand Up @@ -2023,17 +2023,13 @@ Document.prototype.$toObject = function(options, json) {
retainKeyOrder: this.schema.options.retainKeyOrder
};

if (options && options.depopulate && !options._skipDepopulateTopLevel && this.$__.wasPopulated) {
// _isNested will only be true if this is not the top level document, we
// should never depopulate
if (options && options.depopulate && options._isNested && this.$__.wasPopulated) {
// populated paths that we set to a document
return clone(this._id, options);
}

// If we're calling toObject on a populated doc, we may want to skip
// depopulated on the top level
if (options && options._skipDepopulateTopLevel) {
options._skipDepopulateTopLevel = false;
}

// When internally saving this document we always pass options,
// bypassing the custom schema options.
if (!(options && utils.getFunctionName(options.constructor) === 'Object') ||
Expand Down Expand Up @@ -2065,6 +2061,8 @@ Document.prototype.$toObject = function(options, json) {
// to save it from being overwritten by sub-transform functions
var originalTransform = options.transform;

options._isNested = true;

var ret = clone(this._doc, options) || {};

if (options.getters) {
Expand Down
3 changes: 2 additions & 1 deletion lib/drivers/node-mongodb-native/collection.js
Expand Up @@ -40,7 +40,8 @@ NativeCollection.prototype.onOpen = function() {

if (!_this.opts.capped.size) {
// non-capped
return _this.conn.db.collection(_this.name, callback);
callback(null, _this.conn.db.collection(_this.name));
return _this.collection;
}

// capped
Expand Down
22 changes: 17 additions & 5 deletions lib/model.js
Expand Up @@ -407,7 +407,7 @@ function handleAtomics(self, where, delta, data, value) {
// $set

if (isMongooseObject(value)) {
value = value.toObject({depopulate: 1});
value = value.toObject({depopulate: 1, _isNested: true});
} else if (value.valueOf) {
value = value.valueOf();
}
Expand All @@ -417,7 +417,7 @@ function handleAtomics(self, where, delta, data, value) {

function iter(mem) {
return isMongooseObject(mem)
? mem.toObject({depopulate: 1})
? mem.toObject({depopulate: 1, _isNested: true})
: mem;
}

Expand All @@ -426,7 +426,7 @@ function handleAtomics(self, where, delta, data, value) {
val = atomics[op];

if (isMongooseObject(val)) {
val = val.toObject({depopulate: true, transform: false});
val = val.toObject({depopulate: true, transform: false, _isNested: true});
} else if (Array.isArray(val)) {
val = val.map(iter);
} else if (val.valueOf) {
Expand Down Expand Up @@ -506,7 +506,7 @@ Model.prototype.$__delta = function() {
value = value.toObject();
operand(this, where, delta, data, value);
} else {
value = utils.clone(value, {depopulate: 1});
value = utils.clone(value, {depopulate: 1, _isNested: true});
operand(this, where, delta, data, value);
}
}
Expand Down Expand Up @@ -1556,7 +1556,7 @@ Model.findOneAndUpdate = function(conditions, update, options, callback) {
fields = options.fields;
}

update = utils.clone(update, {depopulate: 1});
update = utils.clone(update, {depopulate: 1, _isNested: true});
if (this.schema.options.versionKey && options && options.upsert) {
if (!update.$setOnInsert) {
update.$setOnInsert = {};
Expand Down Expand Up @@ -2081,6 +2081,13 @@ Model.update = function update(conditions, doc, options, callback) {
}
options = typeof options === 'function' ? options : utils.clone(options);

if (this.schema.options.versionKey && options && options.upsert) {
if (!doc.$setOnInsert) {
doc.$setOnInsert = {};
}
doc.$setOnInsert[this.schema.options.versionKey] = 0;
}

return mq.update(conditions, doc, options, callback);
};

Expand Down Expand Up @@ -2927,6 +2934,11 @@ function getModelsMapForPopulate(model, docs, options) {

if (refPath) {
modelNames = utils.getValue(refPath, doc);
if (Array.isArray(modelNames)) {
modelNames = modelNames.filter(function(v) {
return v != null;
});
}
} else {
if (!modelNameFromQuery) {
var modelForCurrentDoc = model;
Expand Down
6 changes: 5 additions & 1 deletion lib/query.js
Expand Up @@ -946,7 +946,11 @@ Query.prototype.getUpdate = function() {
*/

Query.prototype._updateForExec = function() {
var update = utils.clone(this._update, { retainKeyOrder: true });
var update = utils.clone(this._update, {
retainKeyOrder: true,
transform: false,
depopulate: true
});
var ops = Object.keys(update);
var i = ops.length;
var ret = {};
Expand Down
8 changes: 5 additions & 3 deletions lib/schema.js
Expand Up @@ -878,10 +878,12 @@ function applyTimestampsToChildren(query) {
if (hasDollarKey) {
if (update.$push) {
for (key in update.$push) {
var $path = schema.path(key);
if (update.$push[key] &&
schema.path(key).$isMongooseDocumentArray &&
schema.path(key).schema.options.timestamps) {
timestamps = schema.path(key).schema.options.timestamps;
$path &&
$path.$isMongooseDocumentArray &&
$path.schema.options.timestamps) {
timestamps = $path.schema.options.timestamps;
createdAt = timestamps.createdAt || 'createdAt';
updatedAt = timestamps.updatedAt || 'updatedAt';
update.$push[key][updatedAt] = now;
Expand Down
6 changes: 3 additions & 3 deletions lib/schema/number.js
Expand Up @@ -203,9 +203,9 @@ SchemaNumber.prototype.cast = function(value, doc, init) {
return ret;
}

var val = value && value._id
? value._id // documents
: value;
var val = value && typeof value._id !== 'undefined' ?
value._id : // documents
value;

if (!isNaN(val)) {
if (val === null) {
Expand Down
10 changes: 8 additions & 2 deletions lib/schema/string.js
Expand Up @@ -129,7 +129,10 @@ SchemaString.prototype.enum = function() {
* @return {SchemaType} this
*/

SchemaString.prototype.lowercase = function() {
SchemaString.prototype.lowercase = function(shouldApply) {
if (arguments.length > 0 && !shouldApply) {
return this;
}
return this.set(function(v, self) {
if (typeof v !== 'string') {
v = self.cast(v);
Expand All @@ -155,7 +158,10 @@ SchemaString.prototype.lowercase = function() {
* @return {SchemaType} this
*/

SchemaString.prototype.uppercase = function() {
SchemaString.prototype.uppercase = function(shouldApply) {
if (arguments.length > 0 && !shouldApply) {
return this;
}
return this.set(function(v, self) {
if (typeof v !== 'string') {
v = self.cast(v);
Expand Down
21 changes: 16 additions & 5 deletions lib/schematype.js
Expand Up @@ -776,12 +776,16 @@ SchemaType.prototype.doValidateSync = function(value, scope) {
}
};

var _this = this;
if (value === undefined && !_this.isRequired) {
return null;
var validators = this.validators;
if (value === void 0) {
if (this.validators.length > 0 && this.validators[0].type === 'required') {
validators = [this.validators[0]];
} else {
return null;
}
}

this.validators.forEach(function(v) {
validators.forEach(function(v) {
if (err) {
return;
}
Expand Down Expand Up @@ -874,7 +878,14 @@ SchemaType.prototype.$conditionalHandlers = {
$eq: handleSingle,
$in: handleArray,
$ne: handleSingle,
$nin: handleArray
$nin: handleArray,
$type: function(val) {
if (typeof val !== 'number' && typeof val !== 'string') {
throw new Error('$type parameter must be number or string');
}

return val;
}
};

/**
Expand Down
7 changes: 4 additions & 3 deletions lib/types/array.js
Expand Up @@ -232,7 +232,7 @@ MongooseArray.mixin = {
var i = keys.length;

if (i === 0) {
ret[0] = ['$set', this.toObject({depopulate: 1, transform: false})];
ret[0] = ['$set', this.toObject({depopulate: 1, transform: false, _isNested: true})];
return ret;
}

Expand All @@ -244,9 +244,9 @@ MongooseArray.mixin = {
// need to convert their elements as if they were MongooseArrays
// to handle populated arrays versus DocumentArrays properly.
if (isMongooseObject(val)) {
val = val.toObject({depopulate: 1, transform: false});
val = val.toObject({depopulate: 1, transform: false, _isNested: true});
} else if (Array.isArray(val)) {
val = this.toObject.call(val, {depopulate: 1, transform: false});
val = this.toObject.call(val, {depopulate: 1, transform: false, _isNested: true});
} else if (val.valueOf) {
val = val.valueOf();
}
Expand Down Expand Up @@ -713,6 +713,7 @@ MongooseArray.mixin = {

toObject: function(options) {
if (options && options.depopulate) {
options._isNested = true;
return this.map(function(doc) {
return doc instanceof Document
? doc.toObject(options)
Expand Down
2 changes: 1 addition & 1 deletion lib/types/subdocument.js
Expand Up @@ -19,7 +19,7 @@ Subdocument.prototype = Object.create(Document.prototype);

Subdocument.prototype.toBSON = function() {
return this.toObject({ transform: false });
},
};

/**
* Used as a stub for [hooks.js](https://github.com/bnoguchi/hooks-js/tree/31ec571cef0332e21121ee7157e0cf9728572cc3)
Expand Down
3 changes: 3 additions & 0 deletions lib/utils.js
Expand Up @@ -840,6 +840,9 @@ exports.mergeClone = function(to, fromObj) {
if (isMongooseObject(fromObj[key]) && !fromObj[key].isMongooseBuffer) {
obj = obj.toObject({ transform: false });
}
if (fromObj[key].isMongooseBuffer) {
obj = new Buffer(obj);
}
exports.mergeClone(to[key], obj);
} else {
// make sure to retain key order here because of a bug handling the
Expand Down

0 comments on commit 5193c15

Please sign in to comment.