mongoose 9.11.0 → 9.11.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/lib/cast.js CHANGED
@@ -11,6 +11,7 @@ const cast$expr = require('./helpers/query/cast$expr');
11
11
  const castString = require('./cast/string');
12
12
  const castTextSearch = require('./schema/operators/text');
13
13
  const get = require('./helpers/get');
14
+ const getDiscriminatorValueFromFilter = require('./helpers/discriminator/getDiscriminatorValueFromFilter');
14
15
  const getSchemaDiscriminatorByValue = require('./helpers/discriminator/getSchemaDiscriminatorByValue');
15
16
  const isOperator = require('./helpers/query/isOperator');
16
17
  const util = require('util');
@@ -43,7 +44,8 @@ module.exports = function cast(schema, obj, options, context) {
43
44
  }
44
45
 
45
46
  if (schema?.discriminators != null && obj[schema.options.discriminatorKey] != null) {
46
- schema = getSchemaDiscriminatorByValue(schema, obj[schema.options.discriminatorKey]) || schema;
47
+ const discriminatorValue = getDiscriminatorValueFromFilter(obj[schema.options.discriminatorKey]);
48
+ schema = getSchemaDiscriminatorByValue(schema, discriminatorValue) || schema;
47
49
  }
48
50
 
49
51
  const paths = Object.keys(obj);
@@ -134,18 +136,10 @@ module.exports = function cast(schema, obj, options, context) {
134
136
  if (_schematype?.schema?.discriminators != null &&
135
137
  discriminatorKey != null &&
136
138
  pathLastHalf !== discriminatorKey) {
137
- const discriminatorVal = get(obj, pathFirstHalf + '.' + discriminatorKey);
139
+ const discriminatorVal = getDiscriminatorValueFromFilter(get(obj, pathFirstHalf + '.' + discriminatorKey));
138
140
  const discriminators = _schematype.schema.discriminators;
139
141
  if (typeof discriminatorVal === 'string' && discriminators[discriminatorVal] != null) {
140
-
141
142
  schematype = discriminators[discriminatorVal].path(pathLastHalf);
142
- } else if (discriminatorVal != null &&
143
- Object.keys(discriminatorVal).length === 1 &&
144
- Array.isArray(discriminatorVal.$in) &&
145
- discriminatorVal.$in.length === 1 &&
146
- typeof discriminatorVal.$in[0] === 'string' &&
147
- discriminators[discriminatorVal.$in[0]] != null) {
148
- schematype = discriminators[discriminatorVal.$in[0]].path(pathLastHalf);
149
143
  }
150
144
  }
151
145
  }
