Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

5.4 #7333

Merged
merged 52 commits into from
Dec 14, 2018
Merged

5.4 #7333

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
208a51c
feat(query): add `map()` to public API
vkarpov15 Oct 27, 2018
bfd78d5
Merge branch 'master' into 5.4
vkarpov15 Nov 3, 2018
99f3455
feat(query): add middleware for deleteOne + deleteMany
vkarpov15 Nov 3, 2018
67af0ba
feat(schematype): add `.get()` to string, objectid, number, mixed
vkarpov15 Nov 3, 2018
7a8e2e7
feat(schema): add `checkRequired()` function for string schemas to al…
vkarpov15 Nov 4, 2018
203960a
Merge branch 'master' into 5.4
vkarpov15 Nov 10, 2018
197b89c
feat: add SchemaType.checkRequired() to all schema types
vkarpov15 Nov 10, 2018
fb465db
Merge branch 'master' into 5.4
vkarpov15 Nov 17, 2018
dddf949
feat(schematype): allow custom casters for numbers
vkarpov15 Nov 17, 2018
34f99c1
style: fix lint
vkarpov15 Nov 17, 2018
f7afe78
test(document): add coverage for #7133
vkarpov15 Nov 17, 2018
774508a
feat(document): add hooks for `updateOne()`
vkarpov15 Nov 17, 2018
75e35d0
feat(query): add maxTimeMS helper
vkarpov15 Nov 18, 2018
79729bd
Merge branch 'master' into 5.4
vkarpov15 Nov 20, 2018
6fc4259
feat(model): add `Model.events.on('error')` for reporting all errors …
vkarpov15 Nov 20, 2018
2d34e07
Merge branch 'master' into 5.4
vkarpov15 Nov 21, 2018
da7cd85
test(aggregate): repro #7267
vkarpov15 Nov 22, 2018
c0365bb
feat(aggregate): add `.catch()` function to be consistent with queries
vkarpov15 Nov 22, 2018
de416e3
fix(aggregate): deprecate Aggregate#addCursorFlag()
vkarpov15 Nov 22, 2018
ae1c24e
Merge branch 'master' into 5.4
vkarpov15 Nov 24, 2018
9e06631
feat(populate): support `count` option
vkarpov15 Nov 24, 2018
0bfe622
docs(populate): add notes re: `count` option
vkarpov15 Nov 24, 2018
b1915fa
style: fix lint
vkarpov15 Nov 26, 2018
e7c6696
Merge branch 'master' into 5.4
vkarpov15 Nov 26, 2018
6340d11
feat(model+query): add findOneAndReplace
vkarpov15 Nov 26, 2018
e1f4e68
feat(schema): support passing an array
vkarpov15 Nov 27, 2018
d2e68e2
Merge branch 'master' into 5.4
vkarpov15 Dec 4, 2018
87fa4f0
Merge branch 'master' into 5.4
vkarpov15 Dec 4, 2018
89bcfcf
Merge branch 'master' into 5.4
vkarpov15 Dec 5, 2018
4a46287
Merge branch 'master' into 5.4
vkarpov15 Dec 11, 2018
e57daef
Merge branch '5.4' of github.com:Automattic/mongoose into 5.4
vkarpov15 Dec 11, 2018
4e480ac
Merge branch '5.4' into gh7045
vkarpov15 Dec 11, 2018
6dfd71e
feat(cast): add custom caster support for ObjectId #7045
vkarpov15 Dec 11, 2018
eed0afe
chore: rename file
vkarpov15 Dec 11, 2018
b075850
feat(boolean): add custom casting logic for booleans
vkarpov15 Dec 11, 2018
e2f18eb
Merge branch 'master' into 5.4
vkarpov15 Dec 12, 2018
9aa59ba
Merge branch 'master' into 5.4
vkarpov15 Dec 12, 2018
f7e99d6
Merge branch '5.4' into gh7045
vkarpov15 Dec 12, 2018
2f28d1f
chore: fix tests
vkarpov15 Dec 12, 2018
9f490bf
refactor: use consistent syntax for getting caster function re: #7045
vkarpov15 Dec 12, 2018
d2c9561
feat(schema): support custom cast function for string
vkarpov15 Dec 12, 2018
e0c6efb
test: fix tests re: ObjectId cast refactor
vkarpov15 Dec 12, 2018
3c686b4
test: rename test
vkarpov15 Dec 12, 2018
31bdd14
feat(populate): add clone option to ensure each result gets a separat…
vkarpov15 Dec 13, 2018
7af371e
docs(model): add `clone` option to docs
vkarpov15 Dec 13, 2018
0b3eb72
Merge pull request #7268 from Automattic/gh7125
vkarpov15 Dec 13, 2018
0c38458
fix: quick fix for #7268 #7125
vkarpov15 Dec 13, 2018
fbd1547
Merge branch '5.4' into gh7045
vkarpov15 Dec 13, 2018
1d12a53
feat(schema): add custom caster support for dates
vkarpov15 Dec 13, 2018
aede9d1
test: add coverage for inheritance re: #7045
vkarpov15 Dec 13, 2018
1a0d204
feat: add custom caster support for Decimal128
vkarpov15 Dec 13, 2018
3a18679
Merge pull request #7256 from Automattic/gh7045
vkarpov15 Dec 13, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 33 additions & 7 deletions docs/populate.jade
Original file line number Diff line number Diff line change
Expand Up @@ -444,21 +444,19 @@ block content

