Skip to content

Commit 59a29a8

Browse files
committed
Merge branch '6.x'
2 parents 678178a + 7a90868 commit 59a29a8

26 files changed

+474
-126
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ jobs:
106106
- name: Setup Deno
107107
uses: denoland/setup-deno@v1
108108
with:
109-
deno-version: v1.30.x
109+
deno-version: v1.33.x
110110
- run: deno --version
111111
- run: npm install
112112
- name: Run Deno tests

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
6.11.1 / 2023-05-08
2+
===================
3+
* fix(query): apply schema-level paths before calculating projection for findOneAndUpdate() #13348 #13340
4+
* fix: add SUPPRESS_JEST_WARNINGS environment variable to hide jest warnings #13384 #13373
5+
* types(model): allow overwriting expected param type for bulkWrite() #13292 [hasezoey](https://github.com/hasezoey)
6+
7+
6.11.0 / 2023-05-01
8+
===================
9+
* feat: upgrade to mongodb 4.16.0 for Deno+Atlas connection fix #13337 #13075
10+
* perf: speed up creating maps of subdocuments #13280 #13191 #13271
11+
* fix(query): set ObjectParameterError if calling findOneAndX() with filter as non-object #13338
12+
* fix(document): merge Document $inc calls instead of overwriting #13322
13+
* fix(update): handle casting doubly nested arrays with $pullAll #13285
14+
* docs: backport documentation versioning changes to 6.x #13253 #13190 [hasezoey](https://github.com/hasezoey)
15+
116
7.1.0 / 2023-04-27
217
==================
318
* feat: upgrade mongodb -> 5.3.0

benchmarks/mapOfSubdocs.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
'use strict';
2+
3+
const mongoose = require('../');
4+
5+
run().catch(err => {
6+
console.error(err);
7+
process.exit(-1);
8+
});
9+
10+
async function run() {
11+
await mongoose.connect('mongodb://127.0.0.1:27017/mongoose_test', {
12+
serverSelectionTimeoutMS: 5000
13+
});
14+
15+
const minisMap = new mongoose.Schema(
16+
{
17+
//? Mini reference
18+
mini: {
19+
type: Map,
20+
of: new mongoose.Schema({
21+
//? Mini ID
22+
miniID: { type: mongoose.Schema.Types.ObjectId, ref: 'Mini', required: true },
23+
//? Timestamp as number
24+
timestamp: { type: Number, required: true },
25+
}),
26+
},
27+
},
28+
//? Automatic creation of timestamps for creation and updating.
29+
//? This will be created on the background by the package
30+
{ timestamps: true }
31+
);
32+
const MinisMap = mongoose.model('MinisMap', minisMap);
33+
await MinisMap.init();
34+
35+
const mini = new Map();
36+
for (let i = 0; i < 2000; ++i) {
37+
const miniID = new mongoose.Types.ObjectId();
38+
mini.set(miniID, {
39+
miniID,
40+
timestamp: Math.floor(Math.random() * 1000000)
41+
});
42+
}
43+
44+
let loopStart = Date.now();
45+
46+
for (let k = 0; k < 10; k++) {
47+
await MinisMap.create({ mini });
48+
}
49+
50+
const results = {
51+
'Average save time ms': (Date.now() - loopStart) / 10
52+
};
53+
54+
console.log(JSON.stringify(results, null, ' '));
55+
}