@@ -329,15 +323,11 @@ module.exports = function cast(schema, obj, options, context) {
329
323
  if (nested && schematype?.schema != null) {
330
324
  cast(schematype.schema, nested, options, context);
331
325
  } else if (nested && schematype?.$isMongooseArray) {
332
- if (utils.isPOJO(nested) && nested.$not != null) {
333
- cast(schema, nested, options, context);
334
- } else {
335
- val[$cond] = schematype.castForQuery(
336
- $cond,
337
- nested,
338
- context
339
- );
340
- }
326
+ val[$cond] = schematype.castForQuery(
327
+ $cond,
328
+ nested,
329
+ context
330
+ );
341
331
  }
342
332
  } else if ($cond === '$not') {
343
333
  if (nested && schematype) {
@@ -170,6 +170,9 @@ QueryCursor.prototype.getDriverCursor = async function getDriverCursor() {
170
170
  if (this.cursor) {
171
171
  return this.cursor;
172
172
  }
173
+ if (this._error) {
174
+ throw this._error;
175
+ }
173
176
 
174
177
  await once(this, 'cursor');
175
178
  return this.cursor;
@@ -278,7 +281,7 @@ QueryCursor.prototype._destroy = function _destroy(_err, callback) {
278
281
 
279
282
  waitForCursor
280
283
  .then(() => {
281
- this.cursor.close();
284
+ return this.cursor.close();
282
285
  })
283
286
  .then(() => {
284
287
  this._closed = true;
package/lib/document.js CHANGED
@@ -63,6 +63,7 @@ const scopeSymbol = require('./helpers/symbols').scopeSymbol;
63
63
  const schemaMixedSymbol = require('./schema/symbols').schemaMixedSymbol;
64
64
  const getDeepestSubdocumentForPath = require('./helpers/document/getDeepestSubdocumentForPath');
65
65
  const sessionNewDocuments = require('./helpers/symbols').sessionNewDocuments;
66
+ const toObjectRecursive = require('./helpers/document/toObjectRecursive');
66
67
 
67
68
  let DocumentArray;
68
69
  let Embedded;
@@ -1253,9 +1254,7 @@ Document.prototype.$set = function $set(path, val, type, options) {
1253
1254
 
1254
1255
  if (pathType === 'nested' && val) {
1255
1256
  if (typeof val === 'object' && val != null) {
1256
- if (val.$__ != null) {
1257
- val = val.toObject(internalToObjectOptions);
1258
- }
1257
+ val = toObjectRecursive(val, internalToObjectOptions);
1259
1258
  if (val == null) {
1260
1259
  this.invalidate(path, new MongooseError.CastError('Object', val, path));
1261
1260
  return this;
@@ -4265,9 +4264,11 @@ Document.prototype.$toObject = function(options, json) {
4265
4264
  ?? false;
4266
4265
 
4267
4266
  let ret;
4268
- if (hasOnlyPrimitiveValues && !options.flattenObjectIds) {
4267
+ if (hasOnlyPrimitiveValues && !options.flattenObjectIds && !options.flattenDecimals) {
4269
4268
  // Fast path: if we don't have any nested objects or arrays, we only need a
4270
- // shallow clone.
4269
+ // shallow clone. Skipped for `flattenObjectIds` and `flattenDecimals`, because
4270
+ // `$__hasOnlyPrimitiveValues()` counts ObjectIds and Decimal128s as primitives
4271
+ // and only the full `clone()` path converts them.
4271
4272
  ret = this.$__toObjectShallow(schemaFieldsOnly);
4272
4273
  } else if (schemaFieldsOnly) {
4273
4274
  ret = {};
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+
3
+ const isPOJO = require('../isPOJO');
4
+
5
+ /**
6
+ * Returns the single discriminator value that a query filter condition selects,
7
+ * like `'Clicked'` for `'Clicked'`, `{ $eq: 'Clicked' }`, or `{ $in: ['Clicked'] }`.
8
+ * Returns the condition unchanged if it does not select exactly one value.
9
+ *
10
+ * @param {any} value the filter condition on the discriminator key
11
+ * @return {any}
12
+ * @api private
13
+ */
14
+
15
+ module.exports = function getDiscriminatorValueFromFilter(value) {
16
+ if (!isPOJO(value)) {
17
+ return value;
18
+ }
19
+ const keys = Object.keys(value);
20
+ if (keys.length !== 1) {
21
+ return value;
22
+ }
23
+ if (keys[0] === '$eq') {
24
+ return value.$eq;
25
+ }
26
+ if (keys[0] === '$in' && Array.isArray(value.$in) && value.$in.length === 1) {
27
+ return value.$in[0];
28
+ }
29
+ return value;
30
+ };
@@ -0,0 +1,49 @@
1
+ 'use strict';
2
+
3
+ const isPOJO = require('../isPOJO');
4
+
5
+ /**
6
+ * Convert nested path documents underneath POJOs and arrays to POJOs without
7
+ * modifying the original value. Only clone containers that contain a nested
8
+ * path document. Preserve regular document children so casting can handle
9
+ * populated documents and subdocuments.
10
+ */
11
+
12
+ module.exports = function toObjectRecursive(value, options) {
13
+ return _toObjectRecursive(value, options, true, new Set());
14
+ };
15
+
16
+ function _toObjectRecursive(value, options, isRoot, seen) {
17
+ if (value == null) {
18
+ return value;
19
+ }
20
+
21
+ if (value.$__ != null && typeof value.toObject === 'function') {
22
+ return isRoot || value.$__isNested ? value.toObject(options) : value;
23
+ }
24
+
25
+ if (!Array.isArray(value) && !isPOJO(value)) {
26
+ return value;
27
+ }
28
+
29
+ if (seen.has(value)) {
30
+ return value;
31
+ }
32
+ seen.add(value);
33
+
34
+ let ret = value;
35
+ for (const key of Object.keys(value)) {
36
+ const converted = _toObjectRecursive(value[key], options, false, seen);
37
+ if (converted === value[key]) {
38
+ continue;
39
+ }
40
+
41
+ if (ret === value) {
42
+ ret = Array.isArray(value) ? Object.assign([], value) : { ...value };
43
+ }
44
+ ret[key] = converted;
45
+ }
46
+
47
+ seen.delete(value);
48
+ return ret;
49
+ }
@@ -3,6 +3,7 @@
3
3
  const cleanPositionalOperators = require('../schema/cleanPositionalOperators');
4
4
  const get = require('../get');
5
5
  const getDiscriminatorByValue = require('../discriminator/getDiscriminatorByValue');
6
+ const getDiscriminatorValueFromFilter = require('../discriminator/getDiscriminatorValueFromFilter');
6
7
  const updatedPathsByArrayFilter = require('../update/updatedPathsByArrayFilter');
7
8
 
8
9
  /**
@@ -46,16 +47,16 @@ module.exports = function getEmbeddedDiscriminatorPath(schema, update, filter, p
46
47
  let discriminatorKey = null;
47
48
 
48
49
  if (discriminatorValuePath in filter) {
49
- discriminatorKey = filter[discriminatorValuePath];
50
+ discriminatorKey = getDiscriminatorValueFromFilter(filter[discriminatorValuePath]);
50
51
  }
51
52
  if (discriminatorFilterPath in filter) {
52
- discriminatorKey = filter[discriminatorFilterPath];
53
+ discriminatorKey = getDiscriminatorValueFromFilter(filter[discriminatorFilterPath]);
53
54
  }
54
55
 
55
56
  const wrapperPath = subpath.replace(/\.\d+$/, '');
56
57
  if (schematype.$isMongooseDocumentArrayElement &&
57
58
  get(filter[wrapperPath], '$elemMatch.' + key) != null) {
58
- discriminatorKey = filter[wrapperPath].$elemMatch[key];
59
+ discriminatorKey = getDiscriminatorValueFromFilter(filter[wrapperPath].$elemMatch[key]);
59
60
  }
60
61
 
61
62
  const discriminatorKeyUpdatePath = originalSubpath + '.' + key;
@@ -92,7 +92,7 @@ function _castArrayFilters(arrayFilters, schema, strictQuery, updatedPathsByFilt
92
92
 
93
93
  if (schematype == null) {
94
94
  if (!strictQuery) {
95
- return;
95
+ continue;
96
96
  }
97
97
  const filterPath = filterPathRelativeToBase == null ?
98
98
  baseFilterPath + '.0' :
@@ -611,6 +611,19 @@ function cast$elemMatch(val, context) {
611
611
  for (let i = 0; i < numKeys; ++i) {
612
612
  const key = keys[i];
613
613
  const value = val[key];
614
+ if (key === '$not') {
615
+ // `$not` wraps other operators, like `{ $not: { $gt: 5 } }` or
616
+ // `{ $not: { $elemMatch: ... } }`, so cast each wrapped operator.
617
+ // A RegExp is left as-is.
618
+ if (utils.isPOJO(value)) {
619
+ for (const notKey of Object.keys(value)) {
620
+ if (isOperator(notKey) && value[notKey] != null) {
621
+ value[notKey] = this.castForQuery(notKey, value[notKey], context);
622
+ }
623
+ }
624
+ }
625
+ continue;
626
+ }
614
627
  if (isOperator(key) && value != null) {
615
628
  val[key] = this.castForQuery(key, value, context);
616
629
  }
@@ -484,10 +484,10 @@ SchemaDocumentArray.prototype.cast = function(value, doc, init, prev, options) {
484
484
  }
485
485
 
486
486
  subdoc = new Constructor(null, value, initDocumentOptions, selected, i);
487
- // Don't pass `path` to $init - it's only for this DocumentArray itself, not its element fields.
488
- // Element subdocuments use relative paths internally for change tracking.
489
- if (options.path != null) {
490
- const { path, ...initOptions } = options;
487
+ // Don't pass `path` or `arrayPathIndex` to $init - they're only for this DocumentArray itself,
488
+ // not its element fields. Element subdocuments use relative paths internally for change tracking.
489
+ if (options.path != null || options.arrayPathIndex != null) {
490
+ const { path, arrayPathIndex, ...initOptions } = options;
491
491
  options = initOptions;
492
492
  }
493
493
  rawArray[i] = subdoc.$init(rawArray[i], options);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "9.11.0",
4
+ "version": "9.11.1",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",
package/types/index.d.ts CHANGED
@@ -738,7 +738,7 @@ declare module 'mongoose' {
738
738
  static ObjectId: typeof Schema.Types.ObjectId;
739
739
  }
740
740
 
741
- export type NumberSchemaDefinition = typeof Number | 'number' | 'Number' | typeof Schema.Types.Number | Schema.Types.Number;
741
+ export type NumberSchemaDefinition = typeof Number | 'number' | 'Number' | typeof Schema.Types.Number | Schema.Types.Number | 'int32' | 'Int32' | typeof Schema.Types.Int32 | Schema.Types.Int32;
742
742
  export type StringSchemaDefinition = typeof String | 'string' | 'String' | typeof Schema.Types.String | Schema.Types.String;
743
743
  export type BooleanSchemaDefinition = typeof Boolean | 'boolean' | 'Boolean' | typeof Schema.Types.Boolean | Schema.Types.Boolean;
744
744
  export type DateSchemaDefinition = DateConstructor | 'date' | 'Date' | typeof Schema.Types.Date | Schema.Types.Date;
package/types/models.d.ts CHANGED
@@ -158,13 +158,19 @@ declare module 'mongoose' {
158
158
  MongooseBulkUpdatePerOperationOptions;
159
159
 
160
160
  export type UpdateOneModel<TSchema extends mongodb.Document = mongodb.Document> =
161
- Omit<mongodb.UpdateOneModel<TSchema>, 'filter'> &
162
- { filter: QueryFilter<TSchema> } &
161
+ Omit<mongodb.UpdateOneModel<TSchema>, 'filter' | 'update'> &
162
+ {
163
+ filter: QueryFilter<TSchema>;
164
+ update: UpdateQuery<TSchema> | UpdateWithAggregationPipeline;
165
+ } &
163
166
  MongooseBulkUpdatePerOperationOptions;
164
167
 
165
168
  export type UpdateManyModel<TSchema extends mongodb.Document = mongodb.Document> =
166
- Omit<mongodb.UpdateManyModel<TSchema>, 'filter'> &
167
- { filter: QueryFilter<TSchema> } &
169
+ Omit<mongodb.UpdateManyModel<TSchema>, 'filter' | 'update'> &
170
+ {
171
+ filter: QueryFilter<TSchema>;
172
+ update: UpdateQuery<TSchema> | UpdateWithAggregationPipeline;
173
+ } &
168
174
  MongooseBulkUpdatePerOperationOptions;
169
175
 
170
176
  export type DeleteOneModel<TSchema extends mongodb.Document = mongodb.Document> =