<h3 id="populate-virtuals"><a href="#populate-virtuals">Populate Virtuals</a></h3>

_New in 4.5.0_

So far you've only populated based on the `_id` field. However, that's
sometimes not the right choice.
In particular, [arrays that grow without bound are a MongoDB anti-pattern](https://docs.mongodb.com/manual/tutorial/model-referenced-one-to-many-relationships-between-documents/).
Using mongoose virtuals, you can define more sophisticated relationships
between documents.

```javascript
var PersonSchema = new Schema({
const PersonSchema = new Schema({
name: String,
band: String
});

var BandSchema = new Schema({
const BandSchema = new Schema({
name: String
});
BandSchema.virtual('members', {
Expand All @@ -471,8 +469,8 @@ block content
options: { sort: { name: -1 }, limit: 5 } // Query options, see http://bit.ly/mongoose-query-options
});

var Person = mongoose.model('Person', PersonSchema);
var Band = mongoose.model('Band', BandSchema);
const Person = mongoose.model('Person', PersonSchema);
const Band = mongoose.model('Band', BandSchema);

/**
* Suppose you have 2 bands: "Guns N' Roses" and "Motley Crue"
Expand All @@ -491,7 +489,7 @@ block content

```javascript
// Set `virtuals: true` so `res.json()` works
var BandSchema = new Schema({
const BandSchema = new Schema({
name: String
}, { toJSON: { virtuals: true } });
```
Expand All @@ -515,6 +513,34 @@ block content
});
```

<h3 id="count"><a href="#populate-virtuals">Populate Virtuals: The Count Option</a></h3>

Populate virtuals also support counting the number of documents with
matching `foreignField` as opposed to the documents themselves. Set the
`count` option on your virtual:

```javascript
const PersonSchema = new Schema({
name: String,
band: String
});

const BandSchema = new Schema({
name: String
});
BandSchema.virtual('numMembers', {
ref: 'Person', // The model to use
localField: 'name', // Find people where `localField`
foreignField: 'band', // is equal to `foreignField`
count: true // And only get the number of docs
});

// Later
const doc = await Band.findOne({ name: 'Motley Crue' }).
populate('numMembers');
doc.numMembers; // 2
```

<h3 id="populate-middleware"><a href="#populate-middleware">Populate in Middleware</a></h3>

You can populate in either pre or post [hooks](http://mongoosejs.com/docs/middleware.html). If you want to
Expand Down
24 changes: 19 additions & 5 deletions lib/aggregate.js
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,7 @@ Aggregate.prototype.explain = function(callback) {
}
cb(null, result);
});
});
}, this._model.events);
};

