mongoose 6.1.9 → 6.2.2
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/.eslintrc.json +154 -0
- package/CHANGELOG.md +59 -0
- package/dist/browser.umd.js +233 -222
- package/index.js +5 -1
- package/lib/aggregate.js +23 -28
- package/lib/browserDocument.js +1 -1
- package/lib/cast/number.js +2 -3
- package/lib/cast.js +9 -7
- package/lib/connection.js +76 -24
- package/lib/cursor/AggregationCursor.js +12 -7
- package/lib/cursor/QueryCursor.js +11 -6
- package/lib/document.js +107 -107
- package/lib/drivers/node-mongodb-native/collection.js +12 -4
- package/lib/drivers/node-mongodb-native/connection.js +11 -0
- package/lib/error/cast.js +3 -2
- package/lib/error/index.js +11 -0
- package/lib/error/syncIndexes.js +30 -0
- package/lib/helpers/clone.js +51 -29
- package/lib/helpers/common.js +2 -2
- package/lib/helpers/cursor/eachAsync.js +18 -15
- package/lib/helpers/document/compile.js +7 -4
- package/lib/helpers/getFunctionName.js +6 -4
- package/lib/helpers/isMongooseObject.js +9 -8
- package/lib/helpers/isObject.js +4 -4
- package/lib/helpers/model/discriminator.js +2 -1
- package/lib/helpers/path/parentPaths.js +10 -5
- package/lib/helpers/populate/assignRawDocsToIdStructure.js +4 -2
- package/lib/helpers/populate/assignVals.js +8 -4
- package/lib/helpers/populate/getModelsMapForPopulate.js +4 -4
- package/lib/helpers/populate/markArraySubdocsPopulated.js +3 -1
- package/lib/helpers/populate/modelNamesFromRefPath.js +4 -3
- package/lib/helpers/printJestWarning.js +2 -2
- package/lib/helpers/projection/applyProjection.js +77 -0
- package/lib/helpers/projection/hasIncludedChildren.js +36 -0
- package/lib/helpers/projection/isExclusive.js +5 -2
- package/lib/helpers/projection/isInclusive.js +5 -1
- package/lib/helpers/query/cast$expr.js +279 -0
- package/lib/helpers/query/castUpdate.js +6 -2
- package/lib/helpers/query/isOperator.js +5 -2
- package/lib/helpers/schema/applyPlugins.js +11 -0
- package/lib/helpers/schema/getPath.js +4 -2
- package/lib/helpers/timestamps/setupTimestamps.js +3 -8
- package/lib/index.js +28 -26
- package/lib/internal.js +1 -1
- package/lib/model.js +161 -122
- package/lib/options/SchemaTypeOptions.js +1 -1
- package/lib/plugins/trackTransaction.js +5 -4
- package/lib/query.js +159 -146
- package/lib/queryhelpers.js +10 -10
- package/lib/schema/SubdocumentPath.js +4 -3
- package/lib/schema/array.js +30 -21
- package/lib/schema/buffer.js +1 -1
- package/lib/schema/date.js +1 -1
- package/lib/schema/decimal128.js +1 -1
- package/lib/schema/documentarray.js +9 -11
- package/lib/schema/number.js +1 -1
- package/lib/schema/objectid.js +2 -2
- package/lib/schema/string.js +4 -4
- package/lib/schema.js +9 -8
- package/lib/schematype.js +77 -30
- package/lib/types/ArraySubdocument.js +2 -1
- package/lib/types/DocumentArray/index.js +10 -27
- package/lib/types/DocumentArray/isMongooseDocumentArray.js +5 -0
- package/lib/types/DocumentArray/methods/index.js +15 -3
- package/lib/types/array/index.js +22 -21
- package/lib/types/array/isMongooseArray.js +5 -0
- package/lib/types/array/methods/index.js +22 -23
- package/lib/types/buffer.js +3 -3
- package/lib/types/map.js +2 -3
- package/lib/utils.js +10 -7
- package/package.json +19 -151
- package/tools/repl.js +1 -1
- package/tsconfig.json +8 -0
- package/types/PipelineStage.d.ts +272 -0
- package/{index.d.ts → types/index.d.ts} +156 -357
- package/lib/types/array/ArrayWrapper.js +0 -981
package/lib/helpers/clone.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
const cloneRegExp = require('regexp-clone');
|
|
5
3
|
const Decimal = require('../types/decimal128');
|
|
6
4
|
const ObjectId = require('../types/objectid');
|
|
7
5
|
const specialProperties = require('./specialProperties');
|
|
@@ -34,7 +32,7 @@ function clone(obj, options, isArrayChild) {
|
|
|
34
32
|
}
|
|
35
33
|
|
|
36
34
|
if (Array.isArray(obj)) {
|
|
37
|
-
return cloneArray(obj, options);
|
|
35
|
+
return cloneArray(utils.isMongooseArray(obj) ? obj.__array : obj, options);
|
|
38
36
|
}
|
|
39
37
|
|
|
40
38
|
if (isMongooseObject(obj)) {
|
|
@@ -43,23 +41,34 @@ function clone(obj, options, isArrayChild) {
|
|
|
43
41
|
if (options && options._skipSingleNestedGetters && obj.$isSingleNested) {
|
|
44
42
|
options = Object.assign({}, options, { getters: false });
|
|
45
43
|
}
|
|
44
|
+
const isSingleNested = obj.$isSingleNested;
|
|
46
45
|
|
|
47
46
|
if (utils.isPOJO(obj) && obj.$__ != null && obj._doc != null) {
|
|
48
47
|
return obj._doc;
|
|
49
48
|
}
|
|
50
49
|
|
|
50
|
+
let ret;
|
|
51
51
|
if (options && options.json && typeof obj.toJSON === 'function') {
|
|
52
|
-
|
|
52
|
+
ret = obj.toJSON(options);
|
|
53
|
+
} else {
|
|
54
|
+
ret = obj.toObject(options);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (options && options.minimize && isSingleNested && Object.keys(ret).length === 0) {
|
|
58
|
+
return undefined;
|
|
53
59
|
}
|
|
54
|
-
|
|
60
|
+
|
|
61
|
+
return ret;
|
|
55
62
|
}
|
|
56
63
|
|
|
57
|
-
|
|
58
|
-
|
|
64
|
+
const objConstructor = obj.constructor;
|
|
65
|
+
|
|
66
|
+
if (objConstructor) {
|
|
67
|
+
switch (getFunctionName(objConstructor)) {
|
|
59
68
|
case 'Object':
|
|
60
69
|
return cloneObject(obj, options, isArrayChild);
|
|
61
70
|
case 'Date':
|
|
62
|
-
return new
|
|
71
|
+
return new objConstructor(+obj);
|
|
63
72
|
case 'RegExp':
|
|
64
73
|
return cloneRegExp(obj);
|
|
65
74
|
default:
|
|
@@ -79,12 +88,12 @@ function clone(obj, options, isArrayChild) {
|
|
|
79
88
|
return Decimal.fromString(obj.toString());
|
|
80
89
|
}
|
|
81
90
|
|
|
82
|
-
|
|
83
|
-
|
|
91
|
+
// object created with Object.create(null)
|
|
92
|
+
if (!objConstructor && isObject(obj)) {
|
|
84
93
|
return cloneObject(obj, options, isArrayChild);
|
|
85
94
|
}
|
|
86
95
|
|
|
87
|
-
if (obj[symbols.schemaTypeSymbol]) {
|
|
96
|
+
if (typeof obj === 'object' && obj[symbols.schemaTypeSymbol]) {
|
|
88
97
|
return obj.clone();
|
|
89
98
|
}
|
|
90
99
|
|
|
@@ -95,7 +104,7 @@ function clone(obj, options, isArrayChild) {
|
|
|
95
104
|
return obj;
|
|
96
105
|
}
|
|
97
106
|
|
|
98
|
-
if (obj.valueOf
|
|
107
|
+
if (typeof obj.valueOf === 'function') {
|
|
99
108
|
return obj.valueOf();
|
|
100
109
|
}
|
|
101
110
|
|
|
@@ -112,25 +121,28 @@ function cloneObject(obj, options, isArrayChild) {
|
|
|
112
121
|
const ret = {};
|
|
113
122
|
let hasKeys;
|
|
114
123
|
|
|
115
|
-
if (obj
|
|
124
|
+
if (trustedSymbol in obj) {
|
|
116
125
|
ret[trustedSymbol] = obj[trustedSymbol];
|
|
117
126
|
}
|
|
118
127
|
|
|
119
|
-
|
|
120
|
-
|
|
128
|
+
let i = 0;
|
|
129
|
+
let key = '';
|
|
130
|
+
const keys = Object.keys(obj);
|
|
131
|
+
const len = keys.length;
|
|
132
|
+
|
|
133
|
+
for (i = 0; i < len; ++i) {
|
|
134
|
+
if (specialProperties.has(key = keys[i])) {
|
|
121
135
|
continue;
|
|
122
136
|
}
|
|
123
137
|
|
|
124
138
|
// Don't pass `isArrayChild` down
|
|
125
|
-
const val = clone(obj[
|
|
126
|
-
|
|
127
|
-
if (
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
ret[k] = val;
|
|
133
|
-
}
|
|
139
|
+
const val = clone(obj[key], options, false);
|
|
140
|
+
|
|
141
|
+
if (minimize === false && typeof val === 'undefined') {
|
|
142
|
+
delete ret[key];
|
|
143
|
+
} else if (minimize !== true || (typeof val !== 'undefined')) {
|
|
144
|
+
hasKeys || (hasKeys = true);
|
|
145
|
+
ret[key] = val;
|
|
134
146
|
}
|
|
135
147
|
}
|
|
136
148
|
|
|
@@ -138,11 +150,21 @@ function cloneObject(obj, options, isArrayChild) {
|
|
|
138
150
|
}
|
|
139
151
|
|
|
140
152
|
function cloneArray(arr, options) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
153
|
+
let i = 0;
|
|
154
|
+
const len = arr.length;
|
|
155
|
+
const ret = new Array(len);
|
|
156
|
+
for (i = 0; i < len; ++i) {
|
|
157
|
+
ret[i] = clone(arr[i], options, true);
|
|
145
158
|
}
|
|
146
159
|
|
|
147
160
|
return ret;
|
|
148
|
-
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function cloneRegExp(regexp) {
|
|
164
|
+
const ret = new RegExp(regexp.source, regexp.flags);
|
|
165
|
+
|
|
166
|
+
if (ret.lastIndex !== regexp.lastIndex) {
|
|
167
|
+
ret.lastIndex = regexp.lastIndex;
|
|
168
|
+
}
|
|
169
|
+
return ret;
|
|
170
|
+
}
|
package/lib/helpers/common.js
CHANGED
|
@@ -88,7 +88,7 @@ function modifiedPaths(update, path, result) {
|
|
|
88
88
|
cur += '.' + sp[i];
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
-
if (
|
|
91
|
+
if (!Buffer.isBuffer(val) && isMongooseObject(val)) {
|
|
92
92
|
val = val.toObject({ transform: false, virtuals: false });
|
|
93
93
|
}
|
|
94
94
|
if (shouldFlatten(val)) {
|
|
@@ -108,7 +108,7 @@ function shouldFlatten(val) {
|
|
|
108
108
|
typeof val === 'object' &&
|
|
109
109
|
!(val instanceof Date) &&
|
|
110
110
|
!(val instanceof ObjectId) &&
|
|
111
|
-
(!Array.isArray(val) || val.length
|
|
111
|
+
(!Array.isArray(val) || val.length !== 0) &&
|
|
112
112
|
!(val instanceof Buffer) &&
|
|
113
113
|
!(val instanceof Decimal128) &&
|
|
114
114
|
!(val instanceof Binary);
|
|
@@ -27,16 +27,15 @@ module.exports = function eachAsync(next, fn, options, callback) {
|
|
|
27
27
|
const enqueue = asyncQueue();
|
|
28
28
|
|
|
29
29
|
return promiseOrCallback(callback, cb => {
|
|
30
|
+
|
|
30
31
|
if (batchSize != null) {
|
|
31
32
|
if (typeof batchSize !== 'number') {
|
|
32
33
|
throw new TypeError('batchSize must be a number');
|
|
33
|
-
}
|
|
34
|
-
|
|
34
|
+
} else if (!Number.isInteger(batchSize)) {
|
|
35
|
+
throw new TypeError('batchSize must be an integer');
|
|
36
|
+
} else if (batchSize < 1) {
|
|
35
37
|
throw new TypeError('batchSize must be at least 1');
|
|
36
38
|
}
|
|
37
|
-
if (batchSize !== Math.floor(batchSize)) {
|
|
38
|
-
throw new TypeError('batchSize must be a positive integer');
|
|
39
|
-
}
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
iterate(cb);
|
|
@@ -71,7 +70,7 @@ module.exports = function eachAsync(next, fn, options, callback) {
|
|
|
71
70
|
drained = true;
|
|
72
71
|
if (handleResultsInProgress <= 0) {
|
|
73
72
|
finalCallback(null);
|
|
74
|
-
} else if (batchSize
|
|
73
|
+
} else if (batchSize && documentsBatch.length) {
|
|
75
74
|
handleNextResult(documentsBatch, currentDocumentIndex++, handleNextResultCallBack);
|
|
76
75
|
}
|
|
77
76
|
return done();
|
|
@@ -83,20 +82,20 @@ module.exports = function eachAsync(next, fn, options, callback) {
|
|
|
83
82
|
// make sure we know that we still have a result to handle re: #8422
|
|
84
83
|
immediate(() => done());
|
|
85
84
|
|
|
86
|
-
if (batchSize
|
|
85
|
+
if (batchSize) {
|
|
87
86
|
documentsBatch.push(doc);
|
|
88
87
|
}
|
|
89
88
|
|
|
90
89
|
// If the current documents size is less than the provided patch size don't process the documents yet
|
|
91
|
-
if (batchSize
|
|
92
|
-
|
|
90
|
+
if (batchSize && documentsBatch.length !== batchSize) {
|
|
91
|
+
immediate(() => enqueue(fetch));
|
|
93
92
|
return;
|
|
94
93
|
}
|
|
95
94
|
|
|
96
|
-
const docsToProcess = batchSize
|
|
95
|
+
const docsToProcess = batchSize ? documentsBatch : doc;
|
|
97
96
|
|
|
98
97
|
function handleNextResultCallBack(err) {
|
|
99
|
-
if (batchSize
|
|
98
|
+
if (batchSize) {
|
|
100
99
|
handleResultsInProgress -= documentsBatch.length;
|
|
101
100
|
documentsBatch = [];
|
|
102
101
|
} else {
|
|
@@ -110,7 +109,7 @@ module.exports = function eachAsync(next, fn, options, callback) {
|
|
|
110
109
|
return finalCallback(null);
|
|
111
110
|
}
|
|
112
111
|
|
|
113
|
-
|
|
112
|
+
immediate(() => enqueue(fetch));
|
|
114
113
|
}
|
|
115
114
|
|
|
116
115
|
handleNextResult(docsToProcess, currentDocumentIndex++, handleNextResultCallBack);
|
|
@@ -139,7 +138,10 @@ function asyncQueue() {
|
|
|
139
138
|
let id = 0;
|
|
140
139
|
|
|
141
140
|
return function enqueue(fn) {
|
|
142
|
-
if (
|
|
141
|
+
if (
|
|
142
|
+
inProgress === null &&
|
|
143
|
+
_queue.length === 0
|
|
144
|
+
) {
|
|
143
145
|
inProgress = id++;
|
|
144
146
|
return fn(_step);
|
|
145
147
|
}
|
|
@@ -147,11 +149,12 @@ function asyncQueue() {
|
|
|
147
149
|
};
|
|
148
150
|
|
|
149
151
|
function _step() {
|
|
150
|
-
|
|
151
|
-
if (_queue.length > 0) {
|
|
152
|
+
if (_queue.length !== 0) {
|
|
152
153
|
inProgress = id++;
|
|
153
154
|
const fn = _queue.shift();
|
|
154
155
|
fn(_step);
|
|
156
|
+
} else {
|
|
157
|
+
inProgress = null;
|
|
155
158
|
}
|
|
156
159
|
}
|
|
157
160
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const documentSchemaSymbol = require('../../helpers/symbols').documentSchemaSymbol;
|
|
4
|
-
const get = require('../../helpers/get');
|
|
5
4
|
const internalToObjectOptions = require('../../options').internalToObjectOptions;
|
|
6
5
|
const utils = require('../../utils');
|
|
7
6
|
|
|
@@ -93,7 +92,11 @@ function defineKey({ prop, subprops, prototype, prefix, options }) {
|
|
|
93
92
|
writable: false,
|
|
94
93
|
value: function() {
|
|
95
94
|
return utils.clone(_this.get(path, null, {
|
|
96
|
-
virtuals:
|
|
95
|
+
virtuals: this &&
|
|
96
|
+
this.schema &&
|
|
97
|
+
this.schema.options &&
|
|
98
|
+
this.schema.options.toObject &&
|
|
99
|
+
this.schema.options.toObject.virtuals || null
|
|
97
100
|
}));
|
|
98
101
|
}
|
|
99
102
|
});
|
|
@@ -104,7 +107,7 @@ function defineKey({ prop, subprops, prototype, prefix, options }) {
|
|
|
104
107
|
writable: false,
|
|
105
108
|
value: function() {
|
|
106
109
|
return _this.get(path, null, {
|
|
107
|
-
virtuals:
|
|
110
|
+
virtuals: this && this.schema && this.schema.options && this.schema.options.toObject && this.schema.options.toObject.virtuals || null
|
|
108
111
|
});
|
|
109
112
|
}
|
|
110
113
|
});
|
|
@@ -115,7 +118,7 @@ function defineKey({ prop, subprops, prototype, prefix, options }) {
|
|
|
115
118
|
writable: false,
|
|
116
119
|
value: function() {
|
|
117
120
|
return _this.get(path, null, {
|
|
118
|
-
virtuals:
|
|
121
|
+
virtuals: this && this.schema && this.schema.options && this.schema.options.toJSON && this.schema.options.toJSON.virtuals || null
|
|
119
122
|
});
|
|
120
123
|
}
|
|
121
124
|
});
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const functionNameRE = /^function\s*([^\s(]+)/;
|
|
4
|
+
|
|
3
5
|
module.exports = function(fn) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
return (
|
|
7
|
+
fn.name ||
|
|
8
|
+
(fn.toString().trim().match(functionNameRE) || [])[1]
|
|
9
|
+
);
|
|
8
10
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const isMongooseArray = require('../types/array/isMongooseArray').isMongooseArray;
|
|
3
4
|
/*!
|
|
4
5
|
* Returns if `v` is a mongoose object that has a `toObject()` method we can use.
|
|
5
6
|
*
|
|
@@ -10,12 +11,12 @@
|
|
|
10
11
|
*/
|
|
11
12
|
|
|
12
13
|
module.exports = function(v) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
return (
|
|
15
|
+
v != null && (
|
|
16
|
+
isMongooseArray(v) || // Array or Document Array
|
|
17
|
+
v.$__ != null || // Document
|
|
18
|
+
v.isMongooseBuffer || // Buffer
|
|
19
|
+
v.$isMongooseMap // Map
|
|
20
|
+
)
|
|
21
|
+
);
|
|
21
22
|
};
|
package/lib/helpers/isObject.js
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
module.exports = function(arg) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
return (
|
|
13
|
+
Buffer.isBuffer(arg) ||
|
|
14
|
+
Object.prototype.toString.call(arg) === '[object Object]'
|
|
15
|
+
);
|
|
16
16
|
};
|
|
@@ -17,6 +17,7 @@ const CUSTOMIZABLE_DISCRIMINATOR_OPTIONS = {
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
module.exports = function discriminator(model, name, schema, tiedValue, applyPlugins) {
|
|
20
|
+
|
|
20
21
|
if (!(schema && schema.instanceOfSchema)) {
|
|
21
22
|
throw new Error('You must pass a valid discriminator Schema');
|
|
22
23
|
}
|
|
@@ -201,7 +202,7 @@ module.exports = function discriminator(model, name, schema, tiedValue, applyPlu
|
|
|
201
202
|
|
|
202
203
|
model.schema.discriminators[name] = schema;
|
|
203
204
|
|
|
204
|
-
if (model.discriminators[name]) {
|
|
205
|
+
if (model.discriminators[name] && !schema.options.overwriteModels) {
|
|
205
206
|
throw new Error('Discriminator with name "' + name + '" already exists');
|
|
206
207
|
}
|
|
207
208
|
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const dotRE = /\./g;
|
|
3
4
|
module.exports = function parentPaths(path) {
|
|
4
|
-
|
|
5
|
+
if (path.indexOf('.') === -1) {
|
|
6
|
+
return [path];
|
|
7
|
+
}
|
|
8
|
+
const pieces = path.split(dotRE);
|
|
9
|
+
const len = pieces.length;
|
|
10
|
+
const ret = new Array(len);
|
|
5
11
|
let cur = '';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
ret.push(cur);
|
|
12
|
+
for (let i = 0; i < len; ++i) {
|
|
13
|
+
cur += (cur.length !== 0) ? '.' + pieces[i] : pieces[i];
|
|
14
|
+
ret[i] = cur;
|
|
10
15
|
}
|
|
11
16
|
|
|
12
17
|
return ret;
|
|
@@ -35,11 +35,13 @@ function assignRawDocsToIdStructure(rawIds, resultDocs, resultOrder, options, re
|
|
|
35
35
|
let sid;
|
|
36
36
|
let id;
|
|
37
37
|
|
|
38
|
-
if (rawIds
|
|
38
|
+
if (utils.isMongooseArray(rawIds)) {
|
|
39
39
|
rawIds = rawIds.__array;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
let i = 0;
|
|
43
|
+
const len = rawIds.length;
|
|
44
|
+
for (i = 0; i < len; ++i) {
|
|
43
45
|
id = rawIds[i];
|
|
44
46
|
|
|
45
47
|
if (Array.isArray(id)) {
|
|
@@ -241,15 +241,19 @@ function valueFilter(val, assignmentOpts, populateOptions, allIds) {
|
|
|
241
241
|
}
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
+
const rLen = ret.length;
|
|
244
245
|
// Since we don't want to have to create a new mongoosearray, make sure to
|
|
245
246
|
// modify the array in place
|
|
246
|
-
while (val.length >
|
|
247
|
+
while (val.length > rLen) {
|
|
247
248
|
Array.prototype.pop.apply(val, []);
|
|
248
249
|
}
|
|
249
|
-
|
|
250
|
-
|
|
250
|
+
let i = 0;
|
|
251
|
+
if (utils.isMongooseArray(val)) {
|
|
252
|
+
for (i = 0; i < rLen; ++i) {
|
|
251
253
|
val.set(i, ret[i], true);
|
|
252
|
-
}
|
|
254
|
+
}
|
|
255
|
+
} else {
|
|
256
|
+
for (i = 0; i < rLen; ++i) {
|
|
253
257
|
val[i] = ret[i];
|
|
254
258
|
}
|
|
255
259
|
}
|
|
@@ -43,7 +43,7 @@ module.exports = function getModelsMapForPopulate(model, docs, options) {
|
|
|
43
43
|
let allSchemaTypes = getSchemaTypes(model, modelSchema, null, options.path);
|
|
44
44
|
allSchemaTypes = Array.isArray(allSchemaTypes) ? allSchemaTypes : [allSchemaTypes].filter(v => v != null);
|
|
45
45
|
|
|
46
|
-
if (allSchemaTypes.length
|
|
46
|
+
if (allSchemaTypes.length === 0 && options.strictPopulate !== false && options._localModel != null) {
|
|
47
47
|
return new MongooseError('Cannot populate path `' + (options._fullPath || options.path) +
|
|
48
48
|
'` because it is not in your schema. Set the `strictPopulate` option ' +
|
|
49
49
|
'to false to override.');
|
|
@@ -607,7 +607,7 @@ function _getLocalFieldValues(doc, localField, model, options, virtual, schema)
|
|
|
607
607
|
const getters = 'getters' in _populateOptions ?
|
|
608
608
|
_populateOptions.getters :
|
|
609
609
|
get(virtual, 'options.getters', false);
|
|
610
|
-
if (localFieldGetters.length
|
|
610
|
+
if (localFieldGetters.length !== 0 && getters) {
|
|
611
611
|
const hydratedDoc = (doc.$__ != null) ? doc : model.hydrate(doc);
|
|
612
612
|
const localFieldValue = utils.getValue(localField, doc);
|
|
613
613
|
if (Array.isArray(localFieldValue)) {
|
|
@@ -644,7 +644,7 @@ function convertTo_id(val, schema) {
|
|
|
644
644
|
rawVal[i] = rawVal[i]._id;
|
|
645
645
|
}
|
|
646
646
|
}
|
|
647
|
-
if (
|
|
647
|
+
if (utils.isMongooseArray(val) && val.$schema()) {
|
|
648
648
|
return val.$schema()._castForPopulate(val, val.$parent());
|
|
649
649
|
}
|
|
650
650
|
|
|
@@ -694,7 +694,7 @@ function _findRefPathForDiscriminators(doc, modelSchema, data, options, normaliz
|
|
|
694
694
|
if (schematype != null &&
|
|
695
695
|
schematype.$isMongooseArray &&
|
|
696
696
|
schematype.caster.discriminators != null &&
|
|
697
|
-
Object.keys(schematype.caster.discriminators).length
|
|
697
|
+
Object.keys(schematype.caster.discriminators).length !== 0) {
|
|
698
698
|
const subdocs = utils.getValue(cur, doc);
|
|
699
699
|
const remnant = options.path.substring(cur.length + 1);
|
|
700
700
|
const discriminatorKey = schematype.caster.schema.options.discriminatorKey;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const utils = require('../../utils');
|
|
4
|
+
|
|
3
5
|
/*!
|
|
4
6
|
* If populating a path within a document array, make sure each
|
|
5
7
|
* subdoc within the array knows its subpaths are populated.
|
|
@@ -29,7 +31,7 @@ module.exports = function markArraySubdocsPopulated(doc, populated) {
|
|
|
29
31
|
continue;
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
if (
|
|
34
|
+
if (utils.isMongooseDocumentArray(val)) {
|
|
33
35
|
for (let j = 0; j < val.length; ++j) {
|
|
34
36
|
val[j].populated(rest, item._docs[id] == null ? void 0 : item._docs[id][j], item);
|
|
35
37
|
}
|
|
@@ -7,6 +7,8 @@ const mpath = require('mpath');
|
|
|
7
7
|
const util = require('util');
|
|
8
8
|
const utils = require('../../utils');
|
|
9
9
|
|
|
10
|
+
const hasNumericPropRE = /(\.\d+$|\.\d+\.)/g;
|
|
11
|
+
|
|
10
12
|
module.exports = function modelNamesFromRefPath(refPath, doc, populatedPath, modelSchema, queryProjection) {
|
|
11
13
|
if (refPath == null) {
|
|
12
14
|
return [];
|
|
@@ -20,10 +22,9 @@ module.exports = function modelNamesFromRefPath(refPath, doc, populatedPath, mod
|
|
|
20
22
|
// If populated path has numerics, the end `refPath` should too. For example,
|
|
21
23
|
// if populating `a.0.b` instead of `a.b` and `b` has `refPath = a.c`, we
|
|
22
24
|
// should return `a.0.c` for the refPath.
|
|
23
|
-
const hasNumericProp = /(\.\d+$|\.\d+\.)/g;
|
|
24
25
|
|
|
25
|
-
if (
|
|
26
|
-
const chunks = populatedPath.split(
|
|
26
|
+
if (hasNumericPropRE.test(populatedPath)) {
|
|
27
|
+
const chunks = populatedPath.split(hasNumericPropRE);
|
|
27
28
|
|
|
28
29
|
if (chunks[chunks.length - 1] === '') {
|
|
29
30
|
throw new Error('Can\'t populate individual element in an array');
|
|
@@ -6,12 +6,12 @@ if (typeof jest !== 'undefined' && typeof window !== 'undefined') {
|
|
|
6
6
|
utils.warn('Mongoose: looks like you\'re trying to test a Mongoose app ' +
|
|
7
7
|
'with Jest\'s default jsdom test environment. Please make sure you read ' +
|
|
8
8
|
'Mongoose\'s docs on configuring Jest to test Node.js apps: ' +
|
|
9
|
-
'
|
|
9
|
+
'https://mongoosejs.com/docs/jest.html');
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
if (typeof jest !== 'undefined' && process.nextTick.toString().indexOf('nextTick') === -1) {
|
|
13
13
|
utils.warn('Mongoose: looks like you\'re trying to test a Mongoose app ' +
|
|
14
14
|
'with Jest\'s mock timers enabled. Please make sure you read ' +
|
|
15
15
|
'Mongoose\'s docs on configuring Jest to test Node.js apps: ' +
|
|
16
|
-
'
|
|
16
|
+
'https://mongoosejs.com/docs/jest.html');
|
|
17
17
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const hasIncludedChildren = require('./hasIncludedChildren');
|
|
4
|
+
const isExclusive = require('./isExclusive');
|
|
5
|
+
const isInclusive = require('./isInclusive');
|
|
6
|
+
const isPOJO = require('../../utils').isPOJO;
|
|
7
|
+
|
|
8
|
+
module.exports = function applyProjection(doc, projection, _hasIncludedChildren) {
|
|
9
|
+
if (projection == null) {
|
|
10
|
+
return doc;
|
|
11
|
+
}
|
|
12
|
+
if (doc == null) {
|
|
13
|
+
return doc;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let exclude = null;
|
|
17
|
+
if (isInclusive(projection)) {
|
|
18
|
+
exclude = false;
|
|
19
|
+
} else if (isExclusive(projection)) {
|
|
20
|
+
exclude = true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (exclude == null) {
|
|
24
|
+
return doc;
|
|
25
|
+
} else if (exclude) {
|
|
26
|
+
_hasIncludedChildren = _hasIncludedChildren || hasIncludedChildren(projection);
|
|
27
|
+
return applyExclusiveProjection(doc, projection, _hasIncludedChildren);
|
|
28
|
+
} else {
|
|
29
|
+
_hasIncludedChildren = _hasIncludedChildren || hasIncludedChildren(projection);
|
|
30
|
+
return applyInclusiveProjection(doc, projection, _hasIncludedChildren);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
function applyExclusiveProjection(doc, projection, hasIncludedChildren, projectionLimb, prefix) {
|
|
35
|
+
if (doc == null || typeof doc !== 'object') {
|
|
36
|
+
return doc;
|
|
37
|
+
}
|
|
38
|
+
const ret = { ...doc };
|
|
39
|
+
projectionLimb = prefix ? (projectionLimb || {}) : projection;
|
|
40
|
+
|
|
41
|
+
for (const key of Object.keys(ret)) {
|
|
42
|
+
const fullPath = prefix ? prefix + '.' + key : key;
|
|
43
|
+
if (projection.hasOwnProperty(fullPath) || projectionLimb.hasOwnProperty(key)) {
|
|
44
|
+
if (isPOJO(projection[fullPath]) || isPOJO(projectionLimb[key])) {
|
|
45
|
+
ret[key] = applyExclusiveProjection(ret[key], projection, hasIncludedChildren, projectionLimb[key], fullPath);
|
|
46
|
+
} else {
|
|
47
|
+
delete ret[key];
|
|
48
|
+
}
|
|
49
|
+
} else if (hasIncludedChildren[fullPath]) {
|
|
50
|
+
ret[key] = applyExclusiveProjection(ret[key], projection, hasIncludedChildren, projectionLimb[key], fullPath);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return ret;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function applyInclusiveProjection(doc, projection, hasIncludedChildren, projectionLimb, prefix) {
|
|
57
|
+
if (doc == null || typeof doc !== 'object') {
|
|
58
|
+
return doc;
|
|
59
|
+
}
|
|
60
|
+
const ret = { ...doc };
|
|
61
|
+
projectionLimb = prefix ? (projectionLimb || {}) : projection;
|
|
62
|
+
|
|
63
|
+
for (const key of Object.keys(ret)) {
|
|
64
|
+
const fullPath = prefix ? prefix + '.' + key : key;
|
|
65
|
+
if (projection.hasOwnProperty(fullPath) || projectionLimb.hasOwnProperty(key)) {
|
|
66
|
+
if (isPOJO(projection[fullPath]) || isPOJO(projectionLimb[key])) {
|
|
67
|
+
ret[key] = applyInclusiveProjection(ret[key], projection, hasIncludedChildren, projectionLimb[key], fullPath);
|
|
68
|
+
}
|
|
69
|
+
continue;
|
|
70
|
+
} else if (hasIncludedChildren[fullPath]) {
|
|
71
|
+
ret[key] = applyInclusiveProjection(ret[key], projection, hasIncludedChildren, projectionLimb[key], fullPath);
|
|
72
|
+
} else {
|
|
73
|
+
delete ret[key];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return ret;
|
|
77
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/*!
|
|
4
|
+
* Creates an object that precomputes whether a given path has child fields in
|
|
5
|
+
* the projection.
|
|
6
|
+
*
|
|
7
|
+
* ####Example:
|
|
8
|
+
* const res = hasIncludedChildren({ 'a.b.c': 0 });
|
|
9
|
+
* res.a; // 1
|
|
10
|
+
* res['a.b']; // 1
|
|
11
|
+
* res['a.b.c']; // 1
|
|
12
|
+
* res['a.c']; // undefined
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
module.exports = function hasIncludedChildren(fields) {
|
|
16
|
+
const hasIncludedChildren = {};
|
|
17
|
+
const keys = Object.keys(fields);
|
|
18
|
+
|
|
19
|
+
for (const key of keys) {
|
|
20
|
+
if (key.indexOf('.') === -1) {
|
|
21
|
+
hasIncludedChildren[key] = 1;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
const parts = key.split('.');
|
|
25
|
+
let c = parts[0];
|
|
26
|
+
|
|
27
|
+
for (let i = 0; i < parts.length; ++i) {
|
|
28
|
+
hasIncludedChildren[c] = 1;
|
|
29
|
+
if (i + 1 < parts.length) {
|
|
30
|
+
c = c + '.' + parts[i + 1];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return hasIncludedChildren;
|
|
36
|
+
};
|
|
@@ -21,8 +21,11 @@ module.exports = function isExclusive(projection) {
|
|
|
21
21
|
while (ki--) {
|
|
22
22
|
// Does this projection explicitly define inclusion/exclusion?
|
|
23
23
|
// Explicitly avoid `$meta` and `$slice`
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
const key = keys[ki];
|
|
25
|
+
if (key !== '_id' && isDefiningProjection(projection[key])) {
|
|
26
|
+
exclude = (projection[key] != null && typeof projection[key] === 'object') ?
|
|
27
|
+
isExclusive(projection[key]) :
|
|
28
|
+
!projection[key];
|
|
26
29
|
break;
|
|
27
30
|
}
|
|
28
31
|
}
|