mongoose 9.9.1 → 9.9.3
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/double.js +6 -0
- package/lib/cast/int32.js +6 -0
- package/lib/connection.js +3 -0
- package/lib/document.js +91 -86
- package/lib/helpers/pathTrie.js +167 -0
- package/lib/internal.js +2 -0
- package/lib/model.js +9 -0
- package/lib/plugins/trackTransaction.js +25 -10
- package/lib/query.js +8 -7
- package/lib/schema/number.js +1 -1
- package/lib/schema/string.js +1 -1
- package/lib/schema.js +7 -14
- package/lib/schemaType.js +21 -0
- package/lib/utils.js +5 -15
- package/package.json +11 -11
- package/types/document.d.ts +4 -4
- package/types/index.d.ts +25 -0
- package/types/schematypes.d.ts +1 -1
package/lib/cast/double.js
CHANGED
|
@@ -30,6 +30,12 @@ module.exports = function castDouble(val) {
|
|
|
30
30
|
assert.ok(false);
|
|
31
31
|
}
|
|
32
32
|
} else if (typeof val === 'object') {
|
|
33
|
+
if (Array.isArray(val)) {
|
|
34
|
+
// `[5].valueOf()` returns the array itself, so without this guard a
|
|
35
|
+
// single-element or empty array would fall through to `Number(tempVal)`
|
|
36
|
+
// below and silently coerce instead of throwing.
|
|
37
|
+
assert.ok(false);
|
|
38
|
+
}
|
|
33
39
|
const tempVal = val.valueOf() ?? val.toString();
|
|
34
40
|
// ex: { a: 'im an object, valueOf: () => 'helloworld' } // throw an error
|
|
35
41
|
if (typeof tempVal === 'string') {
|
package/lib/cast/int32.js
CHANGED
|
@@ -20,6 +20,12 @@ module.exports = function castInt32(val) {
|
|
|
20
20
|
if (val === '') {
|
|
21
21
|
return null;
|
|
22
22
|
}
|
|
23
|
+
if (Array.isArray(val)) {
|
|
24
|
+
// `Number([5])` is `5` and `Number([])` is `0`, so without this guard a
|
|
25
|
+
// single-element or empty array would silently coerce instead of throwing,
|
|
26
|
+
// unlike every other array input and unlike `castNumber()`'s own guard.
|
|
27
|
+
assert.ok(false);
|
|
28
|
+
}
|
|
23
29
|
|
|
24
30
|
const coercedVal = isBsonType(val, 'Long') ? val.toNumber() : Number(val);
|
|
25
31
|
|
package/lib/connection.js
CHANGED
|
@@ -778,6 +778,9 @@ function _resetSessionDocuments(session) {
|
|
|
778
778
|
if (Object.hasOwn(state, 'versionKey')) {
|
|
779
779
|
doc.set(doc.schema.options.versionKey, state.versionKey);
|
|
780
780
|
}
|
|
781
|
+
if (Object.hasOwn(state, 'isDeleted')) {
|
|
782
|
+
doc.$isDeleted(state.isDeleted);
|
|
783
|
+
}
|
|
781
784
|
|
|
782
785
|
for (const path of state.modifiedPaths) {
|
|
783
786
|
doc.$__.activePaths.modify(path);
|
package/lib/document.js
CHANGED
|
@@ -14,6 +14,7 @@ const ModifiedPathsSnapshot = require('./modifiedPathsSnapshot');
|
|
|
14
14
|
const ObjectExpectedError = require('./error/objectExpected');
|
|
15
15
|
const ObjectParameterError = require('./error/objectParameter');
|
|
16
16
|
const ParallelValidateError = require('./error/parallelValidate');
|
|
17
|
+
const PathTrie = require('./helpers/pathTrie');
|
|
17
18
|
const Schema = require('./schema');
|
|
18
19
|
const StrictModeError = require('./error/strict');
|
|
19
20
|
const ValidationError = require('./error/validation');
|
|
@@ -31,7 +32,6 @@ const getEmbeddedDiscriminatorPath = require('./helpers/document/getEmbeddedDisc
|
|
|
31
32
|
const getKeysInSchemaOrder = require('./helpers/schema/getKeysInSchemaOrder');
|
|
32
33
|
const getSubdocumentStrictValue = require('./helpers/schema/getSubdocumentStrictValue');
|
|
33
34
|
const handleSpreadDoc = require('./helpers/document/handleSpreadDoc');
|
|
34
|
-
const isBsonType = require('./helpers/isBsonType');
|
|
35
35
|
const isDefiningProjection = require('./helpers/projection/isDefiningProjection');
|
|
36
36
|
const isExclusive = require('./helpers/projection/isExclusive');
|
|
37
37
|
const isPathExcluded = require('./helpers/projection/isPathExcluded');
|
|
@@ -2570,25 +2570,15 @@ Document.prototype.isSelected = function isSelected(path) {
|
|
|
2570
2570
|
return path.some(p => this.$__isSelected(p));
|
|
2571
2571
|
}
|
|
2572
2572
|
|
|
2573
|
-
const
|
|
2574
|
-
|
|
2573
|
+
const index = _getProjectionIndex(this);
|
|
2574
|
+
const paths = index.paths;
|
|
2575
|
+
const inclusive = index.inclusive;
|
|
2575
2576
|
|
|
2576
|
-
if (
|
|
2577
|
+
if (index.onlyId) {
|
|
2577
2578
|
// only _id was selected.
|
|
2578
2579
|
return this.$__.selected._id === 0;
|
|
2579
2580
|
}
|
|
2580
2581
|
|
|
2581
|
-
for (const cur of paths) {
|
|
2582
|
-
if (cur === '_id') {
|
|
2583
|
-
continue;
|
|
2584
|
-
}
|
|
2585
|
-
if (!isDefiningProjection(this.$__.selected[cur])) {
|
|
2586
|
-
continue;
|
|
2587
|
-
}
|
|
2588
|
-
inclusive = !!this.$__.selected[cur];
|
|
2589
|
-
break;
|
|
2590
|
-
}
|
|
2591
|
-
|
|
2592
2582
|
if (inclusive === null) {
|
|
2593
2583
|
return true;
|
|
2594
2584
|
}
|
|
@@ -2599,6 +2589,25 @@ Document.prototype.isSelected = function isSelected(path) {
|
|
|
2599
2589
|
|
|
2600
2590
|
const pathHasDot = path.indexOf('.') !== -1;
|
|
2601
2591
|
|
|
2592
|
+
if (!index.hasNestedKey) {
|
|
2593
|
+
// No projection key is nested, so no key can start with `path + '.'` and
|
|
2594
|
+
// only the "is an ancestor of `path` projected?" check below can match.
|
|
2595
|
+
// Every ancestor that matches returns the same value, so we can walk
|
|
2596
|
+
// `path`'s ancestors instead of scanning every projection key. This keeps
|
|
2597
|
+
// `isSelected()` O(depth of path) rather than O(number of projected paths),
|
|
2598
|
+
// which matters because `toObject({ getters: true })` calls this once per
|
|
2599
|
+
// schema path. Re: gh-16373
|
|
2600
|
+
if (pathHasDot) {
|
|
2601
|
+
for (let dot = path.indexOf('.'); dot !== -1; dot = path.indexOf('.', dot + 1)) {
|
|
2602
|
+
const ancestor = path.slice(0, dot);
|
|
2603
|
+
if (ancestor !== '_id' && Object.hasOwn(this.$__.selected, ancestor)) {
|
|
2604
|
+
return inclusive;
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
return !inclusive;
|
|
2609
|
+
}
|
|
2610
|
+
|
|
2602
2611
|
for (const cur of paths) {
|
|
2603
2612
|
if (cur === '_id') {
|
|
2604
2613
|
continue;
|
|
@@ -2615,6 +2624,43 @@ Document.prototype.isSelected = function isSelected(path) {
|
|
|
2615
2624
|
return !inclusive;
|
|
2616
2625
|
};
|
|
2617
2626
|
|
|
2627
|
+
/*!
|
|
2628
|
+
* Computes and caches the derived properties of `doc.$__.selected` that
|
|
2629
|
+
* `isSelected()` and `isDirectSelected()` would otherwise recompute on every
|
|
2630
|
+
* call. `$__.selected` is assigned once in the Document constructor and never
|
|
2631
|
+
* mutated afterwards, so this can safely be cached for the document's lifetime.
|
|
2632
|
+
*/
|
|
2633
|
+
|
|
2634
|
+
function _getProjectionIndex(doc) {
|
|
2635
|
+
if (doc.$__.selectedIndex !== undefined) {
|
|
2636
|
+
return doc.$__.selectedIndex;
|
|
2637
|
+
}
|
|
2638
|
+
|
|
2639
|
+
const selected = doc.$__.selected;
|
|
2640
|
+
const paths = Object.keys(selected);
|
|
2641
|
+
let inclusive = null;
|
|
2642
|
+
let hasNestedKey = false;
|
|
2643
|
+
|
|
2644
|
+
for (const cur of paths) {
|
|
2645
|
+
if (cur === '_id') {
|
|
2646
|
+
continue;
|
|
2647
|
+
}
|
|
2648
|
+
if (cur.indexOf('.') !== -1) {
|
|
2649
|
+
hasNestedKey = true;
|
|
2650
|
+
}
|
|
2651
|
+
if (inclusive === null && isDefiningProjection(selected[cur])) {
|
|
2652
|
+
inclusive = !!selected[cur];
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
|
|
2656
|
+
return (doc.$__.selectedIndex = {
|
|
2657
|
+
paths,
|
|
2658
|
+
inclusive,
|
|
2659
|
+
hasNestedKey,
|
|
2660
|
+
onlyId: paths.length === 1 && paths[0] === '_id'
|
|
2661
|
+
});
|
|
2662
|
+
}
|
|
2663
|
+
|
|
2618
2664
|
Document.prototype.$__isSelected = Document.prototype.isSelected;
|
|
2619
2665
|
|
|
2620
2666
|
/**
|
|
@@ -2650,34 +2696,22 @@ Document.prototype.isDirectSelected = function isDirectSelected(path) {
|
|
|
2650
2696
|
return path.some(p => this.isDirectSelected(p));
|
|
2651
2697
|
}
|
|
2652
2698
|
|
|
2653
|
-
const
|
|
2654
|
-
let inclusive = null;
|
|
2699
|
+
const index = _getProjectionIndex(this);
|
|
2655
2700
|
|
|
2656
|
-
if (
|
|
2701
|
+
if (index.onlyId) {
|
|
2657
2702
|
// only _id was selected.
|
|
2658
2703
|
return this.$__.selected._id === 0;
|
|
2659
2704
|
}
|
|
2660
2705
|
|
|
2661
|
-
|
|
2662
|
-
if (cur === '_id') {
|
|
2663
|
-
continue;
|
|
2664
|
-
}
|
|
2665
|
-
if (!isDefiningProjection(this.$__.selected[cur])) {
|
|
2666
|
-
continue;
|
|
2667
|
-
}
|
|
2668
|
-
inclusive = !!this.$__.selected[cur];
|
|
2669
|
-
break;
|
|
2670
|
-
}
|
|
2671
|
-
|
|
2672
|
-
if (inclusive === null) {
|
|
2706
|
+
if (index.inclusive === null) {
|
|
2673
2707
|
return true;
|
|
2674
2708
|
}
|
|
2675
2709
|
|
|
2676
2710
|
if (Object.hasOwn(this.$__.selected, path)) {
|
|
2677
|
-
return inclusive;
|
|
2711
|
+
return index.inclusive;
|
|
2678
2712
|
}
|
|
2679
2713
|
|
|
2680
|
-
return !inclusive;
|
|
2714
|
+
return !index.inclusive;
|
|
2681
2715
|
};
|
|
2682
2716
|
|
|
2683
2717
|
/**
|
|
@@ -5242,26 +5276,26 @@ Document.prototype.$__delta = function $__delta(pathsToSave, pathsToSaveSet) {
|
|
|
5242
5276
|
const optimisticConcurrency = this.$__schema.options.optimisticConcurrency;
|
|
5243
5277
|
if (optimisticConcurrency) {
|
|
5244
5278
|
if (Array.isArray(optimisticConcurrency)) {
|
|
5245
|
-
if (!this.$__schema.options.
|
|
5246
|
-
this.$__schema.options.
|
|
5279
|
+
if (!this.$__schema.options._optimisticConcurrencyTrie) {
|
|
5280
|
+
this.$__schema.options._optimisticConcurrencyTrie = new PathTrie(optimisticConcurrency);
|
|
5247
5281
|
}
|
|
5248
|
-
const
|
|
5282
|
+
const optimisticConcurrencyTrie = this.$__schema.options._optimisticConcurrencyTrie;
|
|
5249
5283
|
const modPaths = this.directModifiedPaths();
|
|
5250
5284
|
const hasRelevantModPaths = pathsToSave == null ?
|
|
5251
|
-
modPaths.find(path =>
|
|
5252
|
-
modPaths.find(path =>
|
|
5285
|
+
modPaths.find(path => optimisticConcurrencyTrie.overlapsPath(path.indexOf('.') === -1 ? path : path.split('.'))) :
|
|
5286
|
+
modPaths.find(path => optimisticConcurrencyTrie.overlapsPath(path.indexOf('.') === -1 ? path : path.split('.')) && isInPathsToSave(path, pathsToSaveSet, pathsToSave));
|
|
5253
5287
|
if (hasRelevantModPaths) {
|
|
5254
5288
|
this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE;
|
|
5255
5289
|
}
|
|
5256
5290
|
} else if (Array.isArray(optimisticConcurrency?.exclude)) {
|
|
5257
|
-
if (!this.$__schema.options.
|
|
5258
|
-
this.$__schema.options.
|
|
5291
|
+
if (!this.$__schema.options._optimisticConcurrencyExcludeTrie) {
|
|
5292
|
+
this.$__schema.options._optimisticConcurrencyExcludeTrie = new PathTrie(optimisticConcurrency.exclude);
|
|
5259
5293
|
}
|
|
5260
|
-
const
|
|
5294
|
+
const optimisticConcurrencyExcludeTrie = this.$__schema.options._optimisticConcurrencyExcludeTrie;
|
|
5261
5295
|
const modPaths = this.directModifiedPaths();
|
|
5262
5296
|
const hasRelevantModPaths = pathsToSave == null ?
|
|
5263
|
-
modPaths.find(path => !
|
|
5264
|
-
modPaths.find(path => !
|
|
5297
|
+
modPaths.find(path => !optimisticConcurrencyExcludeTrie.matchesPathOrAncestor(path.indexOf('.') === -1 ? path : path.split('.'))) :
|
|
5298
|
+
modPaths.find(path => !optimisticConcurrencyExcludeTrie.matchesPathOrAncestor(path.indexOf('.') === -1 ? path : path.split('.')) && isInPathsToSave(path, pathsToSaveSet, pathsToSave));
|
|
5265
5299
|
if (hasRelevantModPaths) {
|
|
5266
5300
|
this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE;
|
|
5267
5301
|
}
|
|
@@ -5755,11 +5789,21 @@ Document.prototype.$__hasOnlyPrimitiveValues = function $__hasOnlyPrimitiveValue
|
|
|
5755
5789
|
const doc = this._doc;
|
|
5756
5790
|
for (const key in doc) {
|
|
5757
5791
|
const v = doc[key];
|
|
5758
|
-
if (v == null
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5792
|
+
if (v == null || typeof v !== 'object') {
|
|
5793
|
+
continue;
|
|
5794
|
+
}
|
|
5795
|
+
// Equivalent to `isNativeObject(v) && !Array.isArray(v)`, plus the
|
|
5796
|
+
// `isBsonType()` checks below, but without the helper calls or the
|
|
5797
|
+
// repeated `Array.isArray()`. This runs for every key of every document
|
|
5798
|
+
// that `toObject()` touches, so the call overhead is measurable.
|
|
5799
|
+
if (Array.isArray(v)) {
|
|
5800
|
+
return false;
|
|
5801
|
+
}
|
|
5802
|
+
if (v instanceof Date || v instanceof Boolean || v instanceof Number || v instanceof String) {
|
|
5803
|
+
continue;
|
|
5804
|
+
}
|
|
5805
|
+
const bsontype = v._bsontype;
|
|
5806
|
+
if (bsontype === 'ObjectId' || bsontype === 'Decimal128') {
|
|
5763
5807
|
continue;
|
|
5764
5808
|
}
|
|
5765
5809
|
return false;
|
|
@@ -5787,45 +5831,6 @@ Document.prototype._applyVersionIncrement = function _applyVersionIncrement() {
|
|
|
5787
5831
|
* Module exports.
|
|
5788
5832
|
*/
|
|
5789
5833
|
|
|
5790
|
-
/*!
|
|
5791
|
-
* Check if `path` or any of its ancestor paths exist in `pathSet`.
|
|
5792
|
-
* For example:
|
|
5793
|
-
* _pathOrAncestorInSet('profile.firstName', Set(['profile'])) === true
|
|
5794
|
-
* _pathOrAncestorInSet('profile', Set(['profile.firstName'])) === false
|
|
5795
|
-
*/
|
|
5796
|
-
function _pathOrAncestorInSet(path, pathSet) {
|
|
5797
|
-
if (pathSet.has(path)) {
|
|
5798
|
-
return true;
|
|
5799
|
-
}
|
|
5800
|
-
let idx = path.indexOf('.');
|
|
5801
|
-
while (idx !== -1) {
|
|
5802
|
-
if (pathSet.has(path.substring(0, idx))) {
|
|
5803
|
-
return true;
|
|
5804
|
-
}
|
|
5805
|
-
idx = path.indexOf('.', idx + 1);
|
|
5806
|
-
}
|
|
5807
|
-
return false;
|
|
5808
|
-
}
|
|
5809
|
-
|
|
5810
|
-
/*!
|
|
5811
|
-
* Check if `path`, any of its ancestor paths, or any of its descendant paths
|
|
5812
|
-
* exist in `pathSet`.
|
|
5813
|
-
* For example:
|
|
5814
|
-
* _pathOverlapsSet('profile.firstName', Set(['profile'])) === true
|
|
5815
|
-
* _pathOverlapsSet('profile', Set(['profile.firstName'])) === true
|
|
5816
|
-
*/
|
|
5817
|
-
function _pathOverlapsSet(path, pathSet) {
|
|
5818
|
-
if (_pathOrAncestorInSet(path, pathSet)) {
|
|
5819
|
-
return true;
|
|
5820
|
-
}
|
|
5821
|
-
for (const p of pathSet) {
|
|
5822
|
-
if (p.length > path.length + 1 && p[path.length] === '.' && p.slice(0, path.length) === path) {
|
|
5823
|
-
return true;
|
|
5824
|
-
}
|
|
5825
|
-
}
|
|
5826
|
-
return false;
|
|
5827
|
-
}
|
|
5828
|
-
|
|
5829
5834
|
Document.VERSION_WHERE = VERSION_WHERE;
|
|
5830
5835
|
Document.VERSION_INC = VERSION_INC;
|
|
5831
5836
|
Document.VERSION_ALL = VERSION_ALL;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const wildcardSegment = '$*';
|
|
4
|
+
const numericSegmentRegex = /^\d+$/;
|
|
5
|
+
|
|
6
|
+
class PathTrieNode {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.children = new Map();
|
|
9
|
+
this.terminal = false;
|
|
10
|
+
this.hasWildcard = false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Trie of dotted paths for efficient path matching. Supports the map wildcard
|
|
16
|
+
* segment `$*`, which matches any single segment. For example, a trie
|
|
17
|
+
* containing `settings.$*` matches `settings.theme` but not `settings` or
|
|
18
|
+
* `profile.theme`. In array lookups, numeric segments like array indexes are
|
|
19
|
+
* ignorable: a trie containing `comments.text` matches `comments.0.text`.
|
|
20
|
+
* Single-segment string lookups skip the numeric check entirely.
|
|
21
|
+
*
|
|
22
|
+
* @api private
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
class PathTrie {
|
|
26
|
+
/**
|
|
27
|
+
* @param {string[]} [paths] paths to add to the trie
|
|
28
|
+
*/
|
|
29
|
+
constructor(paths) {
|
|
30
|
+
this.root = new PathTrieNode();
|
|
31
|
+
if (paths != null) {
|
|
32
|
+
for (const path of paths) {
|
|
33
|
+
this.add(path);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Add a dotted path, like `settings.$*` or `profile.firstName`, to the trie.
|
|
40
|
+
*
|
|
41
|
+
* @param {string} path
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
add(path) {
|
|
45
|
+
if (path.indexOf('.') === -1) {
|
|
46
|
+
this._addSegment(this.root, path).terminal = true;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
let node = this.root;
|
|
50
|
+
for (const segment of path.split('.')) {
|
|
51
|
+
node = this._addSegment(node, segment);
|
|
52
|
+
}
|
|
53
|
+
node.terminal = true;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @param {PathTrieNode} node
|
|
58
|
+
* @param {string} segment
|
|
59
|
+
*/
|
|
60
|
+
_addSegment(node, segment) {
|
|
61
|
+
let child = node.children.get(segment);
|
|
62
|
+
if (child == null) {
|
|
63
|
+
child = new PathTrieNode();
|
|
64
|
+
node.children.set(segment, child);
|
|
65
|
+
if (segment === wildcardSegment) {
|
|
66
|
+
node.hasWildcard = true;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return child;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Check if `path` or any of its ancestor paths exist in the trie. `path` is
|
|
74
|
+
* either a single segment string or a pre-split array of segments — lookups
|
|
75
|
+
* never split, callers are responsible for splitting dotted paths.
|
|
76
|
+
* For example, with a trie containing 'profile':
|
|
77
|
+
* matchesPathOrAncestor(['profile', 'firstName']) === true
|
|
78
|
+
* matchesPathOrAncestor('profile') === true
|
|
79
|
+
* But with a trie containing 'profile.firstName':
|
|
80
|
+
* matchesPathOrAncestor('profile') === false
|
|
81
|
+
*
|
|
82
|
+
* @param {string|string[]} path
|
|
83
|
+
*/
|
|
84
|
+
|
|
85
|
+
matchesPathOrAncestor(path) {
|
|
86
|
+
return this._walk(path) === true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Check if `path`, any of its ancestor paths, or any of its descendant paths
|
|
91
|
+
* exist in the trie. `path` is either a single segment string or a pre-split
|
|
92
|
+
* array of segments — lookups never split, callers are responsible for
|
|
93
|
+
* splitting dotted paths. For example:
|
|
94
|
+
* new PathTrie(['profile']).overlapsPath(['profile', 'firstName']) === true
|
|
95
|
+
* new PathTrie(['profile.firstName']).overlapsPath('profile') === true
|
|
96
|
+
*
|
|
97
|
+
* @param {string|string[]} path
|
|
98
|
+
*/
|
|
99
|
+
|
|
100
|
+
overlapsPath(path) {
|
|
101
|
+
return this._walk(path) !== false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Walk `path` through the trie. `path` is assumed to be pre-split - if the
|
|
106
|
+
* path has multiple segments, pass in an array of strings. Returns `true` if
|
|
107
|
+
* a terminal node was found at `path` or one of its ancestors, `false` if the
|
|
108
|
+
* walk dead-ended, and the array of reached nodes if `path` is a strict prefix
|
|
109
|
+
* of entries in the trie.
|
|
110
|
+
*
|
|
111
|
+
* @param {string|string[]} path
|
|
112
|
+
*/
|
|
113
|
+
|
|
114
|
+
_walk(path) {
|
|
115
|
+
if (typeof path === 'string') {
|
|
116
|
+
const next = [];
|
|
117
|
+
const literal = this.root.children.get(path);
|
|
118
|
+
if (literal != null) {
|
|
119
|
+
if (literal.terminal) {
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
next.push(literal);
|
|
123
|
+
}
|
|
124
|
+
if (this.root.hasWildcard) {
|
|
125
|
+
const wildcard = this.root.children.get(wildcardSegment);
|
|
126
|
+
if (wildcard.terminal) {
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
next.push(wildcard);
|
|
130
|
+
}
|
|
131
|
+
return next.length === 0 ? false : next;
|
|
132
|
+
}
|
|
133
|
+
let nodes = [this.root];
|
|
134
|
+
for (const segment of path) {
|
|
135
|
+
const next = [];
|
|
136
|
+
const isNumericSegment = numericSegmentRegex.test(segment);
|
|
137
|
+
for (const node of nodes) {
|
|
138
|
+
if (isNumericSegment) {
|
|
139
|
+
// Numeric segments like array indexes are ignorable: `comments.text`
|
|
140
|
+
// in the trie matches the modified path `comments.0.text`.
|
|
141
|
+
next.push(node);
|
|
142
|
+
}
|
|
143
|
+
const literal = node.children.get(segment);
|
|
144
|
+
if (literal != null) {
|
|
145
|
+
if (literal.terminal) {
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
next.push(literal);
|
|
149
|
+
}
|
|
150
|
+
if (node.hasWildcard) {
|
|
151
|
+
const wildcard = node.children.get(wildcardSegment);
|
|
152
|
+
if (wildcard.terminal) {
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
next.push(wildcard);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (next.length === 0) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
nodes = next;
|
|
162
|
+
}
|
|
163
|
+
return nodes;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
module.exports = PathTrie;
|
package/lib/internal.js
CHANGED
|
@@ -17,6 +17,8 @@ InternalCache.prototype.strictMode = true;
|
|
|
17
17
|
|
|
18
18
|
InternalCache.prototype.fullPath = undefined;
|
|
19
19
|
InternalCache.prototype.selected = undefined;
|
|
20
|
+
// Lazily computed index over `selected`, see `_getProjectionIndex()` in document.js
|
|
21
|
+
InternalCache.prototype.selectedIndex = undefined;
|
|
20
22
|
InternalCache.prototype.shardval = undefined;
|
|
21
23
|
InternalCache.prototype.saveError = undefined;
|
|
22
24
|
InternalCache.prototype.validationError = undefined;
|
package/lib/model.js
CHANGED
|
@@ -3622,6 +3622,15 @@ Model.bulkSave = async function bulkSave(documents, options) {
|
|
|
3622
3622
|
}
|
|
3623
3623
|
}
|
|
3624
3624
|
|
|
3625
|
+
// Associate the documents with the session like `save()` does, so built-in
|
|
3626
|
+
// `pre('save')` middleware like `trackTransaction` can snapshot document
|
|
3627
|
+
// state and restore it between transaction retries. Re: gh-16432
|
|
3628
|
+
if (Object.hasOwn(options, 'session')) {
|
|
3629
|
+
for (const document of documents) {
|
|
3630
|
+
document.$session(options.session);
|
|
3631
|
+
}
|
|
3632
|
+
}
|
|
3633
|
+
|
|
3625
3634
|
await Promise.all(documents.map(doc => buildPreSavePromise(doc, options)));
|
|
3626
3635
|
|
|
3627
3636
|
const writeOperations = this.buildBulkWriteOperations(documents, options);
|
|
@@ -7,31 +7,45 @@ const utils = require('../utils');
|
|
|
7
7
|
|
|
8
8
|
module.exports = function trackTransaction(schema) {
|
|
9
9
|
schema.pre('save', trackTransactionPreSave);
|
|
10
|
+
schema.pre('deleteOne', { document: true, query: false }, trackTransactionPreDeleteOne);
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
function trackTransactionPreSave() {
|
|
13
|
-
|
|
14
|
+
_getInitialState(this);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function trackTransactionPreDeleteOne() {
|
|
18
|
+
const initialState = _getInitialState(this);
|
|
19
|
+
if (initialState != null && !Object.hasOwn(initialState, 'isDeleted')) {
|
|
20
|
+
initialState.isDeleted = this.$isDeleted();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function _getInitialState(doc) {
|
|
25
|
+
const session = doc.$session();
|
|
14
26
|
if (session == null) {
|
|
15
|
-
return;
|
|
27
|
+
return null;
|
|
16
28
|
}
|
|
17
29
|
if (session.transaction == null || session[sessionNewDocuments] == null) {
|
|
18
|
-
return;
|
|
30
|
+
return null;
|
|
19
31
|
}
|
|
20
32
|
|
|
21
|
-
if (!session[sessionNewDocuments].has(
|
|
33
|
+
if (!session[sessionNewDocuments].has(doc)) {
|
|
22
34
|
const initialState = {};
|
|
23
|
-
if (
|
|
35
|
+
if (doc.isNew) {
|
|
24
36
|
initialState.isNew = true;
|
|
25
37
|
}
|
|
26
|
-
if (
|
|
27
|
-
initialState.versionKey =
|
|
38
|
+
if (doc.$__schema.options.versionKey) {
|
|
39
|
+
initialState.versionKey = doc.get(doc.$__schema.options.versionKey);
|
|
28
40
|
}
|
|
29
41
|
|
|
30
|
-
initialState.modifiedPaths = new Set(Object.keys(
|
|
31
|
-
initialState.atomics = _getAtomics(
|
|
42
|
+
initialState.modifiedPaths = new Set(Object.keys(doc.$__.activePaths.getStatePaths('modify')));
|
|
43
|
+
initialState.atomics = _getAtomics(doc);
|
|
32
44
|
|
|
33
|
-
session[sessionNewDocuments].set(
|
|
45
|
+
session[sessionNewDocuments].set(doc, initialState);
|
|
34
46
|
}
|
|
47
|
+
|
|
48
|
+
return session[sessionNewDocuments].get(doc);
|
|
35
49
|
}
|
|
36
50
|
|
|
37
51
|
function _getAtomics(doc, previous) {
|
|
@@ -87,3 +101,4 @@ function mergeAtomics(destination, source) {
|
|
|
87
101
|
}
|
|
88
102
|
|
|
89
103
|
trackTransactionPreSave[symbols.builtInMiddleware] = true;
|
|
104
|
+
trackTransactionPreDeleteOne[symbols.builtInMiddleware] = true;
|
package/lib/query.js
CHANGED
|
@@ -2849,12 +2849,7 @@ Query.prototype.findOne = function(conditions, projection, options) {
|
|
|
2849
2849
|
|
|
2850
2850
|
Query.prototype._countDocuments = async function _countDocuments() {
|
|
2851
2851
|
this._applyTranslateAliases();
|
|
2852
|
-
|
|
2853
|
-
try {
|
|
2854
|
-
this.cast(this.model);
|
|
2855
|
-
} catch (err) {
|
|
2856
|
-
this.error(err);
|
|
2857
|
-
}
|
|
2852
|
+
this._castConditions();
|
|
2858
2853
|
|
|
2859
2854
|
if (this.error()) {
|
|
2860
2855
|
throw this.error();
|
|
@@ -5377,11 +5372,17 @@ Query.prototype.cursor = function cursor(opts) {
|
|
|
5377
5372
|
}
|
|
5378
5373
|
|
|
5379
5374
|
try {
|
|
5380
|
-
this.
|
|
5375
|
+
this._castConditions();
|
|
5381
5376
|
} catch (err) {
|
|
5377
|
+
// `_castConditions()` reports cast errors on the query, but `sanitizeFilter`
|
|
5378
|
+
// throws for filters it refuses outright, like `$where`.
|
|
5382
5379
|
return (new QueryCursor(this))._markError(err);
|
|
5383
5380
|
}
|
|
5384
5381
|
|
|
5382
|
+
if (this.error()) {
|
|
5383
|
+
return (new QueryCursor(this))._markError(this.error());
|
|
5384
|
+
}
|
|
5385
|
+
|
|
5385
5386
|
return new QueryCursor(this);
|
|
5386
5387
|
};
|
|
5387
5388
|
|
package/lib/schema/number.js
CHANGED
|
@@ -498,7 +498,7 @@ SchemaNumber.prototype.castForQuery = function($conditional, val, context) {
|
|
|
498
498
|
*/
|
|
499
499
|
|
|
500
500
|
SchemaNumber.prototype.toJSONSchema = function toJSONSchema(options) {
|
|
501
|
-
return this._createJSONSchemaTypeDefinition('number', 'number', options);
|
|
501
|
+
return this._addJSONSchemaEnum(this._createJSONSchemaTypeDefinition('number', 'number', options));
|
|
502
502
|
};
|
|
503
503
|
|
|
504
504
|
/*!
|
package/lib/schema/string.js
CHANGED
|
@@ -719,7 +719,7 @@ SchemaString.prototype.castForQuery = function($conditional, val, context) {
|
|
|
719
719
|
*/
|
|
720
720
|
|
|
721
721
|
SchemaString.prototype.toJSONSchema = function toJSONSchema(options) {
|
|
722
|
-
return this._createJSONSchemaTypeDefinition('string', 'string', options);
|
|
722
|
+
return this._addJSONSchemaEnum(this._createJSONSchemaTypeDefinition('string', 'string', options));
|
|
723
723
|
};
|
|
724
724
|
|
|
725
725
|
SchemaString.prototype.autoEncryptionType = function autoEncryptionType() {
|
package/lib/schema.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
const EventEmitter = require('events').EventEmitter;
|
|
8
8
|
const Kareem = require('kareem');
|
|
9
9
|
const MongooseError = require('./error/mongooseError');
|
|
10
|
+
const PathTrie = require('./helpers/pathTrie');
|
|
10
11
|
const SchemaType = require('./schemaType');
|
|
11
12
|
const SchemaTypeOptions = require('./options/schemaTypeOptions');
|
|
12
13
|
const VirtualOptions = require('./options/virtualOptions');
|
|
@@ -79,7 +80,7 @@ const numberRE = /^\d+$/;
|
|
|
79
80
|
* - [validateBeforeSave](https://mongoosejs.com/docs/guide.html#validateBeforeSave) - bool - defaults to `true`
|
|
80
81
|
* - [validateModifiedOnly](https://mongoosejs.com/docs/api/document.html#Document.prototype.validate()) - bool - defaults to `false`
|
|
81
82
|
* - [versionKey](https://mongoosejs.com/docs/guide.html#versionKey): string or object - defaults to "__v"
|
|
82
|
-
* - [optimisticConcurrency](https://mongoosejs.com/docs/guide.html#optimisticConcurrency): bool or string[] or { exclude: string[] } - defaults to false. Set to true to enable [optimistic concurrency](https://thecodebarbarian.com/whats-new-in-mongoose-5-10-optimistic-concurrency.html) for all fields. Set to a string array to enable optimistic concurrency only for the specified fields; note that this **replaces** the default array versioning behavior. Set to `{ exclude: string[] }` to enable optimistic concurrency for all fields except the specified ones; this also replaces the default array versioning.
|
|
83
|
+
* - [optimisticConcurrency](https://mongoosejs.com/docs/guide.html#optimisticConcurrency): bool or string[] or { exclude: string[] } - defaults to false. Set to true to enable [optimistic concurrency](https://thecodebarbarian.com/whats-new-in-mongoose-5-10-optimistic-concurrency.html) for all fields. Set to a string array to enable optimistic concurrency only for the specified fields; note that this **replaces** the default array versioning behavior. Set to `{ exclude: string[] }` to enable optimistic concurrency for all fields except the specified ones; this also replaces the default array versioning. Paths may include the map wildcard `$*`, like `'settings.$*'`, to match all keys of a map.
|
|
83
84
|
* - [collation](https://mongoosejs.com/docs/guide.html#collation): object - defaults to null (which means use no collation)
|
|
84
85
|
* - [timeseries](https://mongoosejs.com/docs/guide.html#timeseries): object - defaults to null (which means this schema's collection won't be a timeseries collection)
|
|
85
86
|
* - [selectPopulatedPaths](https://mongoosejs.com/docs/guide.html#selectPopulatedPaths): boolean - defaults to `true`
|
|
@@ -3142,9 +3143,9 @@ Schema.prototype._preCompile = function _preCompile() {
|
|
|
3142
3143
|
};
|
|
3143
3144
|
|
|
3144
3145
|
/*!
|
|
3145
|
-
* Build precomputed
|
|
3146
|
-
*
|
|
3147
|
-
*
|
|
3146
|
+
* Build precomputed path tries for optimisticConcurrency include/exclude so
|
|
3147
|
+
* that lookups at save time are a single trie walk that handles nested paths
|
|
3148
|
+
* and map wildcards like `settings.$*`.
|
|
3148
3149
|
*/
|
|
3149
3150
|
|
|
3150
3151
|
function _precomputeOptimisticConcurrency(schema) {
|
|
@@ -3153,9 +3154,9 @@ function _precomputeOptimisticConcurrency(schema) {
|
|
|
3153
3154
|
return;
|
|
3154
3155
|
}
|
|
3155
3156
|
if (Array.isArray(opt)) {
|
|
3156
|
-
schema.options.
|
|
3157
|
+
schema.options._optimisticConcurrencyTrie = new PathTrie(opt);
|
|
3157
3158
|
} else if (Array.isArray(opt.exclude)) {
|
|
3158
|
-
schema.options.
|
|
3159
|
+
schema.options._optimisticConcurrencyExcludeTrie = new PathTrie(opt.exclude);
|
|
3159
3160
|
}
|
|
3160
3161
|
}
|
|
3161
3162
|
|
|
@@ -3215,27 +3216,19 @@ Schema.prototype.toJSONSchema = function toJSONSchema(options) {
|
|
|
3215
3216
|
}
|
|
3216
3217
|
|
|
3217
3218
|
const lastSubpath = schemaType._presplitPath[schemaType._presplitPath.length - 1];
|
|
3218
|
-
let isRequired = false;
|
|
3219
3219
|
if (path === '_id') {
|
|
3220
3220
|
if (!jsonSchemaForPath.required) {
|
|
3221
3221
|
jsonSchemaForPath.required = [];
|
|
3222
3222
|
}
|
|
3223
3223
|
jsonSchemaForPath.required.push('_id');
|
|
3224
|
-
isRequired = true;
|
|
3225
3224
|
} else if (schemaType.options.required && typeof schemaType.options.required !== 'function') {
|
|
3226
3225
|
if (!jsonSchemaForPath.required) {
|
|
3227
3226
|
jsonSchemaForPath.required = [];
|
|
3228
3227
|
}
|
|
3229
3228
|
// Only `required: true` paths are required, conditional required is not required
|
|
3230
3229
|
jsonSchemaForPath.required.push(lastSubpath);
|
|
3231
|
-
isRequired = true;
|
|
3232
3230
|
}
|
|
3233
3231
|
jsonSchemaForPath.properties[lastSubpath] = schemaType.toJSONSchema(options);
|
|
3234
|
-
if (schemaType.options.enum) {
|
|
3235
|
-
jsonSchemaForPath.properties[lastSubpath].enum = isRequired
|
|
3236
|
-
? schemaType.options.enum
|
|
3237
|
-
: [...schemaType.options.enum, null];
|
|
3238
|
-
}
|
|
3239
3232
|
}
|
|
3240
3233
|
|
|
3241
3234
|
// Otherwise MongoDB errors with "$jsonSchema keyword 'required' cannot be an empty array"
|
package/lib/schemaType.js
CHANGED
|
@@ -215,6 +215,27 @@ SchemaType.prototype._createJSONSchemaTypeDefinition = function _createJSONSchem
|
|
|
215
215
|
return { type: [type, 'null'] };
|
|
216
216
|
};
|
|
217
217
|
|
|
218
|
+
/**
|
|
219
|
+
* Helper for adding this SchemaType's enum values to a JSON schema type definition.
|
|
220
|
+
* Reads `enumValues` rather than `options.enum` because `enum` also accepts an object.
|
|
221
|
+
*
|
|
222
|
+
* @param {object} definition the type definition from `_createJSONSchemaTypeDefinition()`
|
|
223
|
+
* @returns {object} the same definition
|
|
224
|
+
* @api private
|
|
225
|
+
*/
|
|
226
|
+
|
|
227
|
+
SchemaType.prototype._addJSONSchemaEnum = function _addJSONSchemaEnum(definition) {
|
|
228
|
+
if (!Array.isArray(this.enumValues) || this.enumValues.length === 0) {
|
|
229
|
+
return definition;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// The enum validator allows nullish values, so allow `null` wherever the type does.
|
|
233
|
+
const allowsNull = Array.isArray(definition.type ?? definition.bsonType);
|
|
234
|
+
definition.enum = allowsNull ? [...this.enumValues, null] : [...this.enumValues];
|
|
235
|
+
|
|
236
|
+
return definition;
|
|
237
|
+
};
|
|
238
|
+
|
|
218
239
|
/**
|
|
219
240
|
* The validators that Mongoose should run to validate properties at this SchemaType's path.
|
|
220
241
|
*
|
package/lib/utils.js
CHANGED
|
@@ -421,26 +421,16 @@ exports.isPOJO = require('./helpers/isPOJO');
|
|
|
421
421
|
|
|
422
422
|
exports.isNonBuiltinObject = function isNonBuiltinObject(val) {
|
|
423
423
|
return typeof val === 'object' &&
|
|
424
|
-
!
|
|
424
|
+
!Array.isArray(val) &&
|
|
425
|
+
!(val instanceof Date) &&
|
|
426
|
+
!(val instanceof Boolean) &&
|
|
427
|
+
!(val instanceof Number) &&
|
|
428
|
+
!(val instanceof String) &&
|
|
425
429
|
!exports.isMongooseType(val) &&
|
|
426
430
|
!(val instanceof UUID) &&
|
|
427
431
|
val != null;
|
|
428
432
|
};
|
|
429
433
|
|
|
430
|
-
/**
|
|
431
|
-
* Determines if `obj` is a built-in object like an array, date, boolean,
|
|
432
|
-
* etc.
|
|
433
|
-
* @param {any} arg
|
|
434
|
-
*/
|
|
435
|
-
|
|
436
|
-
exports.isNativeObject = function(arg) {
|
|
437
|
-
return Array.isArray(arg) ||
|
|
438
|
-
arg instanceof Date ||
|
|
439
|
-
arg instanceof Boolean ||
|
|
440
|
-
arg instanceof Number ||
|
|
441
|
-
arg instanceof String;
|
|
442
|
-
};
|
|
443
|
-
|
|
444
434
|
/**
|
|
445
435
|
* Determines if `val` is an object that has no own keys
|
|
446
436
|
* @param {any} val
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mongoose",
|
|
3
3
|
"description": "Mongoose MongoDB ODM",
|
|
4
|
-
"version": "9.9.
|
|
4
|
+
"version": "9.9.3",
|
|
5
5
|
"author": "Guillermo Rauch <guillermo@learnboost.com>",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"mongodb",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"sift": "17.1.3"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@ark/attest": "0.56.
|
|
32
|
+
"@ark/attest": "0.56.3",
|
|
33
33
|
"@eslint/js": "^9.39.3",
|
|
34
34
|
"@mongodb-js/mongodb-downloader": "^1.0.0",
|
|
35
35
|
"@types/node": "^20.19.0",
|
|
@@ -37,30 +37,30 @@
|
|
|
37
37
|
"acquit-ignore": "0.2.2",
|
|
38
38
|
"acquit-require": "0.1.1",
|
|
39
39
|
"ajv": "8.20.0",
|
|
40
|
-
"c8": "
|
|
40
|
+
"c8": "12.0.0",
|
|
41
41
|
"cheerio": "1.2.0",
|
|
42
42
|
"dox": "1.0.0",
|
|
43
|
-
"eslint": "10.
|
|
43
|
+
"eslint": "10.8.0",
|
|
44
44
|
"eslint-plugin-mocha-no-only": "1.2.0",
|
|
45
45
|
"express": "5.2.1",
|
|
46
|
-
"fs-extra": "~11.
|
|
46
|
+
"fs-extra": "~11.4.0",
|
|
47
47
|
"glob": "^13.0.6",
|
|
48
48
|
"globals": "^17.4.0",
|
|
49
49
|
"highlight.js": "11.11.1",
|
|
50
|
-
"linkinator": "
|
|
50
|
+
"linkinator": "8.x",
|
|
51
51
|
"lodash.isequal": "4.5.0",
|
|
52
52
|
"lodash.isequalwith": "4.4.0",
|
|
53
|
-
"markdownlint-cli2": "0.
|
|
54
|
-
"marked": "18.0.
|
|
53
|
+
"markdownlint-cli2": "0.23.2",
|
|
54
|
+
"marked": "18.0.7",
|
|
55
55
|
"mkdirp": "^3.0.1",
|
|
56
|
-
"mocha": "12.0.0-
|
|
56
|
+
"mocha": "12.0.0-rc.5",
|
|
57
57
|
"moment": "2.30.1",
|
|
58
58
|
"mongodb-client-encryption": "^7.2.0",
|
|
59
59
|
"mongodb-memory-server": "11.2.0",
|
|
60
60
|
"mongodb-runner": "^6.0.0",
|
|
61
61
|
"ncp": "^2.0.0",
|
|
62
62
|
"pug": "3.0.4",
|
|
63
|
-
"sinon": "22.
|
|
63
|
+
"sinon": "22.1.0",
|
|
64
64
|
"tstyche": "^7.0.0",
|
|
65
65
|
"typescript": "5.9.3",
|
|
66
66
|
"typescript-eslint": "^8.31.1",
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
"publish-7x": "npm publish --tag 7x",
|
|
101
101
|
"create-separate-require-instance": "rm -rf ./node_modules/mongoose-separate-require-instance && node ./scripts/create-tarball && tar -xzf mongoose.tgz -C ./node_modules && mv ./node_modules/package ./node_modules/mongoose-separate-require-instance",
|
|
102
102
|
"test": "mocha --exit --ignore \"test/encryption/**/*.test.js\" \"./test/**/*.test.js\"",
|
|
103
|
-
"test:ci": "npm run test -- --reporter min",
|
|
103
|
+
"test:ci": "npm run test -- --reporter min --timeout 10000",
|
|
104
104
|
"test-deno": "deno run --allow-env --allow-read --allow-net --allow-run --allow-sys --allow-write ./test/deno.mjs",
|
|
105
105
|
"test-deno:ci": "npm run test-deno -- --reporter min",
|
|
106
106
|
"test-rs": "START_REPLICA_SET=1 mocha --timeout 30000 --exit --ignore \"test/encryption/**/*.test.js\" \"./test/**/*.test.js\"",
|
package/types/document.d.ts
CHANGED
|
@@ -289,9 +289,9 @@ declare module 'mongoose' {
|
|
|
289
289
|
): ToObjectReturnType<PopulatedRawDocType, TVirtuals, O, TSchemaOptions>;
|
|
290
290
|
toJSON<PopulatedRawDocType>(
|
|
291
291
|
this: PopulatedDocumentMarker<PopulatedRawDocType, any>
|
|
292
|
-
):
|
|
292
|
+
): DefaultToObjectReturnType<PopulatedRawDocType, TVirtuals, TSchemaOptions, 'toJSON'>;
|
|
293
293
|
toJSON<O extends ToObjectOptions>(options: O): ToObjectReturnType<DocType, TVirtuals, O, TSchemaOptions>;
|
|
294
|
-
toJSON(options?: ToObjectOptions):
|
|
294
|
+
toJSON(options?: ToObjectOptions): DefaultToObjectReturnType<DocType, TVirtuals, TSchemaOptions, 'toJSON'>;
|
|
295
295
|
toJSON<T>(options?: ToObjectOptions): Default__v<Require_id<T>, ResolveSchemaOptions<TSchemaOptions>>;
|
|
296
296
|
|
|
297
297
|
/** Converts this document into a plain-old JavaScript object ([POJO](https://masteringjs.io/tutorials/fundamentals/pojo)). */
|
|
@@ -309,9 +309,9 @@ declare module 'mongoose' {
|
|
|
309
309
|
): ToObjectReturnType<PopulatedRawDocType, TVirtuals, O, TSchemaOptions>;
|
|
310
310
|
toObject<PopulatedRawDocType>(
|
|
311
311
|
this: PopulatedDocumentMarker<PopulatedRawDocType, any>
|
|
312
|
-
):
|
|
312
|
+
): DefaultToObjectReturnType<PopulatedRawDocType, TVirtuals, TSchemaOptions, 'toObject'>;
|
|
313
313
|
toObject<O extends ToObjectOptions>(options: O): ToObjectReturnType<DocType, TVirtuals, O, TSchemaOptions>;
|
|
314
|
-
toObject(options?: ToObjectOptions):
|
|
314
|
+
toObject(options?: ToObjectOptions): DefaultToObjectReturnType<DocType, TVirtuals, TSchemaOptions, 'toObject'>;
|
|
315
315
|
toObject<T>(options?: ToObjectOptions): Default__v<Require_id<T>, ResolveSchemaOptions<TSchemaOptions>>;
|
|
316
316
|
|
|
317
317
|
/** Clears the modified state on the specified path. */
|
package/types/index.d.ts
CHANGED
|
@@ -1122,6 +1122,31 @@ declare module 'mongoose' {
|
|
|
1122
1122
|
? { [K in keyof T]: ApplyFlattenTransforms<T[K], O> }
|
|
1123
1123
|
: T;
|
|
1124
1124
|
|
|
1125
|
+
/**
|
|
1126
|
+
* Extracts the `toObject` or `toJSON` options declared in the schema options.
|
|
1127
|
+
* The runtime applies these as defaults when the corresponding method is
|
|
1128
|
+
* called without arguments.
|
|
1129
|
+
*/
|
|
1130
|
+
export type SchemaDeclaredToObjectOptions<TSchemaOptions, Key extends 'toObject' | 'toJSON'> =
|
|
1131
|
+
Key extends keyof TSchemaOptions
|
|
1132
|
+
? NonNullable<TSchemaOptions[Key]> extends infer O extends ToObjectOptions
|
|
1133
|
+
? O
|
|
1134
|
+
: {}
|
|
1135
|
+
: {};
|
|
1136
|
+
|
|
1137
|
+
/**
|
|
1138
|
+
* Computes the return type of toObject/toJSON when called without arguments,
|
|
1139
|
+
* applying the options declared in the schema like the runtime does. When the
|
|
1140
|
+
* schema declares no options for the method, the plain document shape is
|
|
1141
|
+
* returned without running it through the option transforms.
|
|
1142
|
+
*/
|
|
1143
|
+
export type DefaultToObjectReturnType<DocType, TVirtuals, TSchemaOptions, Key extends 'toObject' | 'toJSON'> =
|
|
1144
|
+
SchemaDeclaredToObjectOptions<TSchemaOptions, Key> extends infer O extends ToObjectOptions
|
|
1145
|
+
? keyof O extends never
|
|
1146
|
+
? Default__v<Require_id<DocType>, TSchemaOptions>
|
|
1147
|
+
: ToObjectReturnType<DocType, TVirtuals, O, TSchemaOptions>
|
|
1148
|
+
: Default__v<Require_id<DocType>, TSchemaOptions>;
|
|
1149
|
+
|
|
1125
1150
|
/**
|
|
1126
1151
|
* Computes the return type of toObject/toJSON based on the provided options.
|
|
1127
1152
|
* Uses a single-pass transform for flatten operations to correctly handle all combinations.
|
package/types/schematypes.d.ts
CHANGED