/**
Expand Down Expand Up @@ -823,16 +823,16 @@ Aggregate.prototype.cursor = function(options) {
* @param {Boolean} value
* @return {Aggregate} this
* @api public
* @see mongodb http://mongodb.github.io/node-mongodb-native/2.2/api/Cursor.html#addCursorFlag
* @deprecated Use [`.option()`](api.html#aggregate_Aggregate-option) instead. Note that MongoDB aggregations do **not** support a `noCursorTimeout` option.
*/

Aggregate.prototype.addCursorFlag = function(flag, value) {
Aggregate.prototype.addCursorFlag = util.deprecate(function(flag, value) {
if (!this.options) {
this.options = {};
}
this.options[flag] = value;
return this;
};
}, 'Mongoose: `Aggregate#addCursorFlag()` is deprecated, use `option()` instead');

/**
* Adds a collation
Expand Down Expand Up @@ -963,7 +963,7 @@ Aggregate.prototype.exec = function(callback) {
});
});
});
});
}, model.events);
};

/**
Expand All @@ -982,6 +982,20 @@ Aggregate.prototype.then = function(resolve, reject) {
return this.exec().then(resolve, reject);
};

/**
* Executes the query returning a `Promise` which will be
* resolved with either the doc(s) or rejected with the error.
* Like [`.then()`](#query_Query-then), but only takes a rejection handler.
*
* @param {Function} [reject]
* @return {Promise}
* @api public
*/

Aggregate.prototype.catch = function(reject) {
return this.exec().then(null, reject);
};

/**
* Returns an asyncIterator for use with [`for/await/of` loops](http://bit.ly/async-iterators)
* This function *only* works for `find()` queries.
Expand Down
6 changes: 6 additions & 0 deletions lib/browserDocument.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ function Document(obj, schema, fields, skipId, skipInit) {
Document.prototype = Object.create(NodeJSDocument.prototype);
Document.prototype.constructor = Document;

/*!
* ignore
*/

Document.events = new EventEmitter();

/*!
* Browser doc exposes the event emitter API
*/
Expand Down
41 changes: 41 additions & 0 deletions lib/cast/date.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use strict';

const assert = require('assert');

module.exports = function castDate(value) {
// Support empty string because of empty form values. Originally introduced
// in https://github.com/Automattic/mongoose/commit/efc72a1898fc3c33a319d915b8c5463a22938dfe
if (value == null || value === '') {
return null;
}

if (value instanceof Date) {
assert.ok(!isNaN(value.valueOf()));

return value;
}

let date;

assert.ok(typeof value !== 'boolean');

if (value instanceof Number || typeof value === 'number') {
date = new Date(value);
} else if (typeof value === 'string' && !isNaN(Number(value)) && (Number(value) >= 275761 || Number(value) < -271820)) {
// string representation of milliseconds take this path
date = new Date(Number(value));
} else if (typeof value.valueOf === 'function') {
// support for moment.js. This is also the path strings will take because
// strings have a `valueOf()`
date = new Date(value.valueOf());
} else {
// fallback
date = new Date(value);
}

if (!isNaN(date.valueOf())) {
return date;
}

assert.ok(false);
};
36 changes: 36 additions & 0 deletions lib/cast/decimal128.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use strict';

const Decimal128Type = require('../types/decimal128');
const assert = require('assert');

module.exports = function castDecimal128(value) {
if (value == null) {
return value;
}

if (typeof value === 'object' && typeof value.$numberDecimal === 'string') {
return Decimal128Type.fromString(value.$numberDecimal);
}

if (value instanceof Decimal128Type) {
return value;
}

if (typeof value === 'string') {
return Decimal128Type.fromString(value);
}

if (Buffer.isBuffer(value)) {
return new Decimal128Type(value);
}

if (typeof value === 'number') {
return Decimal128Type.fromString(String(value));
}

if (typeof value.valueOf === 'function' && typeof value.valueOf() === 'string') {
return Decimal128Type.fromString(value.valueOf());
}

assert.ok(false);
};
16 changes: 6 additions & 10 deletions lib/cast/number.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

const CastError = require('../error/cast');
const assert = require('assert');

