mongoose 8.6.0 → 8.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.umd.js +1 -1
- package/lib/cursor/changeStream.js +43 -9
- package/lib/document.js +7 -7
- package/lib/helpers/clone.js +0 -5
- package/lib/model.js +35 -14
- package/package.json +7 -7
- package/types/query.d.ts +1 -0
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const EventEmitter = require('events').EventEmitter;
|
|
8
|
+
const MongooseError = require('../error/mongooseError');
|
|
8
9
|
|
|
9
10
|
/*!
|
|
10
11
|
* ignore
|
|
@@ -25,6 +26,7 @@ class ChangeStream extends EventEmitter {
|
|
|
25
26
|
this.bindedEvents = false;
|
|
26
27
|
this.pipeline = pipeline;
|
|
27
28
|
this.options = options;
|
|
29
|
+
this.errored = false;
|
|
28
30
|
|
|
29
31
|
if (options && options.hydrate && !options.model) {
|
|
30
32
|
throw new Error(
|
|
@@ -33,19 +35,36 @@ class ChangeStream extends EventEmitter {
|
|
|
33
35
|
);
|
|
34
36
|
}
|
|
35
37
|
|
|
38
|
+
let syncError = null;
|
|
36
39
|
this.$driverChangeStreamPromise = new Promise((resolve, reject) => {
|
|
37
40
|
// This wrapper is necessary because of buffering.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
try {
|
|
42
|
+
changeStreamThunk((err, driverChangeStream) => {
|
|
43
|
+
if (err != null) {
|
|
44
|
+
this.errored = true;
|
|
45
|
+
this.emit('error', err);
|
|
46
|
+
return reject(err);
|
|
47
|
+
}
|
|
43
48
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
this.driverChangeStream = driverChangeStream;
|
|
50
|
+
this.emit('ready');
|
|
51
|
+
resolve();
|
|
52
|
+
});
|
|
53
|
+
} catch (err) {
|
|
54
|
+
syncError = err;
|
|
55
|
+
this.errored = true;
|
|
56
|
+
this.emit('error', err);
|
|
57
|
+
reject(err);
|
|
58
|
+
}
|
|
48
59
|
});
|
|
60
|
+
|
|
61
|
+
// Because a ChangeStream is an event emitter, there's no way to register an 'error' handler
|
|
62
|
+
// that catches errors which occur in the constructor, unless we force sync errors into async
|
|
63
|
+
// errors with setImmediate(). For cleaner stack trace, we just immediately throw any synchronous
|
|
64
|
+
// errors that occurred with changeStreamThunk().
|
|
65
|
+
if (syncError != null) {
|
|
66
|
+
throw syncError;
|
|
67
|
+
}
|
|
49
68
|
}
|
|
50
69
|
|
|
51
70
|
_bindEvents() {
|
|
@@ -92,10 +111,16 @@ class ChangeStream extends EventEmitter {
|
|
|
92
111
|
}
|
|
93
112
|
|
|
94
113
|
hasNext(cb) {
|
|
114
|
+
if (this.errored) {
|
|
115
|
+
throw new MongooseError('Cannot call hasNext() on errored ChangeStream');
|
|
116
|
+
}
|
|
95
117
|
return this.driverChangeStream.hasNext(cb);
|
|
96
118
|
}
|
|
97
119
|
|
|
98
120
|
next(cb) {
|
|
121
|
+
if (this.errored) {
|
|
122
|
+
throw new MongooseError('Cannot call next() on errored ChangeStream');
|
|
123
|
+
}
|
|
99
124
|
if (this.options && this.options.hydrate) {
|
|
100
125
|
if (cb != null) {
|
|
101
126
|
const originalCb = cb;
|
|
@@ -126,16 +151,25 @@ class ChangeStream extends EventEmitter {
|
|
|
126
151
|
}
|
|
127
152
|
|
|
128
153
|
addListener(event, handler) {
|
|
154
|
+
if (this.errored) {
|
|
155
|
+
throw new MongooseError('Cannot call addListener() on errored ChangeStream');
|
|
156
|
+
}
|
|
129
157
|
this._bindEvents();
|
|
130
158
|
return super.addListener(event, handler);
|
|
131
159
|
}
|
|
132
160
|
|
|
133
161
|
on(event, handler) {
|
|
162
|
+
if (this.errored) {
|
|
163
|
+
throw new MongooseError('Cannot call on() on errored ChangeStream');
|
|
164
|
+
}
|
|
134
165
|
this._bindEvents();
|
|
135
166
|
return super.on(event, handler);
|
|
136
167
|
}
|
|
137
168
|
|
|
138
169
|
once(event, handler) {
|
|
170
|
+
if (this.errored) {
|
|
171
|
+
throw new MongooseError('Cannot call once() on errored ChangeStream');
|
|
172
|
+
}
|
|
139
173
|
this._bindEvents();
|
|
140
174
|
return super.once(event, handler);
|
|
141
175
|
}
|
package/lib/document.js
CHANGED
|
@@ -3856,7 +3856,6 @@ Document.prototype.$toObject = function(options, json) {
|
|
|
3856
3856
|
// Parent options should only bubble down for subdocuments, not populated docs
|
|
3857
3857
|
options._parentOptions = this.$isSubdocument ? options : null;
|
|
3858
3858
|
|
|
3859
|
-
options._skipSingleNestedGetters = false;
|
|
3860
3859
|
// remember the root transform function
|
|
3861
3860
|
// to save it from being overwritten by sub-transform functions
|
|
3862
3861
|
// const originalTransform = options.transform;
|
|
@@ -3870,13 +3869,13 @@ Document.prototype.$toObject = function(options, json) {
|
|
|
3870
3869
|
ret = clone(this._doc, options) || {};
|
|
3871
3870
|
}
|
|
3872
3871
|
|
|
3873
|
-
options._skipSingleNestedGetters = true;
|
|
3874
3872
|
const getters = options._calledWithOptions.getters
|
|
3875
3873
|
?? options.getters
|
|
3876
3874
|
?? defaultOptions.getters
|
|
3877
3875
|
?? false;
|
|
3876
|
+
|
|
3878
3877
|
if (getters) {
|
|
3879
|
-
applyGetters(this, ret
|
|
3878
|
+
applyGetters(this, ret);
|
|
3880
3879
|
|
|
3881
3880
|
if (options.minimize) {
|
|
3882
3881
|
ret = minimize(ret) || {};
|
|
@@ -4187,12 +4186,11 @@ function applyVirtuals(self, json, options, toObjectOptions) {
|
|
|
4187
4186
|
*
|
|
4188
4187
|
* @param {Document} self
|
|
4189
4188
|
* @param {Object} json
|
|
4190
|
-
* @param {Object} [options]
|
|
4191
4189
|
* @return {Object} `json`
|
|
4192
4190
|
* @api private
|
|
4193
4191
|
*/
|
|
4194
4192
|
|
|
4195
|
-
function applyGetters(self, json
|
|
4193
|
+
function applyGetters(self, json) {
|
|
4196
4194
|
const schema = self.$__schema;
|
|
4197
4195
|
const paths = Object.keys(schema.paths);
|
|
4198
4196
|
let i = paths.length;
|
|
@@ -4228,8 +4226,10 @@ function applyGetters(self, json, options) {
|
|
|
4228
4226
|
if (branch != null && typeof branch !== 'object') {
|
|
4229
4227
|
break;
|
|
4230
4228
|
} else if (ii === last) {
|
|
4231
|
-
|
|
4232
|
-
|
|
4229
|
+
branch[part] = schema.paths[path].applyGetters(
|
|
4230
|
+
branch[part],
|
|
4231
|
+
self
|
|
4232
|
+
);
|
|
4233
4233
|
if (Array.isArray(branch[part]) && schema.paths[path].$embeddedSchemaType) {
|
|
4234
4234
|
for (let i = 0; i < branch[part].length; ++i) {
|
|
4235
4235
|
branch[part][i] = schema.paths[path].$embeddedSchemaType.applyGetters(
|
package/lib/helpers/clone.js
CHANGED
|
@@ -40,11 +40,6 @@ function clone(obj, options, isArrayChild) {
|
|
|
40
40
|
|
|
41
41
|
if (isMongooseObject(obj)) {
|
|
42
42
|
if (options) {
|
|
43
|
-
// Single nested subdocs should apply getters later in `applyGetters()`
|
|
44
|
-
// when calling `toObject()`. See gh-7442, gh-8295
|
|
45
|
-
if (options._skipSingleNestedGetters && obj.$isSingleNested) {
|
|
46
|
-
options._calledWithOptions = Object.assign({}, options._calledWithOptions || {}, { getters: false });
|
|
47
|
-
}
|
|
48
43
|
if (options.retainDocuments && obj.$__ != null) {
|
|
49
44
|
const clonedDoc = obj.$clone();
|
|
50
45
|
if (obj.__index != null) {
|
package/lib/model.js
CHANGED
|
@@ -3368,11 +3368,19 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
|
|
|
3368
3368
|
};
|
|
3369
3369
|
|
|
3370
3370
|
/**
|
|
3371
|
-
*
|
|
3372
|
-
*
|
|
3371
|
+
* Takes an array of documents, gets the changes and inserts/updates documents in the database
|
|
3372
|
+
* according to whether or not the document is new, or whether it has changes or not.
|
|
3373
3373
|
*
|
|
3374
3374
|
* `bulkSave` uses `bulkWrite` under the hood, so it's mostly useful when dealing with many documents (10K+)
|
|
3375
3375
|
*
|
|
3376
|
+
* `bulkSave()` throws errors under the following conditions:
|
|
3377
|
+
*
|
|
3378
|
+
* - one of the provided documents fails validation. In this case, `bulkSave()` does not send a `bulkWrite()`, and throws the first validation error.
|
|
3379
|
+
* - `bulkWrite()` fails (for example, due to being unable to connect to MongoDB or due to duplicate key error)
|
|
3380
|
+
* - `bulkWrite()` did not insert or update **any** documents. In this case, `bulkSave()` will throw a DocumentNotFound error.
|
|
3381
|
+
*
|
|
3382
|
+
* Note that `bulkSave()` will **not** throw an error if only some of the `save()` calls succeeded.
|
|
3383
|
+
*
|
|
3376
3384
|
* @param {Array<Document>} documents
|
|
3377
3385
|
* @param {Object} [options] options passed to the underlying `bulkWrite()`
|
|
3378
3386
|
* @param {Boolean} [options.timestamps] defaults to `null`, when set to false, mongoose will not add/update timestamps to the documents.
|
|
@@ -3380,7 +3388,7 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
|
|
|
3380
3388
|
* @param {String|number} [options.w=1] The [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/). See [`Query#w()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.w()) for more information.
|
|
3381
3389
|
* @param {number} [options.wtimeout=null] The [write concern timeout](https://www.mongodb.com/docs/manual/reference/write-concern/#wtimeout).
|
|
3382
3390
|
* @param {Boolean} [options.j=true] If false, disable [journal acknowledgement](https://www.mongodb.com/docs/manual/reference/write-concern/#j-option)
|
|
3383
|
-
*
|
|
3391
|
+
* @return {BulkWriteResult} the return value from `bulkWrite()`
|
|
3384
3392
|
*/
|
|
3385
3393
|
Model.bulkSave = async function bulkSave(documents, options) {
|
|
3386
3394
|
options = options || {};
|
|
@@ -3408,18 +3416,31 @@ Model.bulkSave = async function bulkSave(documents, options) {
|
|
|
3408
3416
|
(err) => ({ bulkWriteResult: null, bulkWriteError: err })
|
|
3409
3417
|
);
|
|
3410
3418
|
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3419
|
+
const matchedCount = bulkWriteResult?.matchedCount ?? 0;
|
|
3420
|
+
const insertedCount = bulkWriteResult?.insertedCount ?? 0;
|
|
3421
|
+
if (writeOperations.length > 0 && matchedCount + insertedCount === 0 && !bulkWriteError) {
|
|
3422
|
+
throw new DocumentNotFoundError(
|
|
3423
|
+
writeOperations.filter(op => op.updateOne).map(op => op.updateOne.filter),
|
|
3424
|
+
this.modelName,
|
|
3425
|
+
writeOperations.length,
|
|
3426
|
+
bulkWriteResult
|
|
3427
|
+
);
|
|
3428
|
+
}
|
|
3417
3429
|
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3430
|
+
const successfulDocuments = [];
|
|
3431
|
+
for (let i = 0; i < documents.length; i++) {
|
|
3432
|
+
const document = documents[i];
|
|
3433
|
+
const documentError = bulkWriteError && bulkWriteError.writeErrors.find(writeError => {
|
|
3434
|
+
const writeErrorDocumentId = writeError.err.op._id || writeError.err.op.q._id;
|
|
3435
|
+
return writeErrorDocumentId.toString() === document._doc._id.toString();
|
|
3436
|
+
});
|
|
3437
|
+
|
|
3438
|
+
if (documentError == null) {
|
|
3439
|
+
successfulDocuments.push(document);
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
|
|
3443
|
+
await Promise.all(successfulDocuments.map(document => handleSuccessfulWrite(document)));
|
|
3423
3444
|
|
|
3424
3445
|
if (bulkWriteError && bulkWriteError.writeErrors && bulkWriteError.writeErrors.length) {
|
|
3425
3446
|
throw bulkWriteError;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mongoose",
|
|
3
3
|
"description": "Mongoose MongoDB ODM",
|
|
4
|
-
"version": "8.6.
|
|
4
|
+
"version": "8.6.1",
|
|
5
5
|
"author": "Guillermo Rauch <guillermo@learnboost.com>",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"mongodb",
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@babel/core": "7.24.7",
|
|
32
|
-
"@babel/preset-env": "7.25.
|
|
33
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
34
|
-
"@typescript-eslint/parser": "^
|
|
32
|
+
"@babel/preset-env": "7.25.4",
|
|
33
|
+
"@typescript-eslint/eslint-plugin": "^8.4.0",
|
|
34
|
+
"@typescript-eslint/parser": "^8.4.0",
|
|
35
35
|
"acquit": "1.3.0",
|
|
36
36
|
"acquit-ignore": "0.2.1",
|
|
37
37
|
"acquit-require": "0.1.1",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"babel-loader": "8.2.5",
|
|
41
41
|
"broken-link-checker": "^0.7.8",
|
|
42
42
|
"buffer": "^5.6.0",
|
|
43
|
-
"cheerio": "1.0.0
|
|
43
|
+
"cheerio": "1.0.0",
|
|
44
44
|
"crypto-browserify": "3.12.0",
|
|
45
45
|
"dotenv": "16.4.5",
|
|
46
46
|
"dox": "1.0.0",
|
|
@@ -53,9 +53,9 @@
|
|
|
53
53
|
"lodash.isequal": "4.5.0",
|
|
54
54
|
"lodash.isequalwith": "4.4.0",
|
|
55
55
|
"markdownlint-cli2": "^0.13.0",
|
|
56
|
-
"marked": "14.
|
|
56
|
+
"marked": "14.1.0",
|
|
57
57
|
"mkdirp": "^3.0.1",
|
|
58
|
-
"mocha": "10.7.
|
|
58
|
+
"mocha": "10.7.3",
|
|
59
59
|
"moment": "2.30.1",
|
|
60
60
|
"mongodb-memory-server": "10.0.0",
|
|
61
61
|
"ncp": "^2.0.0",
|
package/types/query.d.ts
CHANGED
|
@@ -116,6 +116,7 @@ declare module 'mongoose' {
|
|
|
116
116
|
$where?: string | Function;
|
|
117
117
|
/** @see https://www.mongodb.com/docs/manual/reference/operator/query/comment/#op._S_comment */
|
|
118
118
|
$comment?: string;
|
|
119
|
+
$expr?: Record<string, any>;
|
|
119
120
|
// we could not find a proper TypeScript generic to support nested queries e.g. 'user.friends.name'
|
|
120
121
|
// this will mark all unrecognized properties as any (including nested queries) only if
|
|
121
122
|
// they include a "." (to avoid generically allowing any unexpected keys)
|