docs/jest.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ Jest is a JavaScript runtime developed by Facebook that is usually used for test
44
Because Jest is designed primarily for testing React applications, using it to test Node.js server-side applications comes with a lot of caveats.
55
We strongly recommend using a different testing framework, like [Mocha](https://mochajs.org/).
66

7+
To suppress any Jest warnings from Mongoose, set the `SUPPRESS_JEST_WARNINGS` environment variable:
8+
9+
```
10+
env SUPPRESS_JEST_WARNINGS=1 npm test
11+
```
12+
713
If you choose to delve into dangerous waters and test Mongoose apps with Jest, here's what you need to know:
814

915
<h2 id="recommended-testenvironment"><a href="#recommended-testenvironment">Recommended <code>testEnvironment</code></a></h2>

lib/document.js

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1778,7 +1778,11 @@ Document.prototype.$inc = function $inc(path, val) {
17781778

17791779
if (shouldSet) {
17801780
this.$__.primitiveAtomics = this.$__.primitiveAtomics || {};
1781-
this.$__.primitiveAtomics[path] = { $inc: valToInc };
1781+
if (this.$__.primitiveAtomics[path] == null) {
1782+
this.$__.primitiveAtomics[path] = { $inc: valToInc };
1783+
} else {
1784+
this.$__.primitiveAtomics[path].$inc += valToInc;
1785+
}
17821786
this.markModified(path);
17831787
this.$__setValue(path, valToSet);
17841788
}
@@ -2195,19 +2199,26 @@ Document.prototype[documentModifiedPaths] = Document.prototype.modifiedPaths;
21952199

21962200
Document.prototype.isModified = function(paths, modifiedPaths) {
21972201
if (paths) {
2198-
const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify'));
2199-
if (directModifiedPaths.length === 0) {
2200-
return false;
2201-
}
2202-
22032202
if (!Array.isArray(paths)) {
22042203
paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' ');
22052204
}
2205+
2206+
const directModifiedPathsObj = this.$__.activePaths.states.modify;
2207+
if (directModifiedPathsObj == null) {
2208+
return false;
2209+
}
2210+
for (const path of paths) {
2211+
if (Object.prototype.hasOwnProperty.call(directModifiedPathsObj, path)) {
2212+
return true;
2213+
}
2214+
}
2215+
22062216
const modified = modifiedPaths || this[documentModifiedPaths]();
22072217
const isModifiedChild = paths.some(function(path) {
22082218
return !!~modified.indexOf(path);
22092219
});
22102220

2221+
const directModifiedPaths = Object.keys(directModifiedPathsObj);
22112222
return isModifiedChild || paths.some(function(path) {
22122223
return directModifiedPaths.some(function(mod) {
22132224
return mod === path || path.startsWith(mod + '.');

lib/error/objectParameter.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class ObjectParameterError extends MongooseError {
1818
*/
1919
constructor(value, paramName, fnName) {
2020
super('Parameter "' + paramName + '" to ' + fnName +
21-
'() must be an object, got ' + value.toString());
21+
'() must be an object, got "' + value.toString() + '" (type ' + typeof value + ')');
2222
}
2323
}
2424

lib/helpers/discriminator/mergeDiscriminatorSchema.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ module.exports = function mergeDiscriminatorSchema(to, from, path, seen) {
5555
// base schema has a given path as a single nested but discriminator schema
5656
// has the path as a document array, or vice versa (gh-9534)
5757
if ((from[key].$isSingleNested && to[key].$isMongooseDocumentArray) ||
58-
(from[key].$isMongooseDocumentArray && to[key].$isSingleNested)) {
58+
(from[key].$isMongooseDocumentArray && to[key].$isSingleNested) ||
59+
(from[key].$isMongooseDocumentArrayElement && to[key].$isMongooseDocumentArrayElement)) {
5960
continue;
6061
} else if (from[key].instanceOfSchema) {
6162
if (to[key].instanceOfSchema) {

lib/helpers/printJestWarning.js

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,20 @@
22

33
const utils = require('../utils');
44

5-
if (typeof jest !== 'undefined' && typeof window !== 'undefined') {
6-
utils.warn('Mongoose: looks like you\'re trying to test a Mongoose app ' +
7-
'with Jest\'s default jsdom test environment. Please make sure you read ' +
8-
'Mongoose\'s docs on configuring Jest to test Node.js apps: ' +
9-
'https://mongoosejs.com/docs/jest.html');
10-
}
5+
if (typeof jest !== 'undefined' && !process.env.SUPPRESS_JEST_WARNINGS) {
6+
if (typeof window !== 'undefined') {
7+
utils.warn('Mongoose: looks like you\'re trying to test a Mongoose app ' +
8+
'with Jest\'s default jsdom test environment. Please make sure you read ' +
9+
'Mongoose\'s docs on configuring Jest to test Node.js apps: ' +
10+
'https://mongoosejs.com/docs/jest.html. Set the SUPPRESS_JEST_WARNINGS to true ' +
11+
'to hide this warning.');
12+
}
1113

12-
if (typeof jest !== 'undefined' && setTimeout.clock != null && typeof setTimeout.clock.Date === 'function') {
13-
utils.warn('Mongoose: looks like you\'re trying to test a Mongoose app ' +
14-
'with Jest\'s mock timers enabled. Please make sure you read ' +
15-
'Mongoose\'s docs on configuring Jest to test Node.js apps: ' +
16-
'https://mongoosejs.com/docs/jest.html');
14+
if (setTimeout.clock != null && typeof setTimeout.clock.Date === 'function') {
15+
utils.warn('Mongoose: looks like you\'re trying to test a Mongoose app ' +
16+
'with Jest\'s mock timers enabled. Please make sure you read ' +
17+
'Mongoose\'s docs on configuring Jest to test Node.js apps: ' +
18+
'https://mongoosejs.com/docs/jest.html. Set the SUPPRESS_JEST_WARNINGS to true ' +
19+
'to hide this warning.');
20+
}
1721
}

lib/query.js

Lines changed: 30 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1967,9 +1967,13 @@ Query.prototype._optionsForExec = function(model) {
19671967
}
19681968
}
19691969

1970-
const projection = this._fieldsForExec();
1971-
if (projection != null) {
1972-
options.projection = projection;
1970+
this._applyPaths();
1971+
if (this._fields != null) {
1972+
this._fields = this._castFields(this._fields);
1973+
const projection = this._fieldsForExec();
1974+
if (projection != null) {
1975+
options.projection = projection;
1976+
}
19731977
}
19741978

19751979
return options;
@@ -2218,10 +2222,6 @@ Query.prototype._find = async function _find() {
22182222
throw this.error();
22192223
}
22202224

2221-
this._applyPaths();
2222-
this._fields = this._castFields(this._fields);
2223-
2224-
const fields = this._fieldsForExec();
22252225
const mongooseOptions = this._mongooseOptions;
22262226
const _this = this;
22272227
const userProvidedFields = _this._userProvidedFields || {};
@@ -2237,8 +2237,8 @@ Query.prototype._find = async function _find() {
22372237
});
22382238

22392239
const options = this._optionsForExec();
2240-
options.projection = this._fieldsForExec();
22412240
const filter = this._conditions;
2241+
const fields = options.projection;
22422242

22432243
const cursor = await this._collection.collection.find(filter, options);
22442244
if (options.explain) {
@@ -2481,8 +2481,6 @@ Query.prototype._findOne = async function _findOne() {
24812481
throw err;
24822482
}
24832483

2484-
this._applyPaths();
2485-
this._fields = this._castFields(this._fields);
24862484
applyGlobalMaxTimeMS(this.options, this.model);
24872485
applyGlobalDiskUse(this.options, this.model);
24882486

@@ -3153,8 +3151,8 @@ function prepareDiscriminatorCriteria(query) {
31533151
* @api public
31543152
*/
31553153

3156-
Query.prototype.findOneAndUpdate = function(criteria, doc, options) {
3157-
if (typeof conditions === 'function' ||
3154+
Query.prototype.findOneAndUpdate = function(filter, doc, options) {
3155+
if (typeof filter === 'function' ||
31583156
typeof doc === 'function' ||
31593157
typeof options === 'function' ||
31603158
typeof arguments[3] === 'function') {
@@ -3170,13 +3168,17 @@ Query.prototype.findOneAndUpdate = function(criteria, doc, options) {
31703168
options = undefined;
31713169
break;
31723170
case 1:
3173-
doc = criteria;
3174-
criteria = options = undefined;
3171+
doc = filter;
3172+
filter = options = undefined;
31753173
break;
31763174
}
31773175

3178-
if (mquery.canMerge(criteria)) {
3179-
this.merge(criteria);
3176+
if (mquery.canMerge(filter)) {
3177+
this.merge(filter);
3178+
} else if (filter != null) {
3179+
this.error(
3180+
new ObjectParameterError(filter, 'filter', 'findOneAndUpdate')
3181+
);
31803182
}
31813183

31823184
// apply doc
@@ -3378,7 +3380,7 @@ Query.prototype.findOneAndRemove = function(conditions, options) {
33783380
*
33793381
* @method findOneAndDelete
33803382
* @memberOf Query
3381-
* @param {Object} [conditions]
3383+
* @param {Object} [filter]
33823384
* @param {Object} [options]
33833385
* @param {Boolean} [options.rawResult] if true, returns the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html)
33843386
* @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html).
@@ -3388,8 +3390,8 @@ Query.prototype.findOneAndRemove = function(conditions, options) {
33883390
* @api public
33893391
*/
33903392

3391-
Query.prototype.findOneAndDelete = function(conditions, options) {
3392-
if (typeof conditions === 'function' ||
3393+
Query.prototype.findOneAndDelete = function(filter, options) {
3394+
if (typeof filter === 'function' ||
33933395
typeof options === 'function' ||
33943396
typeof arguments[2] === 'function') {
33953397
throw new MongooseError('Query.prototype.findOneAndDelete() no longer accepts a callback');
@@ -3399,8 +3401,8 @@ Query.prototype.findOneAndDelete = function(conditions, options) {
33993401
this._validateOp();
34003402
this._validate();
34013403

3402-
if (mquery.canMerge(conditions)) {
3403-
this.merge(conditions);
3404+
if (mquery.canMerge(filter)) {
3405+
this.merge(filter);
34043406
}
34053407

34063408
options && this.setOptions(options);
@@ -3512,6 +3514,10 @@ Query.prototype.findOneAndReplace = function(filter, replacement, options) {
35123514

35133515
if (mquery.canMerge(filter)) {
35143516
this.merge(filter);
3517+
} else if (filter != null) {
3518+
this.error(
3519+
new ObjectParameterError(filter, 'filter', 'findOneAndReplace')
3520+
);
35153521
}
35163522

35173523
if (replacement != null) {
@@ -3555,16 +3561,6 @@ Query.prototype._findOneAndReplace = async function _findOneAndReplace() {
35553561
const filter = this._conditions;
35563562
const options = this._optionsForExec();
35573563
convertNewToReturnDocument(options);
3558-
let fields = null;
3559-
3560-
this._applyPaths();
3561-
if (this._fields != null) {
3562-
options.projection = this._castFields(clone(this._fields));
3563-
fields = options.projection;
3564-
if (fields instanceof Error) {
3565-
throw fields;
3566-
}
3567-
}
35683564

35693565
const runValidators = _getOption(this, 'runValidators', false);
35703566
if (runValidators === false) {
@@ -4890,6 +4886,9 @@ Query.prototype._castFields = function _castFields(fields) {
48904886
*/
48914887

48924888
Query.prototype._applyPaths = function applyPaths() {
4889+
if (!this.model) {
4890+
return;
4891+
}
48934892
this._fields = this._fields || {};
48944893
helpers.applyPaths(this._fields, this.model.schema);
48954894

@@ -4948,9 +4947,6 @@ Query.prototype._applyPaths = function applyPaths() {
49484947
*/
49494948

49504949
Query.prototype.cursor = function cursor(opts) {
4951-
this._applyPaths();
4952-
this._fields = this._castFields(this._fields);
4953-
49544950
if (opts) {
49554951
this.setOptions(opts);
49564952
}

0 commit comments

Comments
 (0)