/*!
* Given a value, cast it to a number, or throw a `CastError` if the value
Expand All @@ -9,14 +9,12 @@ const CastError = require('../error/cast');
* @param {Any} value
* @param {String} [path] optional the path to set on the CastError
* @return {Boolean|null|undefined}
* @throws {CastError} if `value` is not one of the allowed values
* @throws {Error} if `value` is not one of the allowed values
* @api private
*/

module.exports = function castNumber(val, path) {
if (isNaN(val)) {
throw new CastError('number', val, path);
}
module.exports = function castNumber(val) {
assert.ok(!isNaN(val));

if (val == null) {
return val;
Expand All @@ -29,9 +27,7 @@ module.exports = function castNumber(val, path) {
val = Number(val);
}

if (isNaN(val)) {
throw new CastError('number', val, path);
}
assert.ok(!isNaN(val));
if (val instanceof Number) {
return val;
}
Expand All @@ -45,5 +41,5 @@ module.exports = function castNumber(val, path) {
return new Number(val);
}

throw new CastError('number', val, path);
assert.ok(false);
};
29 changes: 29 additions & 0 deletions lib/cast/objectid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use strict';

const ObjectId = require('../driver').get().ObjectId;
const assert = require('assert');

module.exports = function castObjectId(value) {
if (value == null) {
return value;
}

if (value instanceof ObjectId) {
return value;
}

if (value._id) {
if (value._id instanceof ObjectId) {
return value._id;
}
if (value._id.toString instanceof Function) {
return new ObjectId(value._id.toString());
}
}

if (value.toString instanceof Function) {
return new ObjectId(value.toString());
}

assert.ok(false);
};
8 changes: 8 additions & 0 deletions lib/collection.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,14 @@ Collection.prototype.findOneAndDelete = function() {
throw new Error('Collection#findOneAndDelete unimplemented by driver');
};

/**
* Abstract method that drivers must implement.
*/

Collection.prototype.findOneAndReplace = function() {
throw new Error('Collection#findOneAndReplace unimplemented by driver');
};

/**
* Abstract method that drivers must implement.
*/
Expand Down
5 changes: 3 additions & 2 deletions lib/cursor/QueryCursor.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ function QueryCursor(query, options) {
const model = query.model;
this._mongooseOptions = {};
this._transforms = [];
this.model = model;
model.hooks.execPre('find', query, () => {
this._transforms = this._transforms.concat(query._transforms.slice());
if (options.transform) {
Expand Down Expand Up @@ -157,7 +158,7 @@ QueryCursor.prototype.close = function(callback) {
this.emit('close');
cb(null);
});
});
}, this.model.events);
};

/**
Expand All @@ -178,7 +179,7 @@ QueryCursor.prototype.next = function(callback) {
}
cb(null, doc);
});
});
}, this.model.events);
};

/**
Expand Down
19 changes: 13 additions & 6 deletions lib/document.js
Original file line number Diff line number Diff line change
Expand Up @@ -594,10 +594,17 @@ Document.prototype.update = function update() {
* @instance
*/

Document.prototype.updateOne = function updateOne() {
const args = utils.args(arguments);
args.unshift({_id: this._id});
return this.constructor.updateOne.apply(this.constructor, args);
Document.prototype.updateOne = function updateOne(doc, options, callback) {
return utils.promiseOrCallback(callback,
cb => this.$__updateOne(doc, options, cb), this.constructor.events);
};

/*!
* ignore
*/

Document.prototype.$__updateOne = function $__updateOne(doc, options, callback) {
return this.constructor.updateOne({ _id: this._id }, doc, options, callback);
};

/**
Expand Down Expand Up @@ -1705,7 +1712,7 @@ Document.prototype.validate = function(options, callback) {

return utils.promiseOrCallback(callback, cb => this.$__validate(function(error) {
cb(error);
}));
}), this.constructor.events);
};

/*!
Expand Down Expand Up @@ -3012,7 +3019,7 @@ Document.prototype.populate = function populate() {
Document.prototype.execPopulate = function(callback) {
return utils.promiseOrCallback(callback, cb => {
this.populate(cb);
});
}, this.constructor.events);
};

/**
Expand Down
Loading