Initial commit
This commit is contained in:
50
node_modules/mongoose/lib/helpers/aggregate/stringifyFunctionOperators.js
generated
vendored
Normal file
50
node_modules/mongoose/lib/helpers/aggregate/stringifyFunctionOperators.js
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function stringifyFunctionOperators(pipeline) {
|
||||
if (!Array.isArray(pipeline)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const stage of pipeline) {
|
||||
if (stage == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const canHaveAccumulator = stage.$group || stage.$bucket || stage.$bucketAuto;
|
||||
if (canHaveAccumulator != null) {
|
||||
for (const key of Object.keys(canHaveAccumulator)) {
|
||||
handleAccumulator(canHaveAccumulator[key]);
|
||||
}
|
||||
}
|
||||
|
||||
const stageType = Object.keys(stage)[0];
|
||||
if (stageType && typeof stage[stageType] === 'object') {
|
||||
const stageOptions = stage[stageType];
|
||||
for (const key of Object.keys(stageOptions)) {
|
||||
if (stageOptions[key] != null &&
|
||||
stageOptions[key].$function != null &&
|
||||
typeof stageOptions[key].$function.body === 'function') {
|
||||
stageOptions[key].$function.body = stageOptions[key].$function.body.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (stage.$facet != null) {
|
||||
for (const key of Object.keys(stage.$facet)) {
|
||||
stringifyFunctionOperators(stage.$facet[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function handleAccumulator(operator) {
|
||||
if (operator == null || operator.$accumulator == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const key of ['init', 'accumulate', 'merge', 'finalize']) {
|
||||
if (typeof operator.$accumulator[key] === 'function') {
|
||||
operator.$accumulator[key] = String(operator.$accumulator[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
33
node_modules/mongoose/lib/helpers/arrayDepth.js
generated
vendored
Normal file
33
node_modules/mongoose/lib/helpers/arrayDepth.js
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = arrayDepth;
|
||||
|
||||
function arrayDepth(arr) {
|
||||
if (!Array.isArray(arr)) {
|
||||
return { min: 0, max: 0, containsNonArrayItem: true };
|
||||
}
|
||||
if (arr.length === 0) {
|
||||
return { min: 1, max: 1, containsNonArrayItem: false };
|
||||
}
|
||||
if (arr.length === 1 && !Array.isArray(arr[0])) {
|
||||
return { min: 1, max: 1, containsNonArrayItem: false };
|
||||
}
|
||||
|
||||
const res = arrayDepth(arr[0]);
|
||||
|
||||
for (let i = 1; i < arr.length; ++i) {
|
||||
const _res = arrayDepth(arr[i]);
|
||||
if (_res.min < res.min) {
|
||||
res.min = _res.min;
|
||||
}
|
||||
if (_res.max > res.max) {
|
||||
res.max = _res.max;
|
||||
}
|
||||
res.containsNonArrayItem = res.containsNonArrayItem || _res.containsNonArrayItem;
|
||||
}
|
||||
|
||||
res.min = res.min + 1;
|
||||
res.max = res.max + 1;
|
||||
|
||||
return res;
|
||||
}
|
||||
143
node_modules/mongoose/lib/helpers/clone.js
generated
vendored
Normal file
143
node_modules/mongoose/lib/helpers/clone.js
generated
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
'use strict';
|
||||
|
||||
|
||||
const cloneRegExp = require('regexp-clone');
|
||||
const Decimal = require('../types/decimal128');
|
||||
const ObjectId = require('../types/objectid');
|
||||
const specialProperties = require('./specialProperties');
|
||||
const isMongooseObject = require('./isMongooseObject');
|
||||
const getFunctionName = require('./getFunctionName');
|
||||
const isBsonType = require('./isBsonType');
|
||||
const isObject = require('./isObject');
|
||||
const symbols = require('./symbols');
|
||||
const utils = require('../utils');
|
||||
|
||||
|
||||
/*!
|
||||
* Object clone with Mongoose natives support.
|
||||
*
|
||||
* If options.minimize is true, creates a minimal data object. Empty objects and undefined values will not be cloned. This makes the data payload sent to MongoDB as small as possible.
|
||||
*
|
||||
* Functions are never cloned.
|
||||
*
|
||||
* @param {Object} obj the object to clone
|
||||
* @param {Object} options
|
||||
* @param {Boolean} isArrayChild true if cloning immediately underneath an array. Special case for minimize.
|
||||
* @return {Object} the cloned object
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function clone(obj, options, isArrayChild) {
|
||||
if (obj == null) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
return cloneArray(obj, options);
|
||||
}
|
||||
|
||||
if (isMongooseObject(obj)) {
|
||||
// Single nested subdocs should apply getters later in `applyGetters()`
|
||||
// when calling `toObject()`. See gh-7442, gh-8295
|
||||
if (options && options._skipSingleNestedGetters && obj.$isSingleNested) {
|
||||
options = Object.assign({}, options, { getters: false });
|
||||
}
|
||||
|
||||
if (utils.isPOJO(obj) && obj.$__ != null && obj._doc != null) {
|
||||
return obj._doc;
|
||||
}
|
||||
|
||||
if (options && options.json && typeof obj.toJSON === 'function') {
|
||||
return obj.toJSON(options);
|
||||
}
|
||||
return obj.toObject(options);
|
||||
}
|
||||
|
||||
if (obj.constructor) {
|
||||
switch (getFunctionName(obj.constructor)) {
|
||||
case 'Object':
|
||||
return cloneObject(obj, options, isArrayChild);
|
||||
case 'Date':
|
||||
return new obj.constructor(+obj);
|
||||
case 'RegExp':
|
||||
return cloneRegExp(obj);
|
||||
default:
|
||||
// ignore
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (obj instanceof ObjectId) {
|
||||
return new ObjectId(obj.id);
|
||||
}
|
||||
|
||||
if (isBsonType(obj, 'Decimal128')) {
|
||||
if (options && options.flattenDecimals) {
|
||||
return obj.toJSON();
|
||||
}
|
||||
return Decimal.fromString(obj.toString());
|
||||
}
|
||||
|
||||
if (!obj.constructor && isObject(obj)) {
|
||||
// object created with Object.create(null)
|
||||
return cloneObject(obj, options, isArrayChild);
|
||||
}
|
||||
|
||||
if (obj[symbols.schemaTypeSymbol]) {
|
||||
return obj.clone();
|
||||
}
|
||||
|
||||
// If we're cloning this object to go into a MongoDB command,
|
||||
// and there's a `toBSON()` function, assume this object will be
|
||||
// stored as a primitive in MongoDB and doesn't need to be cloned.
|
||||
if (options && options.bson && typeof obj.toBSON === 'function') {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (obj.valueOf != null) {
|
||||
return obj.valueOf();
|
||||
}
|
||||
|
||||
return cloneObject(obj, options, isArrayChild);
|
||||
}
|
||||
module.exports = clone;
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
function cloneObject(obj, options, isArrayChild) {
|
||||
const minimize = options && options.minimize;
|
||||
const ret = {};
|
||||
let hasKeys;
|
||||
|
||||
for (const k of Object.keys(obj)) {
|
||||
if (specialProperties.has(k)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Don't pass `isArrayChild` down
|
||||
const val = clone(obj[k], options);
|
||||
|
||||
if (!minimize || (typeof val !== 'undefined')) {
|
||||
if (minimize === false && typeof val === 'undefined') {
|
||||
delete ret[k];
|
||||
} else {
|
||||
hasKeys || (hasKeys = true);
|
||||
ret[k] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return minimize && !isArrayChild ? hasKeys && ret : ret;
|
||||
}
|
||||
|
||||
function cloneArray(arr, options) {
|
||||
const ret = [];
|
||||
|
||||
for (const item of arr) {
|
||||
ret.push(clone(item, options, true));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
106
node_modules/mongoose/lib/helpers/common.js
generated
vendored
Normal file
106
node_modules/mongoose/lib/helpers/common.js
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
'use strict';
|
||||
|
||||
/*!
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
const Binary = require('../driver').get().Binary;
|
||||
const Decimal128 = require('../types/decimal128');
|
||||
const ObjectId = require('../types/objectid');
|
||||
const isMongooseObject = require('./isMongooseObject');
|
||||
|
||||
exports.flatten = flatten;
|
||||
exports.modifiedPaths = modifiedPaths;
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
function flatten(update, path, options, schema) {
|
||||
let keys;
|
||||
if (update && isMongooseObject(update) && !Buffer.isBuffer(update)) {
|
||||
keys = Object.keys(update.toObject({ transform: false, virtuals: false }));
|
||||
} else {
|
||||
keys = Object.keys(update || {});
|
||||
}
|
||||
|
||||
const numKeys = keys.length;
|
||||
const result = {};
|
||||
path = path ? path + '.' : '';
|
||||
|
||||
for (let i = 0; i < numKeys; ++i) {
|
||||
const key = keys[i];
|
||||
const val = update[key];
|
||||
result[path + key] = val;
|
||||
|
||||
// Avoid going into mixed paths if schema is specified
|
||||
const keySchema = schema && schema.path && schema.path(path + key);
|
||||
const isNested = schema && schema.nested && schema.nested[path + key];
|
||||
if (keySchema && keySchema.instance === 'Mixed') continue;
|
||||
|
||||
if (shouldFlatten(val)) {
|
||||
if (options && options.skipArrays && Array.isArray(val)) {
|
||||
continue;
|
||||
}
|
||||
const flat = flatten(val, path + key, options, schema);
|
||||
for (const k in flat) {
|
||||
result[k] = flat[k];
|
||||
}
|
||||
if (Array.isArray(val)) {
|
||||
result[path + key] = val;
|
||||
}
|
||||
}
|
||||
|
||||
if (isNested) {
|
||||
const paths = Object.keys(schema.paths);
|
||||
for (const p of paths) {
|
||||
if (p.startsWith(path + key + '.') && !result.hasOwnProperty(p)) {
|
||||
result[p] = void 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
function modifiedPaths(update, path, result) {
|
||||
const keys = Object.keys(update || {});
|
||||
const numKeys = keys.length;
|
||||
result = result || {};
|
||||
path = path ? path + '.' : '';
|
||||
|
||||
for (let i = 0; i < numKeys; ++i) {
|
||||
const key = keys[i];
|
||||
let val = update[key];
|
||||
|
||||
result[path + key] = true;
|
||||
if (isMongooseObject(val) && !Buffer.isBuffer(val)) {
|
||||
val = val.toObject({ transform: false, virtuals: false });
|
||||
}
|
||||
if (shouldFlatten(val)) {
|
||||
modifiedPaths(val, path + key, result);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
function shouldFlatten(val) {
|
||||
return val &&
|
||||
typeof val === 'object' &&
|
||||
!(val instanceof Date) &&
|
||||
!(val instanceof ObjectId) &&
|
||||
(!Array.isArray(val) || val.length > 0) &&
|
||||
!(val instanceof Buffer) &&
|
||||
!(val instanceof Decimal128) &&
|
||||
!(val instanceof Binary);
|
||||
}
|
||||
157
node_modules/mongoose/lib/helpers/cursor/eachAsync.js
generated
vendored
Normal file
157
node_modules/mongoose/lib/helpers/cursor/eachAsync.js
generated
vendored
Normal file
@@ -0,0 +1,157 @@
|
||||
'use strict';
|
||||
|
||||
/*!
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
const immediate = require('../immediate');
|
||||
const promiseOrCallback = require('../promiseOrCallback');
|
||||
|
||||
/**
|
||||
* Execute `fn` for every document in the cursor. If `fn` returns a promise,
|
||||
* will wait for the promise to resolve before iterating on to the next one.
|
||||
* Returns a promise that resolves when done.
|
||||
*
|
||||
* @param {Function} next the thunk to call to get the next document
|
||||
* @param {Function} fn
|
||||
* @param {Object} options
|
||||
* @param {Function} [callback] executed when all docs have been processed
|
||||
* @return {Promise}
|
||||
* @api public
|
||||
* @method eachAsync
|
||||
*/
|
||||
|
||||
module.exports = function eachAsync(next, fn, options, callback) {
|
||||
const parallel = options.parallel || 1;
|
||||
const batchSize = options.batchSize;
|
||||
const enqueue = asyncQueue();
|
||||
|
||||
return promiseOrCallback(callback, cb => {
|
||||
if (batchSize != null) {
|
||||
if (typeof batchSize !== 'number') {
|
||||
throw new TypeError('batchSize must be a number');
|
||||
}
|
||||
if (batchSize < 1) {
|
||||
throw new TypeError('batchSize must be at least 1');
|
||||
}
|
||||
if (batchSize !== Math.floor(batchSize)) {
|
||||
throw new TypeError('batchSize must be a positive integer');
|
||||
}
|
||||
}
|
||||
|
||||
iterate(cb);
|
||||
});
|
||||
|
||||
function iterate(finalCallback) {
|
||||
let drained = false;
|
||||
let handleResultsInProgress = 0;
|
||||
let currentDocumentIndex = 0;
|
||||
let documentsBatch = [];
|
||||
|
||||
let error = null;
|
||||
for (let i = 0; i < parallel; ++i) {
|
||||
enqueue(fetch);
|
||||
}
|
||||
|
||||
function fetch(done) {
|
||||
if (drained || error) {
|
||||
return done();
|
||||
}
|
||||
|
||||
next(function(err, doc) {
|
||||
if (drained || error != null) {
|
||||
return done();
|
||||
}
|
||||
if (err != null) {
|
||||
error = err;
|
||||
finalCallback(err);
|
||||
return done();
|
||||
}
|
||||
if (doc == null) {
|
||||
drained = true;
|
||||
if (handleResultsInProgress <= 0) {
|
||||
finalCallback(null);
|
||||
} else if (batchSize != null && documentsBatch.length) {
|
||||
handleNextResult(documentsBatch, currentDocumentIndex++, handleNextResultCallBack);
|
||||
}
|
||||
return done();
|
||||
}
|
||||
|
||||
++handleResultsInProgress;
|
||||
|
||||
// Kick off the subsequent `next()` before handling the result, but
|
||||
// make sure we know that we still have a result to handle re: #8422
|
||||
immediate(() => done());
|
||||
|
||||
if (batchSize != null) {
|
||||
documentsBatch.push(doc);
|
||||
}
|
||||
|
||||
// If the current documents size is less than the provided patch size don't process the documents yet
|
||||
if (batchSize != null && documentsBatch.length !== batchSize) {
|
||||
setTimeout(() => enqueue(fetch), 0);
|
||||
return;
|
||||
}
|
||||
|
||||
const docsToProcess = batchSize != null ? documentsBatch : doc;
|
||||
|
||||
function handleNextResultCallBack(err) {
|
||||
if (batchSize != null) {
|
||||
handleResultsInProgress -= documentsBatch.length;
|
||||
documentsBatch = [];
|
||||
} else {
|
||||
--handleResultsInProgress;
|
||||
}
|
||||
if (err != null) {
|
||||
error = err;
|
||||
return finalCallback(err);
|
||||
}
|
||||
if (drained && handleResultsInProgress <= 0) {
|
||||
return finalCallback(null);
|
||||
}
|
||||
|
||||
setTimeout(() => enqueue(fetch), 0);
|
||||
}
|
||||
|
||||
handleNextResult(docsToProcess, currentDocumentIndex++, handleNextResultCallBack);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handleNextResult(doc, i, callback) {
|
||||
const promise = fn(doc, i);
|
||||
if (promise && typeof promise.then === 'function') {
|
||||
promise.then(
|
||||
function() { callback(null); },
|
||||
function(error) { callback(error || new Error('`eachAsync()` promise rejected without error')); });
|
||||
} else {
|
||||
callback(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// `next()` can only execute one at a time, so make sure we always execute
|
||||
// `next()` in series, while still allowing multiple `fn()` instances to run
|
||||
// in parallel.
|
||||
function asyncQueue() {
|
||||
const _queue = [];
|
||||
let inProgress = null;
|
||||
let id = 0;
|
||||
|
||||
return function enqueue(fn) {
|
||||
if (_queue.length === 0 && inProgress == null) {
|
||||
inProgress = id++;
|
||||
return fn(_step);
|
||||
}
|
||||
_queue.push(fn);
|
||||
};
|
||||
|
||||
function _step() {
|
||||
inProgress = null;
|
||||
if (_queue.length > 0) {
|
||||
inProgress = id++;
|
||||
const fn = _queue.shift();
|
||||
fn(_step);
|
||||
}
|
||||
}
|
||||
}
|
||||
16
node_modules/mongoose/lib/helpers/discriminator/areDiscriminatorValuesEqual.js
generated
vendored
Normal file
16
node_modules/mongoose/lib/helpers/discriminator/areDiscriminatorValuesEqual.js
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
const ObjectId = require('../../types/objectid');
|
||||
|
||||
module.exports = function areDiscriminatorValuesEqual(a, b) {
|
||||
if (typeof a === 'string' && typeof b === 'string') {
|
||||
return a === b;
|
||||
}
|
||||
if (typeof a === 'number' && typeof b === 'number') {
|
||||
return a === b;
|
||||
}
|
||||
if (a instanceof ObjectId && b instanceof ObjectId) {
|
||||
return a.toString() === b.toString();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
12
node_modules/mongoose/lib/helpers/discriminator/checkEmbeddedDiscriminatorKeyProjection.js
generated
vendored
Normal file
12
node_modules/mongoose/lib/helpers/discriminator/checkEmbeddedDiscriminatorKeyProjection.js
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function checkEmbeddedDiscriminatorKeyProjection(userProjection, path, schema, selected, addedPaths) {
|
||||
const userProjectedInPath = Object.keys(userProjection).
|
||||
reduce((cur, key) => cur || key.startsWith(path + '.'), false);
|
||||
const _discriminatorKey = path + '.' + schema.options.discriminatorKey;
|
||||
if (!userProjectedInPath &&
|
||||
addedPaths.length === 1 &&
|
||||
addedPaths[0] === _discriminatorKey) {
|
||||
selected.splice(selected.indexOf(_discriminatorKey), 1);
|
||||
}
|
||||
};
|
||||
25
node_modules/mongoose/lib/helpers/discriminator/getConstructor.js
generated
vendored
Normal file
25
node_modules/mongoose/lib/helpers/discriminator/getConstructor.js
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
'use strict';
|
||||
|
||||
const getDiscriminatorByValue = require('./getDiscriminatorByValue');
|
||||
|
||||
/*!
|
||||
* Find the correct constructor, taking into account discriminators
|
||||
*/
|
||||
|
||||
module.exports = function getConstructor(Constructor, value) {
|
||||
const discriminatorKey = Constructor.schema.options.discriminatorKey;
|
||||
if (value != null &&
|
||||
Constructor.discriminators &&
|
||||
value[discriminatorKey] != null) {
|
||||
if (Constructor.discriminators[value[discriminatorKey]]) {
|
||||
Constructor = Constructor.discriminators[value[discriminatorKey]];
|
||||
} else {
|
||||
const constructorByValue = getDiscriminatorByValue(Constructor.discriminators, value[discriminatorKey]);
|
||||
if (constructorByValue) {
|
||||
Constructor = constructorByValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Constructor;
|
||||
};
|
||||
27
node_modules/mongoose/lib/helpers/discriminator/getDiscriminatorByValue.js
generated
vendored
Normal file
27
node_modules/mongoose/lib/helpers/discriminator/getDiscriminatorByValue.js
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
'use strict';
|
||||
|
||||
const areDiscriminatorValuesEqual = require('./areDiscriminatorValuesEqual');
|
||||
|
||||
/*!
|
||||
* returns discriminator by discriminatorMapping.value
|
||||
*
|
||||
* @param {Model} model
|
||||
* @param {string} value
|
||||
*/
|
||||
|
||||
module.exports = function getDiscriminatorByValue(discriminators, value) {
|
||||
if (discriminators == null) {
|
||||
return null;
|
||||
}
|
||||
for (const name of Object.keys(discriminators)) {
|
||||
const it = discriminators[name];
|
||||
if (
|
||||
it.schema &&
|
||||
it.schema.discriminatorMapping &&
|
||||
areDiscriminatorValuesEqual(it.schema.discriminatorMapping.value, value)
|
||||
) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
26
node_modules/mongoose/lib/helpers/discriminator/getSchemaDiscriminatorByValue.js
generated
vendored
Normal file
26
node_modules/mongoose/lib/helpers/discriminator/getSchemaDiscriminatorByValue.js
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
'use strict';
|
||||
|
||||
const areDiscriminatorValuesEqual = require('./areDiscriminatorValuesEqual');
|
||||
|
||||
/*!
|
||||
* returns discriminator by discriminatorMapping.value
|
||||
*
|
||||
* @param {Schema} schema
|
||||
* @param {string} value
|
||||
*/
|
||||
|
||||
module.exports = function getSchemaDiscriminatorByValue(schema, value) {
|
||||
if (schema == null || schema.discriminators == null) {
|
||||
return null;
|
||||
}
|
||||
for (const key of Object.keys(schema.discriminators)) {
|
||||
const discriminatorSchema = schema.discriminators[key];
|
||||
if (discriminatorSchema.discriminatorMapping == null) {
|
||||
continue;
|
||||
}
|
||||
if (areDiscriminatorValuesEqual(discriminatorSchema.discriminatorMapping.value, value)) {
|
||||
return discriminatorSchema;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
28
node_modules/mongoose/lib/helpers/document/cleanModifiedSubpaths.js
generated
vendored
Normal file
28
node_modules/mongoose/lib/helpers/document/cleanModifiedSubpaths.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
'use strict';
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
module.exports = function cleanModifiedSubpaths(doc, path, options) {
|
||||
options = options || {};
|
||||
const skipDocArrays = options.skipDocArrays;
|
||||
|
||||
let deleted = 0;
|
||||
if (!doc) {
|
||||
return deleted;
|
||||
}
|
||||
for (const modifiedPath of Object.keys(doc.$__.activePaths.states.modify)) {
|
||||
if (skipDocArrays) {
|
||||
const schemaType = doc.$__schema.path(modifiedPath);
|
||||
if (schemaType && schemaType.$isMongooseDocumentArray) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (modifiedPath.startsWith(path + '.')) {
|
||||
delete doc.$__.activePaths.states.modify[modifiedPath];
|
||||
++deleted;
|
||||
}
|
||||
}
|
||||
return deleted;
|
||||
};
|
||||
211
node_modules/mongoose/lib/helpers/document/compile.js
generated
vendored
Normal file
211
node_modules/mongoose/lib/helpers/document/compile.js
generated
vendored
Normal file
@@ -0,0 +1,211 @@
|
||||
'use strict';
|
||||
|
||||
const documentSchemaSymbol = require('../../helpers/symbols').documentSchemaSymbol;
|
||||
const get = require('../../helpers/get');
|
||||
const internalToObjectOptions = require('../../options').internalToObjectOptions;
|
||||
const utils = require('../../utils');
|
||||
|
||||
let Document;
|
||||
const getSymbol = require('../../helpers/symbols').getSymbol;
|
||||
const scopeSymbol = require('../../helpers/symbols').scopeSymbol;
|
||||
|
||||
/*!
|
||||
* exports
|
||||
*/
|
||||
|
||||
exports.compile = compile;
|
||||
exports.defineKey = defineKey;
|
||||
|
||||
/*!
|
||||
* Compiles schemas.
|
||||
*/
|
||||
|
||||
function compile(tree, proto, prefix, options) {
|
||||
Document = Document || require('../../document');
|
||||
const keys = Object.keys(tree);
|
||||
const len = keys.length;
|
||||
let limb;
|
||||
let key;
|
||||
|
||||
for (let i = 0; i < len; ++i) {
|
||||
key = keys[i];
|
||||
limb = tree[key];
|
||||
|
||||
const hasSubprops = utils.isPOJO(limb) && Object.keys(limb).length &&
|
||||
(!limb[options.typeKey] || (options.typeKey === 'type' && limb.type.type));
|
||||
const subprops = hasSubprops ? limb : null;
|
||||
|
||||
defineKey(key, subprops, proto, prefix, keys, options);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Defines the accessor named prop on the incoming prototype.
|
||||
*/
|
||||
|
||||
function defineKey(prop, subprops, prototype, prefix, keys, options) {
|
||||
Document = Document || require('../../document');
|
||||
const path = (prefix ? prefix + '.' : '') + prop;
|
||||
prefix = prefix || '';
|
||||
|
||||
if (subprops) {
|
||||
Object.defineProperty(prototype, prop, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: function() {
|
||||
const _this = this;
|
||||
if (!this.$__.getters) {
|
||||
this.$__.getters = {};
|
||||
}
|
||||
|
||||
if (!this.$__.getters[path]) {
|
||||
const nested = Object.create(Document.prototype, getOwnPropertyDescriptors(this));
|
||||
|
||||
// save scope for nested getters/setters
|
||||
if (!prefix) {
|
||||
nested.$__[scopeSymbol] = this;
|
||||
}
|
||||
nested.$__.nestedPath = path;
|
||||
|
||||
Object.defineProperty(nested, 'schema', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: false,
|
||||
value: prototype.schema
|
||||
});
|
||||
|
||||
Object.defineProperty(nested, '$__schema', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: false,
|
||||
value: prototype.schema
|
||||
});
|
||||
|
||||
Object.defineProperty(nested, documentSchemaSymbol, {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: false,
|
||||
value: prototype.schema
|
||||
});
|
||||
|
||||
Object.defineProperty(nested, 'toObject', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: false,
|
||||
value: function() {
|
||||
return utils.clone(_this.get(path, null, {
|
||||
virtuals: get(this, 'schema.options.toObject.virtuals', null)
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(nested, '$__get', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: false,
|
||||
value: function() {
|
||||
return _this.get(path, null, {
|
||||
virtuals: get(this, 'schema.options.toObject.virtuals', null)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(nested, 'toJSON', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: false,
|
||||
value: function() {
|
||||
return _this.get(path, null, {
|
||||
virtuals: get(_this, 'schema.options.toJSON.virtuals', null)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(nested, '$__isNested', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: false,
|
||||
value: true
|
||||
});
|
||||
|
||||
const _isEmptyOptions = Object.freeze({
|
||||
minimize: true,
|
||||
virtuals: false,
|
||||
getters: false,
|
||||
transform: false
|
||||
});
|
||||
Object.defineProperty(nested, '$isEmpty', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: false,
|
||||
value: function() {
|
||||
return Object.keys(this.get(path, null, _isEmptyOptions) || {}).length === 0;
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(nested, '$__parent', {
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
writable: false,
|
||||
value: this
|
||||
});
|
||||
|
||||
compile(subprops, nested, path, options);
|
||||
this.$__.getters[path] = nested;
|
||||
}
|
||||
|
||||
return this.$__.getters[path];
|
||||
},
|
||||
set: function(v) {
|
||||
if (v != null && v.$__isNested) {
|
||||
// Convert top-level to POJO, but leave subdocs hydrated so `$set`
|
||||
// can handle them. See gh-9293.
|
||||
v = v.$__get();
|
||||
} else if (v instanceof Document && !v.$__isNested) {
|
||||
v = v.toObject(internalToObjectOptions);
|
||||
}
|
||||
const doc = this.$__[scopeSymbol] || this;
|
||||
doc.$set(path, v);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Object.defineProperty(prototype, prop, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: function() {
|
||||
return this[getSymbol].call(this.$__[scopeSymbol] || this, path);
|
||||
},
|
||||
set: function(v) {
|
||||
this.$set.call(this.$__[scopeSymbol] || this, path, v);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// gets descriptors for all properties of `object`
|
||||
// makes all properties non-enumerable to match previous behavior to #2211
|
||||
function getOwnPropertyDescriptors(object) {
|
||||
const result = {};
|
||||
|
||||
Object.getOwnPropertyNames(object).forEach(function(key) {
|
||||
result[key] = Object.getOwnPropertyDescriptor(object, key);
|
||||
// Assume these are schema paths, ignore them re: #5470
|
||||
if (result[key].get) {
|
||||
delete result[key];
|
||||
return;
|
||||
}
|
||||
result[key].enumerable = [
|
||||
'isNew',
|
||||
'$__',
|
||||
'errors',
|
||||
'_doc',
|
||||
'$locals',
|
||||
'$op',
|
||||
'__parentArray',
|
||||
'__index',
|
||||
'$isDocumentArrayElement'
|
||||
].indexOf(key) === -1;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
43
node_modules/mongoose/lib/helpers/document/getEmbeddedDiscriminatorPath.js
generated
vendored
Normal file
43
node_modules/mongoose/lib/helpers/document/getEmbeddedDiscriminatorPath.js
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
'use strict';
|
||||
|
||||
const get = require('../get');
|
||||
|
||||
/*!
|
||||
* Like `schema.path()`, except with a document, because impossible to
|
||||
* determine path type without knowing the embedded discriminator key.
|
||||
*/
|
||||
|
||||
module.exports = function getEmbeddedDiscriminatorPath(doc, path, options) {
|
||||
options = options || {};
|
||||
const typeOnly = options.typeOnly;
|
||||
const parts = path.split('.');
|
||||
let schema = null;
|
||||
let type = 'adhocOrUndefined';
|
||||
|
||||
for (let i = 0; i < parts.length; ++i) {
|
||||
const subpath = parts.slice(0, i + 1).join('.');
|
||||
schema = doc.schema.path(subpath);
|
||||
if (schema == null) {
|
||||
type = 'adhocOrUndefined';
|
||||
continue;
|
||||
}
|
||||
if (schema.instance === 'Mixed') {
|
||||
return typeOnly ? 'real' : schema;
|
||||
}
|
||||
type = doc.schema.pathType(subpath);
|
||||
if ((schema.$isSingleNested || schema.$isMongooseDocumentArrayElement) &&
|
||||
schema.schema.discriminators != null) {
|
||||
const discriminators = schema.schema.discriminators;
|
||||
const discriminatorKey = doc.get(subpath + '.' +
|
||||
get(schema, 'schema.options.discriminatorKey'));
|
||||
if (discriminatorKey == null || discriminators[discriminatorKey] == null) {
|
||||
continue;
|
||||
}
|
||||
const rest = parts.slice(i + 1).join('.');
|
||||
return getEmbeddedDiscriminatorPath(doc.get(subpath), rest, options);
|
||||
}
|
||||
}
|
||||
|
||||
// Are we getting the whole schema or just the type, 'real', 'nested', etc.
|
||||
return typeOnly ? type : schema;
|
||||
};
|
||||
17
node_modules/mongoose/lib/helpers/document/handleSpreadDoc.js
generated
vendored
Normal file
17
node_modules/mongoose/lib/helpers/document/handleSpreadDoc.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
'use strict';
|
||||
|
||||
const utils = require('../../utils');
|
||||
|
||||
/**
|
||||
* Using spread operator on a Mongoose document gives you a
|
||||
* POJO that has a tendency to cause infinite recursion. So
|
||||
* we use this function on `set()` to prevent that.
|
||||
*/
|
||||
|
||||
module.exports = function handleSpreadDoc(v) {
|
||||
if (utils.isPOJO(v) && v.$__ != null && v._doc != null) {
|
||||
return v._doc;
|
||||
}
|
||||
|
||||
return v;
|
||||
};
|
||||
25
node_modules/mongoose/lib/helpers/each.js
generated
vendored
Normal file
25
node_modules/mongoose/lib/helpers/each.js
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function each(arr, cb, done) {
|
||||
if (arr.length === 0) {
|
||||
return done();
|
||||
}
|
||||
|
||||
let remaining = arr.length;
|
||||
let err = null;
|
||||
for (const v of arr) {
|
||||
cb(v, function(_err) {
|
||||
if (err != null) {
|
||||
return;
|
||||
}
|
||||
if (_err != null) {
|
||||
err = _err;
|
||||
return done(err);
|
||||
}
|
||||
|
||||
if (--remaining <= 0) {
|
||||
return done();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
64
node_modules/mongoose/lib/helpers/get.js
generated
vendored
Normal file
64
node_modules/mongoose/lib/helpers/get.js
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
'use strict';
|
||||
|
||||
/*!
|
||||
* Simplified lodash.get to work around the annoying null quirk. See:
|
||||
* https://github.com/lodash/lodash/issues/3659
|
||||
*/
|
||||
|
||||
module.exports = function get(obj, path, def) {
|
||||
let parts;
|
||||
let isPathArray = false;
|
||||
if (typeof path === 'string') {
|
||||
if (path.indexOf('.') === -1) {
|
||||
const _v = getProperty(obj, path);
|
||||
if (_v == null) {
|
||||
return def;
|
||||
}
|
||||
return _v;
|
||||
}
|
||||
|
||||
parts = path.split('.');
|
||||
} else {
|
||||
isPathArray = true;
|
||||
parts = path;
|
||||
|
||||
if (parts.length === 1) {
|
||||
const _v = getProperty(obj, parts[0]);
|
||||
if (_v == null) {
|
||||
return def;
|
||||
}
|
||||
return _v;
|
||||
}
|
||||
}
|
||||
let rest = path;
|
||||
let cur = obj;
|
||||
for (const part of parts) {
|
||||
if (cur == null) {
|
||||
return def;
|
||||
}
|
||||
|
||||
// `lib/cast.js` depends on being able to get dotted paths in updates,
|
||||
// like `{ $set: { 'a.b': 42 } }`
|
||||
if (!isPathArray && cur[rest] != null) {
|
||||
return cur[rest];
|
||||
}
|
||||
|
||||
cur = getProperty(cur, part);
|
||||
|
||||
if (!isPathArray) {
|
||||
rest = rest.substr(part.length + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return cur == null ? def : cur;
|
||||
};
|
||||
|
||||
function getProperty(obj, prop) {
|
||||
if (obj == null) {
|
||||
return obj;
|
||||
}
|
||||
if (obj instanceof Map) {
|
||||
return obj.get(prop);
|
||||
}
|
||||
return obj[prop];
|
||||
}
|
||||
27
node_modules/mongoose/lib/helpers/getDefaultBulkwriteResult.js
generated
vendored
Normal file
27
node_modules/mongoose/lib/helpers/getDefaultBulkwriteResult.js
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
'use strict';
|
||||
function getDefaultBulkwriteResult() {
|
||||
return {
|
||||
result: {
|
||||
ok: 1,
|
||||
writeErrors: [],
|
||||
writeConcernErrors: [],
|
||||
insertedIds: [],
|
||||
nInserted: 0,
|
||||
nUpserted: 0,
|
||||
nMatched: 0,
|
||||
nModified: 0,
|
||||
nRemoved: 0,
|
||||
upserted: []
|
||||
},
|
||||
insertedCount: 0,
|
||||
matchedCount: 0,
|
||||
modifiedCount: 0,
|
||||
deletedCount: 0,
|
||||
upsertedCount: 0,
|
||||
upsertedIds: {},
|
||||
insertedIds: {},
|
||||
n: 0
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = getDefaultBulkwriteResult;
|
||||
8
node_modules/mongoose/lib/helpers/getFunctionName.js
generated
vendored
Normal file
8
node_modules/mongoose/lib/helpers/getFunctionName.js
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function(fn) {
|
||||
if (fn.name) {
|
||||
return fn.name;
|
||||
}
|
||||
return (fn.toString().trim().match(/^function\s*([^\s(]+)/) || [])[1];
|
||||
};
|
||||
14
node_modules/mongoose/lib/helpers/immediate.js
generated
vendored
Normal file
14
node_modules/mongoose/lib/helpers/immediate.js
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/*!
|
||||
* Centralize this so we can more easily work around issues with people
|
||||
* stubbing out `process.nextTick()` in tests using sinon:
|
||||
* https://github.com/sinonjs/lolex#automatically-incrementing-mocked-time
|
||||
* See gh-6074
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const nextTick = process.nextTick.bind(process);
|
||||
|
||||
module.exports = function immediate(cb) {
|
||||
return nextTick(cb);
|
||||
};
|
||||
18
node_modules/mongoose/lib/helpers/indexes/isDefaultIdIndex.js
generated
vendored
Normal file
18
node_modules/mongoose/lib/helpers/indexes/isDefaultIdIndex.js
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
'use strict';
|
||||
|
||||
const get = require('../get');
|
||||
|
||||
module.exports = function isDefaultIdIndex(index) {
|
||||
if (Array.isArray(index)) {
|
||||
// Mongoose syntax
|
||||
const keys = Object.keys(index[0]);
|
||||
return keys.length === 1 && keys[0] === '_id' && index[0]._id !== 'hashed';
|
||||
}
|
||||
|
||||
if (typeof index !== 'object') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const key = get(index, 'key', {});
|
||||
return Object.keys(key).length === 1 && key.hasOwnProperty('_id');
|
||||
};
|
||||
95
node_modules/mongoose/lib/helpers/indexes/isIndexEqual.js
generated
vendored
Normal file
95
node_modules/mongoose/lib/helpers/indexes/isIndexEqual.js
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
'use strict';
|
||||
|
||||
const get = require('../get');
|
||||
const utils = require('../../utils');
|
||||
|
||||
/**
|
||||
* Given a Mongoose index definition (key + options objects) and a MongoDB server
|
||||
* index definition, determine if the two indexes are equal.
|
||||
*
|
||||
* @param {Object} key the Mongoose index spec
|
||||
* @param {Object} options the Mongoose index definition's options
|
||||
* @param {Object} dbIndex the index in MongoDB as returned by `listIndexes()`
|
||||
* @api private
|
||||
*/
|
||||
|
||||
module.exports = function isIndexEqual(key, options, dbIndex) {
|
||||
// Special case: text indexes have a special format in the db. For example,
|
||||
// `{ name: 'text' }` becomes:
|
||||
// {
|
||||
// v: 2,
|
||||
// key: { _fts: 'text', _ftsx: 1 },
|
||||
// name: 'name_text',
|
||||
// ns: 'test.tests',
|
||||
// background: true,
|
||||
// weights: { name: 1 },
|
||||
// default_language: 'english',
|
||||
// language_override: 'language',
|
||||
// textIndexVersion: 3
|
||||
// }
|
||||
if (dbIndex.textIndexVersion != null) {
|
||||
const weights = dbIndex.weights;
|
||||
if (Object.keys(weights).length !== Object.keys(key).length) {
|
||||
return false;
|
||||
}
|
||||
for (const prop of Object.keys(weights)) {
|
||||
if (!(prop in key)) {
|
||||
return false;
|
||||
}
|
||||
const weight = weights[prop];
|
||||
if (weight !== get(options, 'weights.' + prop) && !(weight === 1 && get(options, 'weights.' + prop) == null)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (options['default_language'] !== dbIndex['default_language']) {
|
||||
return dbIndex['default_language'] === 'english' && options['default_language'] == null;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const optionKeys = [
|
||||
'unique',
|
||||
'partialFilterExpression',
|
||||
'sparse',
|
||||
'expireAfterSeconds',
|
||||
'collation'
|
||||
];
|
||||
for (const key of optionKeys) {
|
||||
if (!(key in options) && !(key in dbIndex)) {
|
||||
continue;
|
||||
}
|
||||
if (key === 'collation') {
|
||||
if (options[key] == null || dbIndex[key] == null) {
|
||||
return options[key] == null && dbIndex[key] == null;
|
||||
}
|
||||
const definedKeys = Object.keys(options.collation);
|
||||
const schemaCollation = options.collation;
|
||||
const dbCollation = dbIndex.collation;
|
||||
for (const opt of definedKeys) {
|
||||
if (get(schemaCollation, opt) !== get(dbCollation, opt)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (!utils.deepEqual(options[key], dbIndex[key])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const schemaIndexKeys = Object.keys(key);
|
||||
const dbIndexKeys = Object.keys(dbIndex.key);
|
||||
if (schemaIndexKeys.length !== dbIndexKeys.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < schemaIndexKeys.length; ++i) {
|
||||
if (schemaIndexKeys[i] !== dbIndexKeys[i]) {
|
||||
return false;
|
||||
}
|
||||
if (!utils.deepEqual(key[schemaIndexKeys[i]], dbIndex.key[dbIndexKeys[i]])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
13
node_modules/mongoose/lib/helpers/isBsonType.js
generated
vendored
Normal file
13
node_modules/mongoose/lib/helpers/isBsonType.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
const get = require('./get');
|
||||
|
||||
/*!
|
||||
* Get the bson type, if it exists
|
||||
*/
|
||||
|
||||
function isBsonType(obj, typename) {
|
||||
return get(obj, '_bsontype', void 0) === typename;
|
||||
}
|
||||
|
||||
module.exports = isBsonType;
|
||||
21
node_modules/mongoose/lib/helpers/isMongooseObject.js
generated
vendored
Normal file
21
node_modules/mongoose/lib/helpers/isMongooseObject.js
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
'use strict';
|
||||
|
||||
/*!
|
||||
* Returns if `v` is a mongoose object that has a `toObject()` method we can use.
|
||||
*
|
||||
* This is for compatibility with libs like Date.js which do foolish things to Natives.
|
||||
*
|
||||
* @param {any} v
|
||||
* @api private
|
||||
*/
|
||||
|
||||
module.exports = function(v) {
|
||||
if (v == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return v.$__ != null || // Document
|
||||
v.isMongooseArray || // Array or Document Array
|
||||
v.isMongooseBuffer || // Buffer
|
||||
v.$isMongooseMap; // Map
|
||||
};
|
||||
16
node_modules/mongoose/lib/helpers/isObject.js
generated
vendored
Normal file
16
node_modules/mongoose/lib/helpers/isObject.js
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
/*!
|
||||
* Determines if `arg` is an object.
|
||||
*
|
||||
* @param {Object|Array|String|Function|RegExp|any} arg
|
||||
* @api private
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
module.exports = function(arg) {
|
||||
if (Buffer.isBuffer(arg)) {
|
||||
return true;
|
||||
}
|
||||
return Object.prototype.toString.call(arg) === '[object Object]';
|
||||
};
|
||||
6
node_modules/mongoose/lib/helpers/isPromise.js
generated
vendored
Normal file
6
node_modules/mongoose/lib/helpers/isPromise.js
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
'use strict';
|
||||
function isPromise(val) {
|
||||
return !!val && (typeof val === 'object' || typeof val === 'function') && typeof val.then === 'function';
|
||||
}
|
||||
|
||||
module.exports = isPromise;
|
||||
138
node_modules/mongoose/lib/helpers/model/applyHooks.js
generated
vendored
Normal file
138
node_modules/mongoose/lib/helpers/model/applyHooks.js
generated
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
'use strict';
|
||||
|
||||
const symbols = require('../../schema/symbols');
|
||||
const promiseOrCallback = require('../promiseOrCallback');
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
module.exports = applyHooks;
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
applyHooks.middlewareFunctions = [
|
||||
'deleteOne',
|
||||
'save',
|
||||
'validate',
|
||||
'remove',
|
||||
'updateOne',
|
||||
'init'
|
||||
];
|
||||
|
||||
/*!
|
||||
* Register hooks for this model
|
||||
*
|
||||
* @param {Model} model
|
||||
* @param {Schema} schema
|
||||
*/
|
||||
|
||||
function applyHooks(model, schema, options) {
|
||||
options = options || {};
|
||||
|
||||
const kareemOptions = {
|
||||
useErrorHandlers: true,
|
||||
numCallbackParams: 1,
|
||||
nullResultByDefault: true,
|
||||
contextParameter: true
|
||||
};
|
||||
const objToDecorate = options.decorateDoc ? model : model.prototype;
|
||||
|
||||
model.$appliedHooks = true;
|
||||
for (const key of Object.keys(schema.paths)) {
|
||||
const type = schema.paths[key];
|
||||
let childModel = null;
|
||||
if (type.$isSingleNested) {
|
||||
childModel = type.caster;
|
||||
} else if (type.$isMongooseDocumentArray) {
|
||||
childModel = type.Constructor;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (childModel.$appliedHooks) {
|
||||
continue;
|
||||
}
|
||||
|
||||
applyHooks(childModel, type.schema, options);
|
||||
if (childModel.discriminators != null) {
|
||||
const keys = Object.keys(childModel.discriminators);
|
||||
for (const key of keys) {
|
||||
applyHooks(childModel.discriminators[key],
|
||||
childModel.discriminators[key].schema, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Built-in hooks rely on hooking internal functions in order to support
|
||||
// promises and make it so that `doc.save.toString()` provides meaningful
|
||||
// information.
|
||||
|
||||
const middleware = schema.s.hooks.
|
||||
filter(hook => {
|
||||
if (hook.name === 'updateOne' || hook.name === 'deleteOne') {
|
||||
return !!hook['document'];
|
||||
}
|
||||
if (hook.name === 'remove' || hook.name === 'init') {
|
||||
return hook['document'] == null || !!hook['document'];
|
||||
}
|
||||
if (hook.query != null || hook.document != null) {
|
||||
return hook.document !== false;
|
||||
}
|
||||
return true;
|
||||
}).
|
||||
filter(hook => {
|
||||
// If user has overwritten the method, don't apply built-in middleware
|
||||
if (schema.methods[hook.name]) {
|
||||
return !hook.fn[symbols.builtInMiddleware];
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
model._middleware = middleware;
|
||||
|
||||
objToDecorate.$__originalValidate = objToDecorate.$__originalValidate || objToDecorate.$__validate;
|
||||
|
||||
for (const method of ['save', 'validate', 'remove', 'deleteOne']) {
|
||||
const toWrap = method === 'validate' ? '$__originalValidate' : `$__${method}`;
|
||||
const wrapped = middleware.
|
||||
createWrapper(method, objToDecorate[toWrap], null, kareemOptions);
|
||||
objToDecorate[`$__${method}`] = wrapped;
|
||||
}
|
||||
objToDecorate.$__init = middleware.
|
||||
createWrapperSync('init', objToDecorate.$__init, null, kareemOptions);
|
||||
|
||||
// Support hooks for custom methods
|
||||
const customMethods = Object.keys(schema.methods);
|
||||
const customMethodOptions = Object.assign({}, kareemOptions, {
|
||||
// Only use `checkForPromise` for custom methods, because mongoose
|
||||
// query thunks are not as consistent as I would like about returning
|
||||
// a nullish value rather than the query. If a query thunk returns
|
||||
// a query, `checkForPromise` causes infinite recursion
|
||||
checkForPromise: true
|
||||
});
|
||||
for (const method of customMethods) {
|
||||
if (!middleware.hasHooks(method)) {
|
||||
// Don't wrap if there are no hooks for the custom method to avoid
|
||||
// surprises. Also, `createWrapper()` enforces consistent async,
|
||||
// so wrapping a sync method would break it.
|
||||
continue;
|
||||
}
|
||||
const originalMethod = objToDecorate[method];
|
||||
objToDecorate[method] = function() {
|
||||
const args = Array.prototype.slice.call(arguments);
|
||||
const cb = args.slice(-1).pop();
|
||||
const argsWithoutCallback = typeof cb === 'function' ?
|
||||
args.slice(0, args.length - 1) : args;
|
||||
return promiseOrCallback(cb, callback => {
|
||||
return this[`$__${method}`].apply(this,
|
||||
argsWithoutCallback.concat([callback]));
|
||||
}, model.events);
|
||||
};
|
||||
objToDecorate[`$__${method}`] = middleware.
|
||||
createWrapper(method, originalMethod, null, customMethodOptions);
|
||||
}
|
||||
}
|
||||
56
node_modules/mongoose/lib/helpers/model/applyMethods.js
generated
vendored
Normal file
56
node_modules/mongoose/lib/helpers/model/applyMethods.js
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
'use strict';
|
||||
|
||||
const get = require('../get');
|
||||
|
||||
/*!
|
||||
* Register methods for this model
|
||||
*
|
||||
* @param {Model} model
|
||||
* @param {Schema} schema
|
||||
*/
|
||||
|
||||
module.exports = function applyMethods(model, schema) {
|
||||
function apply(method, schema) {
|
||||
Object.defineProperty(model.prototype, method, {
|
||||
get: function() {
|
||||
const h = {};
|
||||
for (const k in schema.methods[method]) {
|
||||
h[k] = schema.methods[method][k].bind(this);
|
||||
}
|
||||
return h;
|
||||
},
|
||||
configurable: true
|
||||
});
|
||||
}
|
||||
for (const method of Object.keys(schema.methods)) {
|
||||
const fn = schema.methods[method];
|
||||
if (schema.tree.hasOwnProperty(method)) {
|
||||
throw new Error('You have a method and a property in your schema both ' +
|
||||
'named "' + method + '"');
|
||||
}
|
||||
if (schema.reserved[method] &&
|
||||
!get(schema, `methodOptions.${method}.suppressWarning`, false)) {
|
||||
console.warn(`mongoose: the method name "${method}" is used by mongoose ` +
|
||||
'internally, overwriting it may cause bugs. If you\'re sure you know ' +
|
||||
'what you\'re doing, you can suppress this error by using ' +
|
||||
`\`schema.method('${method}', fn, { suppressWarning: true })\`.`);
|
||||
}
|
||||
if (typeof fn === 'function') {
|
||||
model.prototype[method] = fn;
|
||||
} else {
|
||||
apply(method, schema);
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively call `applyMethods()` on child schemas
|
||||
model.$appliedMethods = true;
|
||||
for (const key of Object.keys(schema.paths)) {
|
||||
const type = schema.paths[key];
|
||||
if (type.$isSingleNested && !type.caster.$appliedMethods) {
|
||||
applyMethods(type.caster, type.schema);
|
||||
}
|
||||
if (type.$isMongooseDocumentArray && !type.Constructor.$appliedMethods) {
|
||||
applyMethods(type.Constructor, type.schema);
|
||||
}
|
||||
}
|
||||
};
|
||||
71
node_modules/mongoose/lib/helpers/model/applyStaticHooks.js
generated
vendored
Normal file
71
node_modules/mongoose/lib/helpers/model/applyStaticHooks.js
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
'use strict';
|
||||
|
||||
const middlewareFunctions = require('../query/applyQueryMiddleware').middlewareFunctions;
|
||||
const promiseOrCallback = require('../promiseOrCallback');
|
||||
|
||||
module.exports = function applyStaticHooks(model, hooks, statics) {
|
||||
const kareemOptions = {
|
||||
useErrorHandlers: true,
|
||||
numCallbackParams: 1
|
||||
};
|
||||
|
||||
hooks = hooks.filter(hook => {
|
||||
// If the custom static overwrites an existing query middleware, don't apply
|
||||
// middleware to it by default. This avoids a potential backwards breaking
|
||||
// change with plugins like `mongoose-delete` that use statics to overwrite
|
||||
// built-in Mongoose functions.
|
||||
if (middlewareFunctions.indexOf(hook.name) !== -1) {
|
||||
return !!hook.model;
|
||||
}
|
||||
return hook.model !== false;
|
||||
});
|
||||
|
||||
model.$__insertMany = hooks.createWrapper('insertMany',
|
||||
model.$__insertMany, model, kareemOptions);
|
||||
|
||||
for (const key of Object.keys(statics)) {
|
||||
if (hooks.hasHooks(key)) {
|
||||
const original = model[key];
|
||||
|
||||
model[key] = function() {
|
||||
const numArgs = arguments.length;
|
||||
const lastArg = numArgs > 0 ? arguments[numArgs - 1] : null;
|
||||
const cb = typeof lastArg === 'function' ? lastArg : null;
|
||||
const args = Array.prototype.slice.
|
||||
call(arguments, 0, cb == null ? numArgs : numArgs - 1);
|
||||
// Special case: can't use `Kareem#wrap()` because it doesn't currently
|
||||
// support wrapped functions that return a promise.
|
||||
return promiseOrCallback(cb, callback => {
|
||||
hooks.execPre(key, model, args, function(err) {
|
||||
if (err != null) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
let postCalled = 0;
|
||||
const ret = original.apply(model, args.concat(post));
|
||||
if (ret != null && typeof ret.then === 'function') {
|
||||
ret.then(res => post(null, res), err => post(err));
|
||||
}
|
||||
|
||||
function post(error, res) {
|
||||
if (postCalled++ > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (error != null) {
|
||||
return callback(error);
|
||||
}
|
||||
|
||||
hooks.execPost(key, model, [res], function(error) {
|
||||
if (error != null) {
|
||||
return callback(error);
|
||||
}
|
||||
callback(null, res);
|
||||
});
|
||||
}
|
||||
});
|
||||
}, model.events);
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
12
node_modules/mongoose/lib/helpers/model/applyStatics.js
generated
vendored
Normal file
12
node_modules/mongoose/lib/helpers/model/applyStatics.js
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
/*!
|
||||
* Register statics for this model
|
||||
* @param {Model} model
|
||||
* @param {Schema} schema
|
||||
*/
|
||||
module.exports = function applyStatics(model, schema) {
|
||||
for (const i in schema.statics) {
|
||||
model[i] = schema.statics[i];
|
||||
}
|
||||
};
|
||||
224
node_modules/mongoose/lib/helpers/model/castBulkWrite.js
generated
vendored
Normal file
224
node_modules/mongoose/lib/helpers/model/castBulkWrite.js
generated
vendored
Normal file
@@ -0,0 +1,224 @@
|
||||
'use strict';
|
||||
|
||||
const getDiscriminatorByValue = require('../../helpers/discriminator/getDiscriminatorByValue');
|
||||
const applyTimestampsToChildren = require('../update/applyTimestampsToChildren');
|
||||
const applyTimestampsToUpdate = require('../update/applyTimestampsToUpdate');
|
||||
const cast = require('../../cast');
|
||||
const castUpdate = require('../query/castUpdate');
|
||||
const setDefaultsOnInsert = require('../setDefaultsOnInsert');
|
||||
|
||||
/*!
|
||||
* Given a model and a bulkWrite op, return a thunk that handles casting and
|
||||
* validating the individual op.
|
||||
*/
|
||||
|
||||
module.exports = function castBulkWrite(originalModel, op, options) {
|
||||
const now = originalModel.base.now();
|
||||
|
||||
if (op['insertOne']) {
|
||||
return (callback) => {
|
||||
const model = decideModelByObject(originalModel, op['insertOne']['document']);
|
||||
|
||||
const doc = new model(op['insertOne']['document']);
|
||||
if (model.schema.options.timestamps) {
|
||||
doc.initializeTimestamps();
|
||||
}
|
||||
if (options.session != null) {
|
||||
doc.$session(options.session);
|
||||
}
|
||||
op['insertOne']['document'] = doc;
|
||||
op['insertOne']['document'].validate({ __noPromise: true }, function(error) {
|
||||
if (error) {
|
||||
return callback(error, null);
|
||||
}
|
||||
callback(null);
|
||||
});
|
||||
};
|
||||
} else if (op['updateOne']) {
|
||||
return (callback) => {
|
||||
try {
|
||||
if (!op['updateOne']['filter']) {
|
||||
throw new Error('Must provide a filter object.');
|
||||
}
|
||||
if (!op['updateOne']['update']) {
|
||||
throw new Error('Must provide an update object.');
|
||||
}
|
||||
|
||||
const model = decideModelByObject(originalModel, op['updateOne']['filter']);
|
||||
const schema = model.schema;
|
||||
const strict = options.strict != null ? options.strict : model.schema.options.strict;
|
||||
|
||||
_addDiscriminatorToObject(schema, op['updateOne']['filter']);
|
||||
|
||||
if (model.schema.$timestamps != null && op['updateOne'].timestamps !== false) {
|
||||
const createdAt = model.schema.$timestamps.createdAt;
|
||||
const updatedAt = model.schema.$timestamps.updatedAt;
|
||||
applyTimestampsToUpdate(now, createdAt, updatedAt, op['updateOne']['update'], {});
|
||||
}
|
||||
|
||||
applyTimestampsToChildren(now, op['updateOne']['update'], model.schema);
|
||||
|
||||
if (op['updateOne'].setDefaultsOnInsert) {
|
||||
setDefaultsOnInsert(op['updateOne']['filter'], model.schema, op['updateOne']['update'], {
|
||||
setDefaultsOnInsert: true,
|
||||
upsert: op['updateOne'].upsert
|
||||
});
|
||||
}
|
||||
|
||||
op['updateOne']['filter'] = cast(model.schema, op['updateOne']['filter'], {
|
||||
strict: strict,
|
||||
upsert: op['updateOne'].upsert
|
||||
});
|
||||
|
||||
op['updateOne']['update'] = castUpdate(model.schema, op['updateOne']['update'], {
|
||||
strict: strict,
|
||||
overwrite: false,
|
||||
upsert: op['updateOne'].upsert
|
||||
}, model, op['updateOne']['filter']);
|
||||
} catch (error) {
|
||||
return callback(error, null);
|
||||
}
|
||||
|
||||
callback(null);
|
||||
};
|
||||
} else if (op['updateMany']) {
|
||||
return (callback) => {
|
||||
try {
|
||||
if (!op['updateMany']['filter']) {
|
||||
throw new Error('Must provide a filter object.');
|
||||
}
|
||||
if (!op['updateMany']['update']) {
|
||||
throw new Error('Must provide an update object.');
|
||||
}
|
||||
|
||||
const model = decideModelByObject(originalModel, op['updateMany']['filter']);
|
||||
const schema = model.schema;
|
||||
const strict = options.strict != null ? options.strict : model.schema.options.strict;
|
||||
|
||||
if (op['updateMany'].setDefaultsOnInsert) {
|
||||
setDefaultsOnInsert(op['updateMany']['filter'], model.schema, op['updateMany']['update'], {
|
||||
setDefaultsOnInsert: true,
|
||||
upsert: op['updateMany'].upsert
|
||||
});
|
||||
}
|
||||
|
||||
if (model.schema.$timestamps != null && op['updateMany'].timestamps !== false) {
|
||||
const createdAt = model.schema.$timestamps.createdAt;
|
||||
const updatedAt = model.schema.$timestamps.updatedAt;
|
||||
applyTimestampsToUpdate(now, createdAt, updatedAt, op['updateMany']['update'], {});
|
||||
}
|
||||
|
||||
applyTimestampsToChildren(now, op['updateMany']['update'], model.schema);
|
||||
|
||||
_addDiscriminatorToObject(schema, op['updateMany']['filter']);
|
||||
|
||||
op['updateMany']['filter'] = cast(model.schema, op['updateMany']['filter'], {
|
||||
strict: strict,
|
||||
upsert: op['updateMany'].upsert
|
||||
});
|
||||
|
||||
op['updateMany']['update'] = castUpdate(model.schema, op['updateMany']['update'], {
|
||||
strict: strict,
|
||||
overwrite: false,
|
||||
upsert: op['updateMany'].upsert
|
||||
}, model, op['updateMany']['filter']);
|
||||
|
||||
} catch (error) {
|
||||
return callback(error, null);
|
||||
}
|
||||
|
||||
callback(null);
|
||||
};
|
||||
} else if (op['replaceOne']) {
|
||||
return (callback) => {
|
||||
const model = decideModelByObject(originalModel, op['replaceOne']['filter']);
|
||||
const schema = model.schema;
|
||||
const strict = options.strict != null ? options.strict : model.schema.options.strict;
|
||||
|
||||
_addDiscriminatorToObject(schema, op['replaceOne']['filter']);
|
||||
try {
|
||||
op['replaceOne']['filter'] = cast(model.schema, op['replaceOne']['filter'], {
|
||||
strict: strict,
|
||||
upsert: op['replaceOne'].upsert
|
||||
});
|
||||
} catch (error) {
|
||||
return callback(error, null);
|
||||
}
|
||||
|
||||
// set `skipId`, otherwise we get "_id field cannot be changed"
|
||||
const doc = new model(op['replaceOne']['replacement'], strict, true);
|
||||
if (model.schema.options.timestamps) {
|
||||
doc.initializeTimestamps();
|
||||
}
|
||||
if (options.session != null) {
|
||||
doc.$session(options.session);
|
||||
}
|
||||
op['replaceOne']['replacement'] = doc;
|
||||
|
||||
op['replaceOne']['replacement'].validate({ __noPromise: true }, function(error) {
|
||||
if (error) {
|
||||
return callback(error, null);
|
||||
}
|
||||
op['replaceOne']['replacement'] = op['replaceOne']['replacement'].toBSON();
|
||||
callback(null);
|
||||
});
|
||||
};
|
||||
} else if (op['deleteOne']) {
|
||||
return (callback) => {
|
||||
const model = decideModelByObject(originalModel, op['deleteOne']['filter']);
|
||||
const schema = model.schema;
|
||||
|
||||
_addDiscriminatorToObject(schema, op['deleteOne']['filter']);
|
||||
|
||||
try {
|
||||
op['deleteOne']['filter'] = cast(model.schema,
|
||||
op['deleteOne']['filter']);
|
||||
} catch (error) {
|
||||
return callback(error, null);
|
||||
}
|
||||
|
||||
callback(null);
|
||||
};
|
||||
} else if (op['deleteMany']) {
|
||||
return (callback) => {
|
||||
const model = decideModelByObject(originalModel, op['deleteMany']['filter']);
|
||||
const schema = model.schema;
|
||||
|
||||
_addDiscriminatorToObject(schema, op['deleteMany']['filter']);
|
||||
|
||||
try {
|
||||
op['deleteMany']['filter'] = cast(model.schema,
|
||||
op['deleteMany']['filter']);
|
||||
} catch (error) {
|
||||
return callback(error, null);
|
||||
}
|
||||
|
||||
callback(null);
|
||||
};
|
||||
} else {
|
||||
return (callback) => {
|
||||
callback(new Error('Invalid op passed to `bulkWrite()`'), null);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
function _addDiscriminatorToObject(schema, obj) {
|
||||
if (schema == null) {
|
||||
return;
|
||||
}
|
||||
if (schema.discriminatorMapping && !schema.discriminatorMapping.isRoot) {
|
||||
obj[schema.discriminatorMapping.key] = schema.discriminatorMapping.value;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* gets discriminator model if discriminator key is present in object
|
||||
*/
|
||||
|
||||
function decideModelByObject(model, object) {
|
||||
const discriminatorKey = model.schema.options.discriminatorKey;
|
||||
if (object != null && object.hasOwnProperty(discriminatorKey)) {
|
||||
model = getDiscriminatorByValue(model.discriminators, object[discriminatorKey]) || model;
|
||||
}
|
||||
return model;
|
||||
}
|
||||
205
node_modules/mongoose/lib/helpers/model/discriminator.js
generated
vendored
Normal file
205
node_modules/mongoose/lib/helpers/model/discriminator.js
generated
vendored
Normal file
@@ -0,0 +1,205 @@
|
||||
'use strict';
|
||||
|
||||
const Mixed = require('../../schema/mixed');
|
||||
const defineKey = require('../document/compile').defineKey;
|
||||
const get = require('../get');
|
||||
const utils = require('../../utils');
|
||||
|
||||
const CUSTOMIZABLE_DISCRIMINATOR_OPTIONS = {
|
||||
toJSON: true,
|
||||
toObject: true,
|
||||
_id: true,
|
||||
id: true
|
||||
};
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
module.exports = function discriminator(model, name, schema, tiedValue, applyPlugins) {
|
||||
if (!(schema && schema.instanceOfSchema)) {
|
||||
throw new Error('You must pass a valid discriminator Schema');
|
||||
}
|
||||
|
||||
if (model.schema.discriminatorMapping &&
|
||||
!model.schema.discriminatorMapping.isRoot) {
|
||||
throw new Error('Discriminator "' + name +
|
||||
'" can only be a discriminator of the root model');
|
||||
}
|
||||
|
||||
if (applyPlugins) {
|
||||
const applyPluginsToDiscriminators = get(model.base,
|
||||
'options.applyPluginsToDiscriminators', false);
|
||||
// Even if `applyPluginsToDiscriminators` isn't set, we should still apply
|
||||
// global plugins to schemas embedded in the discriminator schema (gh-7370)
|
||||
model.base._applyPlugins(schema, {
|
||||
skipTopLevel: !applyPluginsToDiscriminators
|
||||
});
|
||||
}
|
||||
|
||||
const key = model.schema.options.discriminatorKey;
|
||||
|
||||
const existingPath = model.schema.path(key);
|
||||
if (existingPath != null) {
|
||||
if (!utils.hasUserDefinedProperty(existingPath.options, 'select')) {
|
||||
existingPath.options.select = true;
|
||||
}
|
||||
existingPath.options.$skipDiscriminatorCheck = true;
|
||||
} else {
|
||||
const baseSchemaAddition = {};
|
||||
baseSchemaAddition[key] = {
|
||||
default: void 0,
|
||||
select: true,
|
||||
$skipDiscriminatorCheck: true
|
||||
};
|
||||
baseSchemaAddition[key][model.schema.options.typeKey] = String;
|
||||
model.schema.add(baseSchemaAddition);
|
||||
defineKey(key, null, model.prototype, null, [key], model.schema.options);
|
||||
}
|
||||
|
||||
if (schema.path(key) && schema.path(key).options.$skipDiscriminatorCheck !== true) {
|
||||
throw new Error('Discriminator "' + name +
|
||||
'" cannot have field with name "' + key + '"');
|
||||
}
|
||||
|
||||
let value = name;
|
||||
if ((typeof tiedValue === 'string' && tiedValue.length) || tiedValue != null) {
|
||||
value = tiedValue;
|
||||
}
|
||||
|
||||
function merge(schema, baseSchema) {
|
||||
// Retain original schema before merging base schema
|
||||
schema._baseSchema = baseSchema;
|
||||
if (baseSchema.paths._id &&
|
||||
baseSchema.paths._id.options &&
|
||||
!baseSchema.paths._id.options.auto) {
|
||||
schema.remove('_id');
|
||||
}
|
||||
|
||||
// Find conflicting paths: if something is a path in the base schema
|
||||
// and a nested path in the child schema, overwrite the base schema path.
|
||||
// See gh-6076
|
||||
const baseSchemaPaths = Object.keys(baseSchema.paths);
|
||||
const conflictingPaths = [];
|
||||
|
||||
for (const path of baseSchemaPaths) {
|
||||
if (schema.nested[path]) {
|
||||
conflictingPaths.push(path);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (path.indexOf('.') === -1) {
|
||||
continue;
|
||||
}
|
||||
const sp = path.split('.').slice(0, -1);
|
||||
let cur = '';
|
||||
for (const piece of sp) {
|
||||
cur += (cur.length ? '.' : '') + piece;
|
||||
if (schema.paths[cur] instanceof Mixed ||
|
||||
schema.singleNestedPaths[cur] instanceof Mixed) {
|
||||
conflictingPaths.push(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
utils.merge(schema, baseSchema, {
|
||||
isDiscriminatorSchemaMerge: true,
|
||||
omit: { discriminators: true, base: true },
|
||||
omitNested: conflictingPaths.reduce((cur, path) => {
|
||||
cur['tree.' + path] = true;
|
||||
return cur;
|
||||
}, {})
|
||||
});
|
||||
|
||||
// Clean up conflicting paths _after_ merging re: gh-6076
|
||||
for (const conflictingPath of conflictingPaths) {
|
||||
delete schema.paths[conflictingPath];
|
||||
}
|
||||
|
||||
// Rebuild schema models because schemas may have been merged re: #7884
|
||||
schema.childSchemas.forEach(obj => {
|
||||
obj.model.prototype.$__setSchema(obj.schema);
|
||||
});
|
||||
|
||||
const obj = {};
|
||||
obj[key] = {
|
||||
default: value,
|
||||
select: true,
|
||||
set: function(newName) {
|
||||
if (newName === value || (Array.isArray(value) && utils.deepEqual(newName, value))) {
|
||||
return value;
|
||||
}
|
||||
throw new Error('Can\'t set discriminator key "' + key + '"');
|
||||
},
|
||||
$skipDiscriminatorCheck: true
|
||||
};
|
||||
obj[key][schema.options.typeKey] = existingPath ? existingPath.options[schema.options.typeKey] : String;
|
||||
schema.add(obj);
|
||||
|
||||
|
||||
schema.discriminatorMapping = { key: key, value: value, isRoot: false };
|
||||
|
||||
if (baseSchema.options.collection) {
|
||||
schema.options.collection = baseSchema.options.collection;
|
||||
}
|
||||
|
||||
const toJSON = schema.options.toJSON;
|
||||
const toObject = schema.options.toObject;
|
||||
const _id = schema.options._id;
|
||||
const id = schema.options.id;
|
||||
|
||||
const keys = Object.keys(schema.options);
|
||||
schema.options.discriminatorKey = baseSchema.options.discriminatorKey;
|
||||
|
||||
for (const _key of keys) {
|
||||
if (!CUSTOMIZABLE_DISCRIMINATOR_OPTIONS[_key]) {
|
||||
// Special case: compiling a model sets `pluralization = true` by default. Avoid throwing an error
|
||||
// for that case. See gh-9238
|
||||
if (_key === 'pluralization' && schema.options[_key] == true && baseSchema.options[_key] == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!utils.deepEqual(schema.options[_key], baseSchema.options[_key])) {
|
||||
throw new Error('Can\'t customize discriminator option ' + _key +
|
||||
' (can only modify ' +
|
||||
Object.keys(CUSTOMIZABLE_DISCRIMINATOR_OPTIONS).join(', ') +
|
||||
')');
|
||||
}
|
||||
}
|
||||
}
|
||||
schema.options = utils.clone(baseSchema.options);
|
||||
if (toJSON) schema.options.toJSON = toJSON;
|
||||
if (toObject) schema.options.toObject = toObject;
|
||||
if (typeof _id !== 'undefined') {
|
||||
schema.options._id = _id;
|
||||
}
|
||||
schema.options.id = id;
|
||||
schema.s.hooks = model.schema.s.hooks.merge(schema.s.hooks);
|
||||
|
||||
schema.plugins = Array.prototype.slice.call(baseSchema.plugins);
|
||||
schema.callQueue = baseSchema.callQueue.concat(schema.callQueue);
|
||||
delete schema._requiredpaths; // reset just in case Schema#requiredPaths() was called on either schema
|
||||
}
|
||||
|
||||
// merges base schema into new discriminator schema and sets new type field.
|
||||
merge(schema, model.schema);
|
||||
|
||||
if (!model.discriminators) {
|
||||
model.discriminators = {};
|
||||
}
|
||||
|
||||
if (!model.schema.discriminatorMapping) {
|
||||
model.schema.discriminatorMapping = { key: key, value: null, isRoot: true };
|
||||
}
|
||||
if (!model.schema.discriminators) {
|
||||
model.schema.discriminators = {};
|
||||
}
|
||||
|
||||
model.schema.discriminators[name] = schema;
|
||||
|
||||
if (model.discriminators[name]) {
|
||||
throw new Error('Discriminator with name "' + name + '" already exists');
|
||||
}
|
||||
|
||||
return schema;
|
||||
};
|
||||
12
node_modules/mongoose/lib/helpers/once.js
generated
vendored
Normal file
12
node_modules/mongoose/lib/helpers/once.js
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function once(fn) {
|
||||
let called = false;
|
||||
return function() {
|
||||
if (called) {
|
||||
return;
|
||||
}
|
||||
called = true;
|
||||
return fn.apply(null, arguments);
|
||||
};
|
||||
};
|
||||
55
node_modules/mongoose/lib/helpers/parallelLimit.js
generated
vendored
Normal file
55
node_modules/mongoose/lib/helpers/parallelLimit.js
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = parallelLimit;
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
function parallelLimit(fns, limit, callback) {
|
||||
let numInProgress = 0;
|
||||
let numFinished = 0;
|
||||
let error = null;
|
||||
|
||||
if (limit <= 0) {
|
||||
throw new Error('Limit must be positive');
|
||||
}
|
||||
|
||||
if (fns.length === 0) {
|
||||
return callback(null, []);
|
||||
}
|
||||
|
||||
for (let i = 0; i < fns.length && i < limit; ++i) {
|
||||
_start();
|
||||
}
|
||||
|
||||
function _start() {
|
||||
fns[numFinished + numInProgress](_done(numFinished + numInProgress));
|
||||
++numInProgress;
|
||||
}
|
||||
|
||||
const results = [];
|
||||
|
||||
function _done(index) {
|
||||
return (err, res) => {
|
||||
--numInProgress;
|
||||
++numFinished;
|
||||
|
||||
if (error != null) {
|
||||
return;
|
||||
}
|
||||
if (err != null) {
|
||||
error = err;
|
||||
return callback(error);
|
||||
}
|
||||
|
||||
results[index] = res;
|
||||
|
||||
if (numFinished === fns.length) {
|
||||
return callback(null, results);
|
||||
} else if (numFinished + numInProgress < fns.length) {
|
||||
_start();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
13
node_modules/mongoose/lib/helpers/path/parentPaths.js
generated
vendored
Normal file
13
node_modules/mongoose/lib/helpers/path/parentPaths.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function parentPaths(path) {
|
||||
const pieces = path.split('.');
|
||||
let cur = '';
|
||||
const ret = [];
|
||||
for (let i = 0; i < pieces.length; ++i) {
|
||||
cur += (cur.length > 0 ? '.' : '') + pieces[i];
|
||||
ret.push(cur);
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
16
node_modules/mongoose/lib/helpers/path/setDottedPath.js
generated
vendored
Normal file
16
node_modules/mongoose/lib/helpers/path/setDottedPath.js
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function setDottedPath(obj, path, val) {
|
||||
const parts = path.split('.');
|
||||
let cur = obj;
|
||||
for (const part of parts.slice(0, -1)) {
|
||||
if (cur[part] == null) {
|
||||
cur[part] = {};
|
||||
}
|
||||
|
||||
cur = cur[part];
|
||||
}
|
||||
|
||||
const last = parts[parts.length - 1];
|
||||
cur[last] = val;
|
||||
};
|
||||
10
node_modules/mongoose/lib/helpers/populate/SkipPopulateValue.js
generated
vendored
Normal file
10
node_modules/mongoose/lib/helpers/populate/SkipPopulateValue.js
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function SkipPopulateValue(val) {
|
||||
if (!(this instanceof SkipPopulateValue)) {
|
||||
return new SkipPopulateValue(val);
|
||||
}
|
||||
|
||||
this.val = val;
|
||||
return this;
|
||||
};
|
||||
98
node_modules/mongoose/lib/helpers/populate/assignRawDocsToIdStructure.js
generated
vendored
Normal file
98
node_modules/mongoose/lib/helpers/populate/assignRawDocsToIdStructure.js
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
'use strict';
|
||||
|
||||
const leanPopulateMap = require('./leanPopulateMap');
|
||||
const modelSymbol = require('../symbols').modelSymbol;
|
||||
const utils = require('../../utils');
|
||||
|
||||
module.exports = assignRawDocsToIdStructure;
|
||||
|
||||
/*!
|
||||
* Assign `vals` returned by mongo query to the `rawIds`
|
||||
* structure returned from utils.getVals() honoring
|
||||
* query sort order if specified by user.
|
||||
*
|
||||
* This can be optimized.
|
||||
*
|
||||
* Rules:
|
||||
*
|
||||
* if the value of the path is not an array, use findOne rules, else find.
|
||||
* for findOne the results are assigned directly to doc path (including null results).
|
||||
* for find, if user specified sort order, results are assigned directly
|
||||
* else documents are put back in original order of array if found in results
|
||||
*
|
||||
* @param {Array} rawIds
|
||||
* @param {Array} vals
|
||||
* @param {Boolean} sort
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function assignRawDocsToIdStructure(rawIds, resultDocs, resultOrder, options, recursed) {
|
||||
// honor user specified sort order
|
||||
const newOrder = [];
|
||||
const sorting = options.sort && rawIds.length > 1;
|
||||
const nullIfNotFound = options.$nullIfNotFound;
|
||||
let doc;
|
||||
let sid;
|
||||
let id;
|
||||
|
||||
for (let i = 0; i < rawIds.length; ++i) {
|
||||
id = rawIds[i];
|
||||
|
||||
if (Array.isArray(id)) {
|
||||
// handle [ [id0, id2], [id3] ]
|
||||
assignRawDocsToIdStructure(id, resultDocs, resultOrder, options, true);
|
||||
newOrder.push(id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (id === null && !sorting) {
|
||||
// keep nulls for findOne unless sorting, which always
|
||||
// removes them (backward compat)
|
||||
newOrder.push(id);
|
||||
continue;
|
||||
}
|
||||
|
||||
sid = String(id);
|
||||
|
||||
doc = resultDocs[sid];
|
||||
// If user wants separate copies of same doc, use this option
|
||||
if (options.clone && doc != null) {
|
||||
if (options.lean) {
|
||||
const _model = leanPopulateMap.get(doc);
|
||||
doc = utils.clone(doc);
|
||||
leanPopulateMap.set(doc, _model);
|
||||
} else {
|
||||
doc = doc.constructor.hydrate(doc._doc);
|
||||
}
|
||||
}
|
||||
|
||||
if (recursed) {
|
||||
if (doc) {
|
||||
if (sorting) {
|
||||
newOrder[resultOrder[sid]] = doc;
|
||||
} else {
|
||||
newOrder.push(doc);
|
||||
}
|
||||
} else if (id != null && id[modelSymbol] != null) {
|
||||
newOrder.push(id);
|
||||
} else {
|
||||
newOrder.push(options.retainNullValues || nullIfNotFound ? null : id);
|
||||
}
|
||||
} else {
|
||||
// apply findOne behavior - if document in results, assign, else assign null
|
||||
newOrder[i] = doc || null;
|
||||
}
|
||||
}
|
||||
|
||||
rawIds.length = 0;
|
||||
if (newOrder.length) {
|
||||
// reassign the documents based on corrected order
|
||||
|
||||
// forEach skips over sparse entries in arrays so we
|
||||
// can safely use this to our advantage dealing with sorted
|
||||
// result sets too.
|
||||
newOrder.forEach(function(doc, i) {
|
||||
rawIds[i] = doc;
|
||||
});
|
||||
}
|
||||
}
|
||||
285
node_modules/mongoose/lib/helpers/populate/assignVals.js
generated
vendored
Normal file
285
node_modules/mongoose/lib/helpers/populate/assignVals.js
generated
vendored
Normal file
@@ -0,0 +1,285 @@
|
||||
'use strict';
|
||||
|
||||
const SkipPopulateValue = require('./SkipPopulateValue');
|
||||
const assignRawDocsToIdStructure = require('./assignRawDocsToIdStructure');
|
||||
const get = require('../get');
|
||||
const getVirtual = require('./getVirtual');
|
||||
const leanPopulateMap = require('./leanPopulateMap');
|
||||
const lookupLocalFields = require('./lookupLocalFields');
|
||||
const mpath = require('mpath');
|
||||
const sift = require('sift').default;
|
||||
const utils = require('../../utils');
|
||||
|
||||
module.exports = function assignVals(o) {
|
||||
// Options that aren't explicitly listed in `populateOptions`
|
||||
const userOptions = Object.assign({}, get(o, 'allOptions.options.options'), get(o, 'allOptions.options'));
|
||||
// `o.options` contains options explicitly listed in `populateOptions`, like
|
||||
// `match` and `limit`.
|
||||
const populateOptions = Object.assign({}, o.options, userOptions, {
|
||||
justOne: o.justOne
|
||||
});
|
||||
populateOptions.$nullIfNotFound = o.isVirtual;
|
||||
const populatedModel = o.populatedModel;
|
||||
|
||||
const originalIds = [].concat(o.rawIds);
|
||||
|
||||
// replace the original ids in our intermediate _ids structure
|
||||
// with the documents found by query
|
||||
o.allIds = [].concat(o.allIds);
|
||||
assignRawDocsToIdStructure(o.rawIds, o.rawDocs, o.rawOrder, populateOptions);
|
||||
|
||||
// now update the original documents being populated using the
|
||||
// result structure that contains real documents.
|
||||
const docs = o.docs;
|
||||
const rawIds = o.rawIds;
|
||||
const options = o.options;
|
||||
const count = o.count && o.isVirtual;
|
||||
let i;
|
||||
|
||||
function setValue(val) {
|
||||
if (count) {
|
||||
return val;
|
||||
}
|
||||
if (val instanceof SkipPopulateValue) {
|
||||
return val.val;
|
||||
}
|
||||
|
||||
const _allIds = o.allIds[i];
|
||||
|
||||
if (o.justOne === true && Array.isArray(val)) {
|
||||
// Might be an embedded discriminator (re: gh-9244) with multiple models, so make sure to pick the right
|
||||
// model before assigning.
|
||||
const ret = [];
|
||||
for (const doc of val) {
|
||||
const _docPopulatedModel = leanPopulateMap.get(doc);
|
||||
if (_docPopulatedModel == null || _docPopulatedModel === populatedModel) {
|
||||
ret.push(doc);
|
||||
}
|
||||
}
|
||||
// Since we don't want to have to create a new mongoosearray, make sure to
|
||||
// modify the array in place
|
||||
while (val.length > ret.length) {
|
||||
Array.prototype.pop.apply(val, []);
|
||||
}
|
||||
for (let i = 0; i < ret.length; ++i) {
|
||||
val[i] = ret[i];
|
||||
}
|
||||
|
||||
return valueFilter(val[0], options, populateOptions, _allIds);
|
||||
} else if (o.justOne === false && !Array.isArray(val)) {
|
||||
return valueFilter([val], options, populateOptions, _allIds);
|
||||
}
|
||||
return valueFilter(val, options, populateOptions, _allIds);
|
||||
}
|
||||
|
||||
for (i = 0; i < docs.length; ++i) {
|
||||
const _path = o.path.endsWith('.$*') ? o.path.slice(0, -3) : o.path;
|
||||
const existingVal = mpath.get(_path, docs[i], lookupLocalFields);
|
||||
if (existingVal == null && !getVirtual(o.originalModel.schema, _path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let valueToSet;
|
||||
if (count) {
|
||||
valueToSet = numDocs(rawIds[i]);
|
||||
} else if (Array.isArray(o.match)) {
|
||||
valueToSet = Array.isArray(rawIds[i]) ?
|
||||
rawIds[i].filter(sift(o.match[i])) :
|
||||
[rawIds[i]].filter(sift(o.match[i]))[0];
|
||||
} else {
|
||||
valueToSet = rawIds[i];
|
||||
}
|
||||
|
||||
// If we're populating a map, the existing value will be an object, so
|
||||
// we need to transform again
|
||||
const originalSchema = o.originalModel.schema;
|
||||
const isDoc = get(docs[i], '$__', null) != null;
|
||||
let isMap = isDoc ?
|
||||
existingVal instanceof Map :
|
||||
utils.isPOJO(existingVal);
|
||||
// If we pass the first check, also make sure the local field's schematype
|
||||
// is map (re: gh-6460)
|
||||
isMap = isMap && get(originalSchema._getSchema(_path), '$isSchemaMap');
|
||||
if (!o.isVirtual && isMap) {
|
||||
const _keys = existingVal instanceof Map ?
|
||||
Array.from(existingVal.keys()) :
|
||||
Object.keys(existingVal);
|
||||
valueToSet = valueToSet.reduce((cur, v, i) => {
|
||||
cur.set(_keys[i], v);
|
||||
return cur;
|
||||
}, new Map());
|
||||
}
|
||||
|
||||
if (isDoc && Array.isArray(valueToSet)) {
|
||||
for (const val of valueToSet) {
|
||||
if (val != null && val.$__ != null) {
|
||||
val.$__.parent = docs[i];
|
||||
}
|
||||
}
|
||||
} else if (isDoc && valueToSet != null && valueToSet.$__ != null) {
|
||||
valueToSet.$__.parent = docs[i];
|
||||
}
|
||||
|
||||
if (o.isVirtual && isDoc) {
|
||||
docs[i].populated(_path, o.justOne ? originalIds[0] : originalIds, o.allOptions);
|
||||
// If virtual populate and doc is already init-ed, need to walk through
|
||||
// the actual doc to set rather than setting `_doc` directly
|
||||
mpath.set(_path, valueToSet, docs[i], setValue);
|
||||
continue;
|
||||
}
|
||||
|
||||
const parts = _path.split('.');
|
||||
let cur = docs[i];
|
||||
const curPath = parts[0];
|
||||
for (let j = 0; j < parts.length - 1; ++j) {
|
||||
// If we get to an array with a dotted path, like `arr.foo`, don't set
|
||||
// `foo` on the array.
|
||||
if (Array.isArray(cur) && !utils.isArrayIndex(parts[j])) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (parts[j] === '$*') {
|
||||
break;
|
||||
}
|
||||
|
||||
if (cur[parts[j]] == null) {
|
||||
// If nothing to set, avoid creating an unnecessary array. Otherwise
|
||||
// we'll end up with a single doc in the array with only defaults.
|
||||
// See gh-8342, gh-8455
|
||||
const schematype = originalSchema._getSchema(curPath);
|
||||
if (valueToSet == null && schematype != null && schematype.$isMongooseArray) {
|
||||
break;
|
||||
}
|
||||
cur[parts[j]] = {};
|
||||
}
|
||||
cur = cur[parts[j]];
|
||||
// If the property in MongoDB is a primitive, we won't be able to populate
|
||||
// the nested path, so skip it. See gh-7545
|
||||
if (typeof cur !== 'object') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (docs[i].$__) {
|
||||
docs[i].populated(_path, o.allIds[i], o.allOptions);
|
||||
}
|
||||
|
||||
// If lean, need to check that each individual virtual respects
|
||||
// `justOne`, because you may have a populated virtual with `justOne`
|
||||
// underneath an array. See gh-6867
|
||||
mpath.set(_path, valueToSet, docs[i], lookupLocalFields, setValue, false);
|
||||
}
|
||||
};
|
||||
|
||||
function numDocs(v) {
|
||||
if (Array.isArray(v)) {
|
||||
// If setting underneath an array of populated subdocs, we may have an
|
||||
// array of arrays. See gh-7573
|
||||
if (v.some(el => Array.isArray(el))) {
|
||||
return v.map(el => numDocs(el));
|
||||
}
|
||||
return v.length;
|
||||
}
|
||||
return v == null ? 0 : 1;
|
||||
}
|
||||
|
||||
/*!
|
||||
* 1) Apply backwards compatible find/findOne behavior to sub documents
|
||||
*
|
||||
* find logic:
|
||||
* a) filter out non-documents
|
||||
* b) remove _id from sub docs when user specified
|
||||
*
|
||||
* findOne
|
||||
* a) if no doc found, set to null
|
||||
* b) remove _id from sub docs when user specified
|
||||
*
|
||||
* 2) Remove _ids when specified by users query.
|
||||
*
|
||||
* background:
|
||||
* _ids are left in the query even when user excludes them so
|
||||
* that population mapping can occur.
|
||||
*/
|
||||
|
||||
function valueFilter(val, assignmentOpts, populateOptions, allIds) {
|
||||
const userSpecifiedTransform = typeof populateOptions.transform === 'function';
|
||||
const transform = userSpecifiedTransform ? populateOptions.transform : noop;
|
||||
if (Array.isArray(val)) {
|
||||
// find logic
|
||||
const ret = [];
|
||||
const numValues = val.length;
|
||||
for (let i = 0; i < numValues; ++i) {
|
||||
let subdoc = val[i];
|
||||
const _allIds = Array.isArray(allIds) ? allIds[i] : allIds;
|
||||
if (!isPopulatedObject(subdoc) && (!populateOptions.retainNullValues || subdoc != null) && !userSpecifiedTransform) {
|
||||
continue;
|
||||
} else if (userSpecifiedTransform) {
|
||||
subdoc = transform(isPopulatedObject(subdoc) ? subdoc : null, _allIds);
|
||||
}
|
||||
maybeRemoveId(subdoc, assignmentOpts);
|
||||
ret.push(subdoc);
|
||||
if (assignmentOpts.originalLimit &&
|
||||
ret.length >= assignmentOpts.originalLimit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Since we don't want to have to create a new mongoosearray, make sure to
|
||||
// modify the array in place
|
||||
while (val.length > ret.length) {
|
||||
Array.prototype.pop.apply(val, []);
|
||||
}
|
||||
for (let i = 0; i < ret.length; ++i) {
|
||||
val[i] = ret[i];
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
// findOne
|
||||
if (isPopulatedObject(val) || utils.isPOJO(val)) {
|
||||
maybeRemoveId(val, assignmentOpts);
|
||||
return transform(val, allIds);
|
||||
}
|
||||
if (val instanceof Map) {
|
||||
return val;
|
||||
}
|
||||
|
||||
if (populateOptions.justOne === false) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return val == null ? transform(val, allIds) : transform(null, allIds);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Remove _id from `subdoc` if user specified "lean" query option
|
||||
*/
|
||||
|
||||
function maybeRemoveId(subdoc, assignmentOpts) {
|
||||
if (subdoc != null && assignmentOpts.excludeId) {
|
||||
if (typeof subdoc.$__setValue === 'function') {
|
||||
delete subdoc._doc._id;
|
||||
} else {
|
||||
delete subdoc._id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Determine if `obj` is something we can set a populated path to. Can be a
|
||||
* document, a lean document, or an array/map that contains docs.
|
||||
*/
|
||||
|
||||
function isPopulatedObject(obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Array.isArray(obj) ||
|
||||
obj.$isMongooseMap ||
|
||||
obj.$__ != null ||
|
||||
leanPopulateMap.has(obj);
|
||||
}
|
||||
|
||||
function noop(v) {
|
||||
return v;
|
||||
}
|
||||
79
node_modules/mongoose/lib/helpers/populate/createPopulateQueryFilter.js
generated
vendored
Normal file
79
node_modules/mongoose/lib/helpers/populate/createPopulateQueryFilter.js
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
'use strict';
|
||||
|
||||
const SkipPopulateValue = require('./SkipPopulateValue');
|
||||
const parentPaths = require('../path/parentPaths');
|
||||
|
||||
module.exports = function createPopulateQueryFilter(ids, _match, _foreignField, model, skipInvalidIds) {
|
||||
const match = _formatMatch(_match);
|
||||
|
||||
if (_foreignField.size === 1) {
|
||||
const foreignField = Array.from(_foreignField)[0];
|
||||
const foreignSchemaType = model.schema.path(foreignField);
|
||||
if (foreignField !== '_id' || !match['_id']) {
|
||||
ids = _filterInvalidIds(ids, foreignSchemaType, skipInvalidIds);
|
||||
match[foreignField] = { $in: ids };
|
||||
}
|
||||
|
||||
const _parentPaths = parentPaths(foreignField);
|
||||
for (let i = 0; i < _parentPaths.length - 1; ++i) {
|
||||
const cur = _parentPaths[i];
|
||||
if (match[cur] != null && match[cur].$elemMatch != null) {
|
||||
match[cur].$elemMatch[foreignField.slice(cur.length + 1)] = { $in: ids };
|
||||
delete match[foreignField];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const $or = [];
|
||||
if (Array.isArray(match.$or)) {
|
||||
match.$and = [{ $or: match.$or }, { $or: $or }];
|
||||
delete match.$or;
|
||||
} else {
|
||||
match.$or = $or;
|
||||
}
|
||||
for (const foreignField of _foreignField) {
|
||||
if (foreignField !== '_id' || !match['_id']) {
|
||||
const foreignSchemaType = model.schema.path(foreignField);
|
||||
ids = _filterInvalidIds(ids, foreignSchemaType, skipInvalidIds);
|
||||
$or.push({ [foreignField]: { $in: ids } });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return match;
|
||||
};
|
||||
|
||||
/*!
|
||||
* Optionally filter out invalid ids that don't conform to foreign field's schema
|
||||
* to avoid cast errors (gh-7706)
|
||||
*/
|
||||
|
||||
function _filterInvalidIds(ids, foreignSchemaType, skipInvalidIds) {
|
||||
ids = ids.filter(v => !(v instanceof SkipPopulateValue));
|
||||
if (!skipInvalidIds) {
|
||||
return ids;
|
||||
}
|
||||
return ids.filter(id => {
|
||||
try {
|
||||
foreignSchemaType.cast(id);
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*!
|
||||
* Format `mod.match` given that it may be an array that we need to $or if
|
||||
* the client has multiple docs with match functions
|
||||
*/
|
||||
|
||||
function _formatMatch(match) {
|
||||
if (Array.isArray(match)) {
|
||||
if (match.length > 1) {
|
||||
return { $or: [].concat(match.map(m => Object.assign({}, m))) };
|
||||
}
|
||||
return Object.assign({}, match[0]);
|
||||
}
|
||||
return Object.assign({}, match);
|
||||
}
|
||||
541
node_modules/mongoose/lib/helpers/populate/getModelsMapForPopulate.js
generated
vendored
Normal file
541
node_modules/mongoose/lib/helpers/populate/getModelsMapForPopulate.js
generated
vendored
Normal file
@@ -0,0 +1,541 @@
|
||||
'use strict';
|
||||
|
||||
const MongooseError = require('../../error/index');
|
||||
const SkipPopulateValue = require('./SkipPopulateValue');
|
||||
const get = require('../get');
|
||||
const getDiscriminatorByValue = require('../discriminator/getDiscriminatorByValue');
|
||||
const isPathExcluded = require('../projection/isPathExcluded');
|
||||
const getSchemaTypes = require('./getSchemaTypes');
|
||||
const getVirtual = require('./getVirtual');
|
||||
const lookupLocalFields = require('./lookupLocalFields');
|
||||
const mpath = require('mpath');
|
||||
const normalizeRefPath = require('./normalizeRefPath');
|
||||
const util = require('util');
|
||||
const utils = require('../../utils');
|
||||
|
||||
const modelSymbol = require('../symbols').modelSymbol;
|
||||
const populateModelSymbol = require('../symbols').populateModelSymbol;
|
||||
const schemaMixedSymbol = require('../../schema/symbols').schemaMixedSymbol;
|
||||
|
||||
module.exports = function getModelsMapForPopulate(model, docs, options) {
|
||||
let i;
|
||||
let doc;
|
||||
const len = docs.length;
|
||||
const available = {};
|
||||
const map = [];
|
||||
const modelNameFromQuery = options.model && options.model.modelName || options.model;
|
||||
let schema;
|
||||
let refPath;
|
||||
let Model;
|
||||
let currentOptions;
|
||||
let modelNames;
|
||||
let modelName;
|
||||
|
||||
const originalModel = options.model;
|
||||
let isVirtual = false;
|
||||
const modelSchema = model.schema;
|
||||
|
||||
let allSchemaTypes = getSchemaTypes(modelSchema, null, options.path);
|
||||
allSchemaTypes = Array.isArray(allSchemaTypes) ? allSchemaTypes : [allSchemaTypes].filter(v => v != null);
|
||||
const _firstWithRefPath = allSchemaTypes.find(schematype => get(schematype, 'options.refPath', null) != null);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
doc = docs[i];
|
||||
let justOne = null;
|
||||
schema = getSchemaTypes(modelSchema, doc, options.path);
|
||||
// Special case: populating a path that's a DocumentArray unless
|
||||
// there's an explicit `ref` or `refPath` re: gh-8946
|
||||
if (schema != null &&
|
||||
schema.$isMongooseDocumentArray &&
|
||||
schema.options.ref == null &&
|
||||
schema.options.refPath == null) {
|
||||
continue;
|
||||
}
|
||||
// Populating a nested path should always be a no-op re: #9073.
|
||||
// People shouldn't do this, but apparently they do.
|
||||
if (options._localModel != null && options._localModel.schema.nested[options.path]) {
|
||||
continue;
|
||||
}
|
||||
const isUnderneathDocArray = schema && schema.$isUnderneathDocArray;
|
||||
if (isUnderneathDocArray && get(options, 'options.sort') != null) {
|
||||
return new MongooseError('Cannot populate with `sort` on path ' + options.path +
|
||||
' because it is a subproperty of a document array');
|
||||
}
|
||||
|
||||
modelNames = null;
|
||||
let isRefPath = !!_firstWithRefPath;
|
||||
let normalizedRefPath = _firstWithRefPath ? get(_firstWithRefPath, 'options.refPath', null) : null;
|
||||
let schemaOptions = null;
|
||||
|
||||
if (Array.isArray(schema)) {
|
||||
const schemasArray = schema;
|
||||
for (const _schema of schemasArray) {
|
||||
let _modelNames;
|
||||
let res;
|
||||
try {
|
||||
res = _getModelNames(doc, _schema);
|
||||
_modelNames = res.modelNames;
|
||||
isRefPath = isRefPath || res.isRefPath;
|
||||
normalizedRefPath = normalizeRefPath(normalizedRefPath, doc, options.path) ||
|
||||
res.refPath;
|
||||
justOne = res.justOne;
|
||||
} catch (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
if (isRefPath && !res.isRefPath) {
|
||||
continue;
|
||||
}
|
||||
if (!_modelNames) {
|
||||
continue;
|
||||
}
|
||||
modelNames = modelNames || [];
|
||||
for (const modelName of _modelNames) {
|
||||
if (modelNames.indexOf(modelName) === -1) {
|
||||
modelNames.push(modelName);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
const res = _getModelNames(doc, schema);
|
||||
modelNames = res.modelNames;
|
||||
isRefPath = res.isRefPath;
|
||||
normalizedRefPath = res.refPath;
|
||||
justOne = res.justOne;
|
||||
schemaOptions = get(schema, 'options.populate', null);
|
||||
} catch (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!modelNames) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const _virtualRes = getVirtual(model.schema, options.path);
|
||||
const virtual = _virtualRes == null ? null : _virtualRes.virtual;
|
||||
|
||||
let localField;
|
||||
let count = false;
|
||||
if (virtual && virtual.options) {
|
||||
const virtualPrefix = _virtualRes.nestedSchemaPath ?
|
||||
_virtualRes.nestedSchemaPath + '.' : '';
|
||||
if (typeof virtual.options.localField === 'function') {
|
||||
localField = virtualPrefix + virtual.options.localField.call(doc, doc);
|
||||
} else if (Array.isArray(virtual.options.localField)) {
|
||||
localField = virtual.options.localField.map(field => virtualPrefix + field);
|
||||
} else {
|
||||
localField = virtualPrefix + virtual.options.localField;
|
||||
}
|
||||
count = virtual.options.count;
|
||||
|
||||
if (virtual.options.skip != null && !options.hasOwnProperty('skip')) {
|
||||
options.skip = virtual.options.skip;
|
||||
}
|
||||
if (virtual.options.limit != null && !options.hasOwnProperty('limit')) {
|
||||
options.limit = virtual.options.limit;
|
||||
}
|
||||
if (virtual.options.perDocumentLimit != null && !options.hasOwnProperty('perDocumentLimit')) {
|
||||
options.perDocumentLimit = virtual.options.perDocumentLimit;
|
||||
}
|
||||
} else {
|
||||
localField = options.path;
|
||||
}
|
||||
let foreignField = virtual && virtual.options ?
|
||||
virtual.options.foreignField :
|
||||
'_id';
|
||||
|
||||
// `justOne = null` means we don't know from the schema whether the end
|
||||
// result should be an array or a single doc. This can result from
|
||||
// populating a POJO using `Model.populate()`
|
||||
if ('justOne' in options && options.justOne !== void 0) {
|
||||
justOne = options.justOne;
|
||||
} else if (virtual && virtual.options && virtual.options.refPath) {
|
||||
const normalizedRefPath =
|
||||
normalizeRefPath(virtual.options.refPath, doc, options.path);
|
||||
justOne = !!virtual.options.justOne;
|
||||
isVirtual = true;
|
||||
const refValue = utils.getValue(normalizedRefPath, doc);
|
||||
modelNames = Array.isArray(refValue) ? refValue : [refValue];
|
||||
} else if (virtual && virtual.options && virtual.options.ref) {
|
||||
let normalizedRef;
|
||||
if (typeof virtual.options.ref === 'function') {
|
||||
normalizedRef = virtual.options.ref.call(doc, doc);
|
||||
} else {
|
||||
normalizedRef = virtual.options.ref;
|
||||
}
|
||||
justOne = !!virtual.options.justOne;
|
||||
isVirtual = true;
|
||||
if (!modelNames) {
|
||||
modelNames = [].concat(normalizedRef);
|
||||
}
|
||||
} else if (schema && !schema[schemaMixedSymbol]) {
|
||||
// Skip Mixed types because we explicitly don't do casting on those.
|
||||
if (options.path.endsWith('.' + schema.path)) {
|
||||
justOne = Array.isArray(schema) ?
|
||||
schema.every(schema => !schema.$isMongooseArray) :
|
||||
!schema.$isMongooseArray;
|
||||
}
|
||||
}
|
||||
|
||||
if (!modelNames) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (virtual && (!localField || !foreignField)) {
|
||||
return new MongooseError('If you are populating a virtual, you must set the ' +
|
||||
'localField and foreignField options');
|
||||
}
|
||||
|
||||
options.isVirtual = isVirtual;
|
||||
options.virtual = virtual;
|
||||
if (typeof localField === 'function') {
|
||||
localField = localField.call(doc, doc);
|
||||
}
|
||||
if (typeof foreignField === 'function') {
|
||||
foreignField = foreignField.call(doc);
|
||||
}
|
||||
|
||||
let match = get(options, 'match', null) ||
|
||||
get(currentOptions, 'match', null) ||
|
||||
get(options, 'virtual.options.match', null) ||
|
||||
get(options, 'virtual.options.options.match', null);
|
||||
|
||||
let hasMatchFunction = typeof match === 'function';
|
||||
if (hasMatchFunction) {
|
||||
match = match.call(doc, doc);
|
||||
}
|
||||
|
||||
if (Array.isArray(localField) && Array.isArray(foreignField) && localField.length === foreignField.length) {
|
||||
match = Object.assign({}, match);
|
||||
for (let i = 1; i < localField.length; ++i) {
|
||||
match[foreignField[i]] = convertTo_id(mpath.get(localField[i], doc, lookupLocalFields), schema);
|
||||
hasMatchFunction = true;
|
||||
}
|
||||
|
||||
localField = localField[0];
|
||||
foreignField = foreignField[0];
|
||||
}
|
||||
|
||||
const localFieldPathType = modelSchema._getPathType(localField);
|
||||
const localFieldPath = localFieldPathType === 'real' ? modelSchema.path(localField) : localFieldPathType.schema;
|
||||
const localFieldGetters = localFieldPath && localFieldPath.getters ? localFieldPath.getters : [];
|
||||
let ret;
|
||||
|
||||
const _populateOptions = get(options, 'options', {});
|
||||
|
||||
const getters = 'getters' in _populateOptions ?
|
||||
_populateOptions.getters :
|
||||
options.isVirtual && get(virtual, 'options.getters', false);
|
||||
if (localFieldGetters.length > 0 && getters) {
|
||||
const hydratedDoc = (doc.$__ != null) ? doc : model.hydrate(doc);
|
||||
const localFieldValue = mpath.get(localField, doc, lookupLocalFields);
|
||||
if (Array.isArray(localFieldValue)) {
|
||||
const localFieldHydratedValue = mpath.get(localField.split('.').slice(0, -1), hydratedDoc, lookupLocalFields);
|
||||
ret = localFieldValue.map((localFieldArrVal, localFieldArrIndex) =>
|
||||
localFieldPath.applyGetters(localFieldArrVal, localFieldHydratedValue[localFieldArrIndex]));
|
||||
} else {
|
||||
ret = localFieldPath.applyGetters(localFieldValue, hydratedDoc);
|
||||
}
|
||||
} else {
|
||||
ret = convertTo_id(mpath.get(localField, doc, lookupLocalFields), schema);
|
||||
}
|
||||
|
||||
const id = String(utils.getValue(foreignField, doc));
|
||||
options._docs[id] = Array.isArray(ret) ? ret.slice() : ret;
|
||||
|
||||
// Re: gh-8452. Embedded discriminators may not have `refPath`, so clear
|
||||
// out embedded discriminator docs that don't have a `refPath` on the
|
||||
// populated path.
|
||||
if (isRefPath && normalizedRefPath != null) {
|
||||
const pieces = normalizedRefPath.split('.');
|
||||
let cur = '';
|
||||
for (let j = 0; j < pieces.length; ++j) {
|
||||
const piece = pieces[j];
|
||||
cur = cur + (cur.length === 0 ? '' : '.') + piece;
|
||||
const schematype = modelSchema.path(cur);
|
||||
if (schematype != null &&
|
||||
schematype.$isMongooseArray &&
|
||||
schematype.caster.discriminators != null &&
|
||||
Object.keys(schematype.caster.discriminators).length > 0) {
|
||||
const subdocs = utils.getValue(cur, doc);
|
||||
const remnant = options.path.substr(cur.length + 1);
|
||||
const discriminatorKey = schematype.caster.schema.options.discriminatorKey;
|
||||
modelNames = [];
|
||||
for (const subdoc of subdocs) {
|
||||
const discriminatorName = utils.getValue(discriminatorKey, subdoc);
|
||||
const discriminator = schematype.caster.discriminators[discriminatorName];
|
||||
const discriminatorSchema = discriminator && discriminator.schema;
|
||||
if (discriminatorSchema == null) {
|
||||
continue;
|
||||
}
|
||||
const _path = discriminatorSchema.path(remnant);
|
||||
if (_path == null || _path.options.refPath == null) {
|
||||
const docValue = utils.getValue(localField.substr(cur.length + 1), subdoc);
|
||||
ret = ret.map(v => v === docValue ? SkipPopulateValue(v) : v);
|
||||
continue;
|
||||
}
|
||||
const modelName = utils.getValue(pieces.slice(j + 1).join('.'), subdoc);
|
||||
modelNames.push(modelName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let k = modelNames.length;
|
||||
while (k--) {
|
||||
modelName = modelNames[k];
|
||||
if (modelName == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// `PopulateOptions#connection`: if the model is passed as a string, the
|
||||
// connection matters because different connections have different models.
|
||||
const connection = options.connection != null ? options.connection : model.db;
|
||||
|
||||
try {
|
||||
Model = originalModel && originalModel[modelSymbol] ?
|
||||
originalModel :
|
||||
modelName[modelSymbol] ? modelName : connection.model(modelName);
|
||||
} catch (error) {
|
||||
// If `ret` is undefined, we'll add an empty entry to modelsMap. We shouldn't
|
||||
// execute a query, but it is necessary to make sure `justOne` gets handled
|
||||
// correctly for setting an empty array (see gh-8455)
|
||||
if (ret !== undefined) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
let ids = ret;
|
||||
const flat = Array.isArray(ret) ? utils.array.flatten(ret) : [];
|
||||
|
||||
if (isRefPath && Array.isArray(ret) && flat.length === modelNames.length) {
|
||||
ids = flat.filter((val, i) => modelNames[i] === modelName);
|
||||
}
|
||||
|
||||
if (!available[modelName] || currentOptions.perDocumentLimit != null || get(currentOptions, 'options.perDocumentLimit') != null) {
|
||||
currentOptions = {
|
||||
model: Model
|
||||
};
|
||||
|
||||
if (isVirtual && get(virtual, 'options.options')) {
|
||||
currentOptions.options = utils.clone(virtual.options.options);
|
||||
} else if (schemaOptions != null) {
|
||||
currentOptions.options = Object.assign({}, schemaOptions);
|
||||
}
|
||||
utils.merge(currentOptions, options);
|
||||
|
||||
// Used internally for checking what model was used to populate this
|
||||
// path.
|
||||
options[populateModelSymbol] = Model;
|
||||
|
||||
available[modelName] = {
|
||||
model: Model,
|
||||
options: currentOptions,
|
||||
match: hasMatchFunction ? [match] : match,
|
||||
docs: [doc],
|
||||
ids: [ids],
|
||||
allIds: [ret],
|
||||
localField: new Set([localField]),
|
||||
foreignField: new Set([foreignField]),
|
||||
justOne: justOne,
|
||||
isVirtual: isVirtual,
|
||||
virtual: virtual,
|
||||
count: count,
|
||||
[populateModelSymbol]: Model
|
||||
};
|
||||
map.push(available[modelName]);
|
||||
} else {
|
||||
available[modelName].localField.add(localField);
|
||||
available[modelName].foreignField.add(foreignField);
|
||||
available[modelName].docs.push(doc);
|
||||
available[modelName].ids.push(ids);
|
||||
available[modelName].allIds.push(ret);
|
||||
if (hasMatchFunction) {
|
||||
available[modelName].match.push(match);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
|
||||
function _getModelNames(doc, schema) {
|
||||
let modelNames;
|
||||
let discriminatorKey;
|
||||
let isRefPath = false;
|
||||
let justOne = null;
|
||||
|
||||
if (schema && schema.caster) {
|
||||
schema = schema.caster;
|
||||
}
|
||||
if (schema && schema.$isSchemaMap) {
|
||||
schema = schema.$__schemaType;
|
||||
}
|
||||
|
||||
if (!schema && model.discriminators) {
|
||||
discriminatorKey = model.schema.discriminatorMapping.key;
|
||||
}
|
||||
|
||||
refPath = schema && schema.options && schema.options.refPath;
|
||||
|
||||
const normalizedRefPath = normalizeRefPath(refPath, doc, options.path);
|
||||
|
||||
if (modelNameFromQuery) {
|
||||
modelNames = [modelNameFromQuery]; // query options
|
||||
} else if (normalizedRefPath) {
|
||||
if (options._queryProjection != null && isPathExcluded(options._queryProjection, normalizedRefPath)) {
|
||||
throw new MongooseError('refPath `' + normalizedRefPath +
|
||||
'` must not be excluded in projection, got ' +
|
||||
util.inspect(options._queryProjection));
|
||||
}
|
||||
|
||||
if (modelSchema.virtuals.hasOwnProperty(normalizedRefPath) && doc.$__ == null) {
|
||||
modelNames = [modelSchema.virtuals[normalizedRefPath].applyGetters(void 0, doc)];
|
||||
} else {
|
||||
modelNames = utils.getValue(normalizedRefPath, doc);
|
||||
}
|
||||
|
||||
if (Array.isArray(modelNames)) {
|
||||
modelNames = utils.array.flatten(modelNames);
|
||||
}
|
||||
|
||||
isRefPath = true;
|
||||
} else {
|
||||
let modelForCurrentDoc = model;
|
||||
let schemaForCurrentDoc;
|
||||
let discriminatorValue;
|
||||
|
||||
if (!schema && discriminatorKey && (discriminatorValue = utils.getValue(discriminatorKey, doc))) {
|
||||
// `modelNameForFind` is the discriminator value, so we might need
|
||||
// find the discriminated model name
|
||||
const discriminatorModel = getDiscriminatorByValue(model.discriminators, discriminatorValue) || model;
|
||||
if (discriminatorModel != null) {
|
||||
modelForCurrentDoc = discriminatorModel;
|
||||
} else {
|
||||
try {
|
||||
modelForCurrentDoc = model.db.model(discriminatorValue);
|
||||
} catch (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
schemaForCurrentDoc = modelForCurrentDoc.schema._getSchema(options.path);
|
||||
|
||||
if (schemaForCurrentDoc && schemaForCurrentDoc.caster) {
|
||||
schemaForCurrentDoc = schemaForCurrentDoc.caster;
|
||||
}
|
||||
} else {
|
||||
schemaForCurrentDoc = schema;
|
||||
}
|
||||
const _virtualRes = getVirtual(modelForCurrentDoc.schema, options.path);
|
||||
const virtual = _virtualRes == null ? null : _virtualRes.virtual;
|
||||
|
||||
if (schemaForCurrentDoc != null) {
|
||||
justOne = !schemaForCurrentDoc.$isMongooseArray && !schemaForCurrentDoc._arrayPath;
|
||||
}
|
||||
|
||||
let ref;
|
||||
let refPath;
|
||||
|
||||
if ((ref = get(schemaForCurrentDoc, 'options.ref')) != null) {
|
||||
ref = handleRefFunction(ref, doc);
|
||||
modelNames = [ref];
|
||||
} else if ((ref = get(virtual, 'options.ref')) != null) {
|
||||
ref = handleRefFunction(ref, doc);
|
||||
|
||||
// When referencing nested arrays, the ref should be an Array
|
||||
// of modelNames.
|
||||
if (Array.isArray(ref)) {
|
||||
modelNames = ref;
|
||||
} else {
|
||||
modelNames = [ref];
|
||||
}
|
||||
|
||||
isVirtual = true;
|
||||
} else if ((refPath = get(schemaForCurrentDoc, 'options.refPath')) != null) {
|
||||
isRefPath = true;
|
||||
refPath = normalizeRefPath(refPath, doc, options.path);
|
||||
modelNames = utils.getValue(refPath, doc);
|
||||
if (Array.isArray(modelNames)) {
|
||||
modelNames = utils.array.flatten(modelNames);
|
||||
}
|
||||
} else {
|
||||
// We may have a discriminator, in which case we don't want to
|
||||
// populate using the base model by default
|
||||
modelNames = discriminatorKey ? null : [model.modelName];
|
||||
}
|
||||
}
|
||||
|
||||
if (!modelNames) {
|
||||
return { modelNames: modelNames, isRefPath: isRefPath, refPath: normalizedRefPath, justOne: justOne };
|
||||
}
|
||||
|
||||
if (!Array.isArray(modelNames)) {
|
||||
modelNames = [modelNames];
|
||||
}
|
||||
|
||||
return { modelNames: modelNames, isRefPath: isRefPath, refPath: normalizedRefPath, justOne: justOne };
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
function handleRefFunction(ref, doc) {
|
||||
if (typeof ref === 'function' && !ref[modelSymbol]) {
|
||||
return ref.call(doc, doc);
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Retrieve the _id of `val` if a Document or Array of Documents.
|
||||
*
|
||||
* @param {Array|Document|Any} val
|
||||
* @return {Array|Document|Any}
|
||||
*/
|
||||
|
||||
function convertTo_id(val, schema) {
|
||||
if (val != null && val.$__ != null) {
|
||||
return val._id;
|
||||
}
|
||||
if (val != null && val._id != null && (schema == null || !schema.$isSchemaMap)) {
|
||||
return val._id;
|
||||
}
|
||||
|
||||
if (Array.isArray(val)) {
|
||||
for (let i = 0; i < val.length; ++i) {
|
||||
if (val[i] != null && val[i].$__ != null) {
|
||||
val[i] = val[i]._id;
|
||||
}
|
||||
}
|
||||
if (val.isMongooseArray && val.$schema()) {
|
||||
return val.$schema()._castForPopulate(val, val.$parent());
|
||||
}
|
||||
|
||||
return [].concat(val);
|
||||
}
|
||||
|
||||
// `populate('map')` may be an object if populating on a doc that hasn't
|
||||
// been hydrated yet
|
||||
if (val != null &&
|
||||
val.constructor.name === 'Object' &&
|
||||
// The intent here is we should only flatten the object if we expect
|
||||
// to get a Map in the end. Avoid doing this for mixed types.
|
||||
(schema == null || schema[schemaMixedSymbol] == null)) {
|
||||
const ret = [];
|
||||
for (const key of Object.keys(val)) {
|
||||
ret.push(val[key]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
// If doc has already been hydrated, e.g. `doc.populate('map').execPopulate()`
|
||||
// then `val` will already be a map
|
||||
if (val instanceof Map) {
|
||||
return Array.from(val.values());
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
209
node_modules/mongoose/lib/helpers/populate/getSchemaTypes.js
generated
vendored
Normal file
209
node_modules/mongoose/lib/helpers/populate/getSchemaTypes.js
generated
vendored
Normal file
@@ -0,0 +1,209 @@
|
||||
'use strict';
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
const Mixed = require('../../schema/mixed');
|
||||
const get = require('../get');
|
||||
const leanPopulateMap = require('./leanPopulateMap');
|
||||
const mpath = require('mpath');
|
||||
|
||||
const populateModelSymbol = require('../symbols').populateModelSymbol;
|
||||
|
||||
/*!
|
||||
* @param {Schema} schema
|
||||
* @param {Object} doc POJO
|
||||
* @param {string} path
|
||||
*/
|
||||
|
||||
module.exports = function getSchemaTypes(schema, doc, path) {
|
||||
const pathschema = schema.path(path);
|
||||
const topLevelDoc = doc;
|
||||
if (pathschema) {
|
||||
return pathschema;
|
||||
}
|
||||
|
||||
function search(parts, schema, subdoc, nestedPath) {
|
||||
let p = parts.length + 1;
|
||||
let foundschema;
|
||||
let trypath;
|
||||
|
||||
while (p--) {
|
||||
trypath = parts.slice(0, p).join('.');
|
||||
foundschema = schema.path(trypath);
|
||||
if (foundschema == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (foundschema.caster) {
|
||||
// array of Mixed?
|
||||
if (foundschema.caster instanceof Mixed) {
|
||||
return foundschema.caster;
|
||||
}
|
||||
|
||||
let schemas = null;
|
||||
if (foundschema.schema != null && foundschema.schema.discriminators != null) {
|
||||
const discriminators = foundschema.schema.discriminators;
|
||||
const discriminatorKeyPath = trypath + '.' +
|
||||
foundschema.schema.options.discriminatorKey;
|
||||
const keys = subdoc ? mpath.get(discriminatorKeyPath, subdoc) || [] : [];
|
||||
schemas = Object.keys(discriminators).
|
||||
reduce(function(cur, discriminator) {
|
||||
const tiedValue = discriminators[discriminator].discriminatorMapping.value;
|
||||
if (doc == null || keys.indexOf(discriminator) !== -1 || keys.indexOf(tiedValue) !== -1) {
|
||||
cur.push(discriminators[discriminator]);
|
||||
}
|
||||
return cur;
|
||||
}, []);
|
||||
}
|
||||
|
||||
// Now that we found the array, we need to check if there
|
||||
// are remaining document paths to look up for casting.
|
||||
// Also we need to handle array.$.path since schema.path
|
||||
// doesn't work for that.
|
||||
// If there is no foundschema.schema we are dealing with
|
||||
// a path like array.$
|
||||
if (p !== parts.length && foundschema.schema) {
|
||||
let ret;
|
||||
if (parts[p] === '$') {
|
||||
if (p + 1 === parts.length) {
|
||||
// comments.$
|
||||
return foundschema;
|
||||
}
|
||||
// comments.$.comments.$.title
|
||||
ret = search(
|
||||
parts.slice(p + 1),
|
||||
schema,
|
||||
subdoc ? mpath.get(trypath, subdoc) : null,
|
||||
nestedPath.concat(parts.slice(0, p))
|
||||
);
|
||||
if (ret) {
|
||||
ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
|
||||
!foundschema.schema.$isSingleNested;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (schemas != null && schemas.length > 0) {
|
||||
ret = [];
|
||||
for (const schema of schemas) {
|
||||
const _ret = search(
|
||||
parts.slice(p),
|
||||
schema,
|
||||
subdoc ? mpath.get(trypath, subdoc) : null,
|
||||
nestedPath.concat(parts.slice(0, p))
|
||||
);
|
||||
if (_ret != null) {
|
||||
_ret.$isUnderneathDocArray = _ret.$isUnderneathDocArray ||
|
||||
!foundschema.schema.$isSingleNested;
|
||||
if (_ret.$isUnderneathDocArray) {
|
||||
ret.$isUnderneathDocArray = true;
|
||||
}
|
||||
ret.push(_ret);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
} else {
|
||||
ret = search(
|
||||
parts.slice(p),
|
||||
foundschema.schema,
|
||||
subdoc ? mpath.get(trypath, subdoc) : null,
|
||||
nestedPath.concat(parts.slice(0, p))
|
||||
);
|
||||
|
||||
if (ret) {
|
||||
ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
|
||||
!foundschema.schema.$isSingleNested;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
} else if (p !== parts.length &&
|
||||
foundschema.$isMongooseArray &&
|
||||
foundschema.casterConstructor.$isMongooseArray) {
|
||||
// Nested arrays. Drill down to the bottom of the nested array.
|
||||
let type = foundschema;
|
||||
while (type.$isMongooseArray && !type.$isMongooseDocumentArray) {
|
||||
type = type.casterConstructor;
|
||||
}
|
||||
|
||||
const ret = search(
|
||||
parts.slice(p),
|
||||
type.schema,
|
||||
null,
|
||||
nestedPath.concat(parts.slice(0, p))
|
||||
);
|
||||
if (ret != null) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (type.schema.discriminators) {
|
||||
const discriminatorPaths = [];
|
||||
for (const discriminatorName of Object.keys(type.schema.discriminators)) {
|
||||
const _schema = type.schema.discriminators[discriminatorName] || type.schema;
|
||||
const ret = search(parts.slice(p), _schema, null, nestedPath.concat(parts.slice(0, p)));
|
||||
if (ret != null) {
|
||||
discriminatorPaths.push(ret);
|
||||
}
|
||||
}
|
||||
if (discriminatorPaths.length > 0) {
|
||||
return discriminatorPaths;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fullPath = nestedPath.concat([trypath]).join('.');
|
||||
if (topLevelDoc != null && topLevelDoc.$__ && topLevelDoc.populated(fullPath) && p < parts.length) {
|
||||
const model = doc.$__.populated[fullPath].options[populateModelSymbol];
|
||||
if (model != null) {
|
||||
const ret = search(
|
||||
parts.slice(p),
|
||||
model.schema,
|
||||
subdoc ? mpath.get(trypath, subdoc) : null,
|
||||
nestedPath.concat(parts.slice(0, p))
|
||||
);
|
||||
|
||||
if (ret) {
|
||||
ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
|
||||
!model.schema.$isSingleNested;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
const _val = get(topLevelDoc, trypath);
|
||||
if (_val != null) {
|
||||
const model = Array.isArray(_val) && _val.length > 0 ?
|
||||
leanPopulateMap.get(_val[0]) :
|
||||
leanPopulateMap.get(_val);
|
||||
// Populated using lean, `leanPopulateMap` value is the foreign model
|
||||
const schema = model != null ? model.schema : null;
|
||||
if (schema != null) {
|
||||
const ret = search(
|
||||
parts.slice(p),
|
||||
schema,
|
||||
subdoc ? mpath.get(trypath, subdoc) : null,
|
||||
nestedPath.concat(parts.slice(0, p))
|
||||
);
|
||||
|
||||
if (ret != null) {
|
||||
ret.$isUnderneathDocArray = ret.$isUnderneathDocArray ||
|
||||
!schema.$isSingleNested;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
return foundschema;
|
||||
}
|
||||
}
|
||||
// look for arrays
|
||||
const parts = path.split('.');
|
||||
for (let i = 0; i < parts.length; ++i) {
|
||||
if (parts[i] === '$') {
|
||||
// Re: gh-5628, because `schema.path()` doesn't take $ into account.
|
||||
parts[i] = '0';
|
||||
}
|
||||
}
|
||||
return search(parts, schema, doc, []);
|
||||
};
|
||||
72
node_modules/mongoose/lib/helpers/populate/getVirtual.js
generated
vendored
Normal file
72
node_modules/mongoose/lib/helpers/populate/getVirtual.js
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = getVirtual;
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
function getVirtual(schema, name) {
|
||||
if (schema.virtuals[name]) {
|
||||
return { virtual: schema.virtuals[name], path: void 0 };
|
||||
}
|
||||
|
||||
const parts = name.split('.');
|
||||
let cur = '';
|
||||
let nestedSchemaPath = '';
|
||||
for (let i = 0; i < parts.length; ++i) {
|
||||
cur += (cur.length > 0 ? '.' : '') + parts[i];
|
||||
if (schema.virtuals[cur]) {
|
||||
if (i === parts.length - 1) {
|
||||
return { virtual: schema.virtuals[cur], path: nestedSchemaPath };
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (schema.nested[cur]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (schema.paths[cur] && schema.paths[cur].schema) {
|
||||
schema = schema.paths[cur].schema;
|
||||
const rest = parts.slice(i + 1).join('.');
|
||||
|
||||
if (schema.virtuals[rest]) {
|
||||
if (i === parts.length - 2) {
|
||||
return {
|
||||
virtual: schema.virtuals[rest],
|
||||
nestedSchemaPath: [nestedSchemaPath, cur].filter(v => !!v).join('.')
|
||||
};
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i + 1 < parts.length && schema.discriminators) {
|
||||
for (const key of Object.keys(schema.discriminators)) {
|
||||
const res = getVirtual(schema.discriminators[key], rest);
|
||||
if (res != null) {
|
||||
const _path = [nestedSchemaPath, cur, res.nestedSchemaPath].
|
||||
filter(v => !!v).join('.');
|
||||
return {
|
||||
virtual: res.virtual,
|
||||
nestedSchemaPath: _path
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nestedSchemaPath += (nestedSchemaPath.length > 0 ? '.' : '') + cur;
|
||||
cur = '';
|
||||
continue;
|
||||
}
|
||||
|
||||
if (schema.discriminators) {
|
||||
for (const discriminatorKey of Object.keys(schema.discriminators)) {
|
||||
const virtualFromDiscriminator = getVirtual(schema.discriminators[discriminatorKey], name);
|
||||
if (virtualFromDiscriminator) return virtualFromDiscriminator;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
7
node_modules/mongoose/lib/helpers/populate/leanPopulateMap.js
generated
vendored
Normal file
7
node_modules/mongoose/lib/helpers/populate/leanPopulateMap.js
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
module.exports = new WeakMap();
|
||||
28
node_modules/mongoose/lib/helpers/populate/lookupLocalFields.js
generated
vendored
Normal file
28
node_modules/mongoose/lib/helpers/populate/lookupLocalFields.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function lookupLocalFields(cur, path, val) {
|
||||
if (cur == null) {
|
||||
return cur;
|
||||
}
|
||||
|
||||
if (cur._doc != null) {
|
||||
cur = cur._doc;
|
||||
}
|
||||
|
||||
if (arguments.length >= 3) {
|
||||
if (typeof cur !== 'object') {
|
||||
return void 0;
|
||||
}
|
||||
cur[path] = val;
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
// Support populating paths under maps using `map.$*.subpath`
|
||||
if (path === '$*') {
|
||||
return cur instanceof Map ?
|
||||
Array.from(cur.values()) :
|
||||
Object.keys(cur).map(key => cur[key]);
|
||||
}
|
||||
return cur[path];
|
||||
};
|
||||
45
node_modules/mongoose/lib/helpers/populate/normalizeRefPath.js
generated
vendored
Normal file
45
node_modules/mongoose/lib/helpers/populate/normalizeRefPath.js
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function normalizeRefPath(refPath, doc, populatedPath) {
|
||||
if (refPath == null) {
|
||||
return refPath;
|
||||
}
|
||||
|
||||
if (typeof refPath === 'function') {
|
||||
refPath = refPath.call(doc, doc, populatedPath);
|
||||
}
|
||||
|
||||
// If populated path has numerics, the end `refPath` should too. For example,
|
||||
// if populating `a.0.b` instead of `a.b` and `b` has `refPath = a.c`, we
|
||||
// should return `a.0.c` for the refPath.
|
||||
const hasNumericProp = /(\.\d+$|\.\d+\.)/g;
|
||||
|
||||
if (hasNumericProp.test(populatedPath)) {
|
||||
const chunks = populatedPath.split(hasNumericProp);
|
||||
|
||||
if (chunks[chunks.length - 1] === '') {
|
||||
throw new Error('Can\'t populate individual element in an array');
|
||||
}
|
||||
|
||||
let _refPath = '';
|
||||
let _remaining = refPath;
|
||||
// 2nd, 4th, etc. will be numeric props. For example: `[ 'a', '.0.', 'b' ]`
|
||||
for (let i = 0; i < chunks.length; i += 2) {
|
||||
const chunk = chunks[i];
|
||||
if (_remaining.startsWith(chunk + '.')) {
|
||||
_refPath += _remaining.substr(0, chunk.length) + chunks[i + 1];
|
||||
_remaining = _remaining.substr(chunk.length + 1);
|
||||
} else if (i === chunks.length - 1) {
|
||||
_refPath += _remaining;
|
||||
_remaining = '';
|
||||
break;
|
||||
} else {
|
||||
throw new Error('Could not normalize ref path, chunk ' + chunk + ' not in populated path');
|
||||
}
|
||||
}
|
||||
|
||||
return _refPath;
|
||||
}
|
||||
|
||||
return refPath;
|
||||
};
|
||||
31
node_modules/mongoose/lib/helpers/populate/removeDeselectedForeignField.js
generated
vendored
Normal file
31
node_modules/mongoose/lib/helpers/populate/removeDeselectedForeignField.js
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
'use strict';
|
||||
|
||||
const get = require('../get');
|
||||
const mpath = require('mpath');
|
||||
const parseProjection = require('../projection/parseProjection');
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
module.exports = function removeDeselectedForeignField(foreignFields, options, docs) {
|
||||
const projection = parseProjection(get(options, 'select', null), true) ||
|
||||
parseProjection(get(options, 'options.select', null), true);
|
||||
|
||||
if (projection == null) {
|
||||
return;
|
||||
}
|
||||
for (const foreignField of foreignFields) {
|
||||
if (!projection.hasOwnProperty('-' + foreignField)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const val of docs) {
|
||||
if (val.$__ != null) {
|
||||
mpath.unset(foreignField, val._doc);
|
||||
} else {
|
||||
mpath.unset(foreignField, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
19
node_modules/mongoose/lib/helpers/populate/validateRef.js
generated
vendored
Normal file
19
node_modules/mongoose/lib/helpers/populate/validateRef.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
const MongooseError = require('../../error/mongooseError');
|
||||
const util = require('util');
|
||||
|
||||
module.exports = validateRef;
|
||||
|
||||
function validateRef(ref, path) {
|
||||
if (typeof ref === 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof ref === 'function') {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new MongooseError('Invalid ref at path "' + path + '". Got ' +
|
||||
util.inspect(ref, { depth: 0 }));
|
||||
}
|
||||
15
node_modules/mongoose/lib/helpers/printJestWarning.js
generated
vendored
Normal file
15
node_modules/mongoose/lib/helpers/printJestWarning.js
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
if (typeof jest !== 'undefined' && typeof window !== 'undefined') {
|
||||
console.warn('Mongoose: looks like you\'re trying to test a Mongoose app ' +
|
||||
'with Jest\'s default jsdom test environment. Please make sure you read ' +
|
||||
'Mongoose\'s docs on configuring Jest to test Node.js apps: ' +
|
||||
'http://mongoosejs.com/docs/jest.html');
|
||||
}
|
||||
|
||||
if (typeof jest !== 'undefined' && process.nextTick.toString().indexOf('nextTick') === -1) {
|
||||
console.warn('Mongoose: looks like you\'re trying to test a Mongoose app ' +
|
||||
'with Jest\'s mock timers enabled. Please make sure you read ' +
|
||||
'Mongoose\'s docs on configuring Jest to test Node.js apps: ' +
|
||||
'http://mongoosejs.com/docs/jest.html');
|
||||
}
|
||||
18
node_modules/mongoose/lib/helpers/projection/isDefiningProjection.js
generated
vendored
Normal file
18
node_modules/mongoose/lib/helpers/projection/isDefiningProjection.js
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
'use strict';
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
module.exports = function isDefiningProjection(val) {
|
||||
if (val == null) {
|
||||
// `undefined` or `null` become exclusive projections
|
||||
return true;
|
||||
}
|
||||
if (typeof val === 'object') {
|
||||
// Only cases where a value does **not** define whether the whole projection
|
||||
// is inclusive or exclusive are `$meta` and `$slice`.
|
||||
return !('$meta' in val) && !('$slice' in val);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
32
node_modules/mongoose/lib/helpers/projection/isExclusive.js
generated
vendored
Normal file
32
node_modules/mongoose/lib/helpers/projection/isExclusive.js
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
'use strict';
|
||||
|
||||
const isDefiningProjection = require('./isDefiningProjection');
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
module.exports = function isExclusive(projection) {
|
||||
if (projection == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const keys = Object.keys(projection);
|
||||
let ki = keys.length;
|
||||
let exclude = null;
|
||||
|
||||
if (ki === 1 && keys[0] === '_id') {
|
||||
exclude = !!projection[keys[ki]];
|
||||
} else {
|
||||
while (ki--) {
|
||||
// Does this projection explicitly define inclusion/exclusion?
|
||||
// Explicitly avoid `$meta` and `$slice`
|
||||
if (keys[ki] !== '_id' && isDefiningProjection(projection[keys[ki]])) {
|
||||
exclude = !projection[keys[ki]];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return exclude;
|
||||
};
|
||||
34
node_modules/mongoose/lib/helpers/projection/isInclusive.js
generated
vendored
Normal file
34
node_modules/mongoose/lib/helpers/projection/isInclusive.js
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
'use strict';
|
||||
|
||||
const isDefiningProjection = require('./isDefiningProjection');
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
module.exports = function isInclusive(projection) {
|
||||
if (projection == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const props = Object.keys(projection);
|
||||
const numProps = props.length;
|
||||
if (numProps === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < numProps; ++i) {
|
||||
const prop = props[i];
|
||||
// Plus paths can't define the projection (see gh-7050)
|
||||
if (prop.startsWith('+')) {
|
||||
continue;
|
||||
}
|
||||
// If field is truthy (1, true, etc.) and not an object, then this
|
||||
// projection must be inclusive. If object, assume its $meta, $slice, etc.
|
||||
if (isDefiningProjection(projection[prop]) && !!projection[prop]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
35
node_modules/mongoose/lib/helpers/projection/isPathExcluded.js
generated
vendored
Normal file
35
node_modules/mongoose/lib/helpers/projection/isPathExcluded.js
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
|
||||
const isDefiningProjection = require('./isDefiningProjection');
|
||||
|
||||
/*!
|
||||
* Determines if `path` is excluded by `projection`
|
||||
*
|
||||
* @param {Object} projection
|
||||
* @param {string} path
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
module.exports = function isPathExcluded(projection, path) {
|
||||
if (path === '_id') {
|
||||
return projection._id === 0;
|
||||
}
|
||||
|
||||
const paths = Object.keys(projection);
|
||||
let type = null;
|
||||
|
||||
for (const _path of paths) {
|
||||
if (isDefiningProjection(projection[_path])) {
|
||||
type = projection[path] === 1 ? 'inclusive' : 'exclusive';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (type === 'inclusive') {
|
||||
return projection[path] !== 1;
|
||||
}
|
||||
if (type === 'exclusive') {
|
||||
return projection[path] === 0;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
28
node_modules/mongoose/lib/helpers/projection/isPathSelectedInclusive.js
generated
vendored
Normal file
28
node_modules/mongoose/lib/helpers/projection/isPathSelectedInclusive.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
'use strict';
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
module.exports = function isPathSelectedInclusive(fields, path) {
|
||||
const chunks = path.split('.');
|
||||
let cur = '';
|
||||
let j;
|
||||
let keys;
|
||||
let numKeys;
|
||||
for (let i = 0; i < chunks.length; ++i) {
|
||||
cur += cur.length ? '.' : '' + chunks[i];
|
||||
if (fields[cur]) {
|
||||
keys = Object.keys(fields);
|
||||
numKeys = keys.length;
|
||||
for (j = 0; j < numKeys; ++j) {
|
||||
if (keys[i].indexOf(cur + '.') === 0 && keys[i].indexOf(path) !== 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
33
node_modules/mongoose/lib/helpers/projection/parseProjection.js
generated
vendored
Normal file
33
node_modules/mongoose/lib/helpers/projection/parseProjection.js
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Convert a string or array into a projection object, retaining all
|
||||
* `-` and `+` paths.
|
||||
*/
|
||||
|
||||
module.exports = function parseProjection(v, retainMinusPaths) {
|
||||
const type = typeof v;
|
||||
|
||||
if (type === 'string') {
|
||||
v = v.split(/\s+/);
|
||||
}
|
||||
if (!Array.isArray(v) && Object.prototype.toString.call(v) !== '[object Arguments]') {
|
||||
return v;
|
||||
}
|
||||
|
||||
const len = v.length;
|
||||
const ret = {};
|
||||
for (let i = 0; i < len; ++i) {
|
||||
let field = v[i];
|
||||
if (!field) {
|
||||
continue;
|
||||
}
|
||||
const include = '-' == field[0] ? 0 : 1;
|
||||
if (!retainMinusPaths && include === 0) {
|
||||
field = field.substring(1);
|
||||
}
|
||||
ret[field] = include;
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
46
node_modules/mongoose/lib/helpers/promiseOrCallback.js
generated
vendored
Normal file
46
node_modules/mongoose/lib/helpers/promiseOrCallback.js
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
'use strict';
|
||||
|
||||
const PromiseProvider = require('../promise_provider');
|
||||
const immediate = require('./immediate');
|
||||
|
||||
const emittedSymbol = Symbol('mongoose:emitted');
|
||||
|
||||
module.exports = function promiseOrCallback(callback, fn, ee, Promise) {
|
||||
if (typeof callback === 'function') {
|
||||
return fn(function(error) {
|
||||
if (error != null) {
|
||||
if (ee != null && ee.listeners != null && ee.listeners('error').length > 0 && !error[emittedSymbol]) {
|
||||
error[emittedSymbol] = true;
|
||||
ee.emit('error', error);
|
||||
}
|
||||
try {
|
||||
callback(error);
|
||||
} catch (error) {
|
||||
return immediate(() => {
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
callback.apply(this, arguments);
|
||||
});
|
||||
}
|
||||
|
||||
Promise = Promise || PromiseProvider.get();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
fn(function(error, res) {
|
||||
if (error != null) {
|
||||
if (ee != null && ee.listeners != null && ee.listeners('error').length > 0 && !error[emittedSymbol]) {
|
||||
error[emittedSymbol] = true;
|
||||
ee.emit('error', error);
|
||||
}
|
||||
return reject(error);
|
||||
}
|
||||
if (arguments.length > 2) {
|
||||
return resolve(Array.prototype.slice.call(arguments, 1));
|
||||
}
|
||||
resolve(res);
|
||||
});
|
||||
});
|
||||
};
|
||||
15
node_modules/mongoose/lib/helpers/query/applyGlobalMaxTimeMS.js
generated
vendored
Normal file
15
node_modules/mongoose/lib/helpers/query/applyGlobalMaxTimeMS.js
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
const utils = require('../../utils');
|
||||
|
||||
module.exports = function applyGlobalMaxTimeMS(options, model) {
|
||||
if (utils.hasUserDefinedProperty(options, 'maxTimeMS')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (utils.hasUserDefinedProperty(model.db.options, 'maxTimeMS')) {
|
||||
options.maxTimeMS = model.db.options.maxTimeMS;
|
||||
} else if (utils.hasUserDefinedProperty(model.base.options, 'maxTimeMS')) {
|
||||
options.maxTimeMS = model.base.options.maxTimeMS;
|
||||
}
|
||||
};
|
||||
93
node_modules/mongoose/lib/helpers/query/applyQueryMiddleware.js
generated
vendored
Normal file
93
node_modules/mongoose/lib/helpers/query/applyQueryMiddleware.js
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
'use strict';
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
module.exports = applyQueryMiddleware;
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
applyQueryMiddleware.middlewareFunctions = [
|
||||
'count',
|
||||
'countDocuments',
|
||||
'deleteMany',
|
||||
'deleteOne',
|
||||
'distinct',
|
||||
'estimatedDocumentCount',
|
||||
'find',
|
||||
'findOne',
|
||||
'findOneAndDelete',
|
||||
'findOneAndRemove',
|
||||
'findOneAndReplace',
|
||||
'findOneAndUpdate',
|
||||
'remove',
|
||||
'replaceOne',
|
||||
'update',
|
||||
'updateMany',
|
||||
'updateOne',
|
||||
'validate'
|
||||
];
|
||||
|
||||
/*!
|
||||
* Apply query middleware
|
||||
*
|
||||
* @param {Query} query constructor
|
||||
* @param {Model} model
|
||||
*/
|
||||
|
||||
function applyQueryMiddleware(Query, model) {
|
||||
const kareemOptions = {
|
||||
useErrorHandlers: true,
|
||||
numCallbackParams: 1,
|
||||
nullResultByDefault: true
|
||||
};
|
||||
|
||||
const middleware = model.hooks.filter(hook => {
|
||||
const contexts = _getContexts(hook);
|
||||
if (hook.name === 'updateOne') {
|
||||
return contexts.query == null || !!contexts.query;
|
||||
}
|
||||
if (hook.name === 'deleteOne') {
|
||||
return !!contexts.query || Object.keys(contexts).length === 0;
|
||||
}
|
||||
if (hook.name === 'validate' || hook.name === 'remove') {
|
||||
return !!contexts.query;
|
||||
}
|
||||
if (hook.query != null || hook.document != null) {
|
||||
return !!hook.query;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// `update()` thunk has a different name because `_update` was already taken
|
||||
Query.prototype._execUpdate = middleware.createWrapper('update',
|
||||
Query.prototype._execUpdate, null, kareemOptions);
|
||||
// `distinct()` thunk has a different name because `_distinct` was already taken
|
||||
Query.prototype.__distinct = middleware.createWrapper('distinct',
|
||||
Query.prototype.__distinct, null, kareemOptions);
|
||||
|
||||
// `validate()` doesn't have a thunk because it doesn't execute a query.
|
||||
Query.prototype.validate = middleware.createWrapper('validate',
|
||||
Query.prototype.validate, null, kareemOptions);
|
||||
|
||||
applyQueryMiddleware.middlewareFunctions.
|
||||
filter(v => v !== 'update' && v !== 'distinct' && v !== 'validate').
|
||||
forEach(fn => {
|
||||
Query.prototype[`_${fn}`] = middleware.createWrapper(fn,
|
||||
Query.prototype[`_${fn}`], null, kareemOptions);
|
||||
});
|
||||
}
|
||||
|
||||
function _getContexts(hook) {
|
||||
const ret = {};
|
||||
if (hook.hasOwnProperty('query')) {
|
||||
ret.query = hook.query;
|
||||
}
|
||||
if (hook.hasOwnProperty('document')) {
|
||||
ret.document = hook.document;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
55
node_modules/mongoose/lib/helpers/query/castFilterPath.js
generated
vendored
Normal file
55
node_modules/mongoose/lib/helpers/query/castFilterPath.js
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
'use strict';
|
||||
|
||||
const isOperator = require('./isOperator');
|
||||
|
||||
module.exports = function castFilterPath(query, schematype, val) {
|
||||
const ctx = query;
|
||||
const any$conditionals = Object.keys(val).some(isOperator);
|
||||
|
||||
if (!any$conditionals) {
|
||||
return schematype.castForQueryWrapper({
|
||||
val: val,
|
||||
context: ctx
|
||||
});
|
||||
}
|
||||
|
||||
const ks = Object.keys(val);
|
||||
|
||||
let k = ks.length;
|
||||
|
||||
while (k--) {
|
||||
const $cond = ks[k];
|
||||
const nested = val[$cond];
|
||||
|
||||
if ($cond === '$not') {
|
||||
if (nested && schematype && !schematype.caster) {
|
||||
const _keys = Object.keys(nested);
|
||||
if (_keys.length && isOperator(_keys[0])) {
|
||||
for (const key of Object.keys(nested)) {
|
||||
nested[key] = schematype.castForQueryWrapper({
|
||||
$conditional: key,
|
||||
val: nested[key],
|
||||
context: ctx
|
||||
});
|
||||
}
|
||||
} else {
|
||||
val[$cond] = schematype.castForQueryWrapper({
|
||||
$conditional: $cond,
|
||||
val: nested,
|
||||
context: ctx
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// cast(schematype.caster ? schematype.caster.schema : schema, nested, options, context);
|
||||
} else {
|
||||
val[$cond] = schematype.castForQueryWrapper({
|
||||
$conditional: $cond,
|
||||
val: nested,
|
||||
context: ctx
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
};
|
||||
554
node_modules/mongoose/lib/helpers/query/castUpdate.js
generated
vendored
Normal file
554
node_modules/mongoose/lib/helpers/query/castUpdate.js
generated
vendored
Normal file
@@ -0,0 +1,554 @@
|
||||
'use strict';
|
||||
|
||||
const CastError = require('../../error/cast');
|
||||
const MongooseError = require('../../error/mongooseError');
|
||||
const StrictModeError = require('../../error/strict');
|
||||
const ValidationError = require('../../error/validation');
|
||||
const castNumber = require('../../cast/number');
|
||||
const cast = require('../../cast');
|
||||
const getEmbeddedDiscriminatorPath = require('./getEmbeddedDiscriminatorPath');
|
||||
const handleImmutable = require('./handleImmutable');
|
||||
const moveImmutableProperties = require('../update/moveImmutableProperties');
|
||||
const schemaMixedSymbol = require('../../schema/symbols').schemaMixedSymbol;
|
||||
const setDottedPath = require('../path/setDottedPath');
|
||||
const utils = require('../../utils');
|
||||
|
||||
/*!
|
||||
* Casts an update op based on the given schema
|
||||
*
|
||||
* @param {Schema} schema
|
||||
* @param {Object} obj
|
||||
* @param {Object} options
|
||||
* @param {Boolean} [options.overwrite] defaults to false
|
||||
* @param {Boolean|String} [options.strict] defaults to true
|
||||
* @param {Query} context passed to setters
|
||||
* @return {Boolean} true iff the update is non-empty
|
||||
*/
|
||||
|
||||
module.exports = function castUpdate(schema, obj, options, context, filter) {
|
||||
if (obj == null) {
|
||||
return undefined;
|
||||
}
|
||||
options = options || {};
|
||||
|
||||
// Update pipeline
|
||||
if (Array.isArray(obj)) {
|
||||
const len = obj.length;
|
||||
for (let i = 0; i < len; ++i) {
|
||||
const ops = Object.keys(obj[i]);
|
||||
for (const op of ops) {
|
||||
obj[i][op] = castPipelineOperator(op, obj[i][op]);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (options.upsert) {
|
||||
moveImmutableProperties(schema, obj, context);
|
||||
}
|
||||
|
||||
const ops = Object.keys(obj);
|
||||
let i = ops.length;
|
||||
const ret = {};
|
||||
let val;
|
||||
let hasDollarKey = false;
|
||||
const overwrite = options.overwrite;
|
||||
|
||||
filter = filter || {};
|
||||
|
||||
while (i--) {
|
||||
const op = ops[i];
|
||||
// if overwrite is set, don't do any of the special $set stuff
|
||||
if (op[0] !== '$' && !overwrite) {
|
||||
// fix up $set sugar
|
||||
if (!ret.$set) {
|
||||
if (obj.$set) {
|
||||
ret.$set = obj.$set;
|
||||
} else {
|
||||
ret.$set = {};
|
||||
}
|
||||
}
|
||||
ret.$set[op] = obj[op];
|
||||
ops.splice(i, 1);
|
||||
if (!~ops.indexOf('$set')) ops.push('$set');
|
||||
} else if (op === '$set') {
|
||||
if (!ret.$set) {
|
||||
ret[op] = obj[op];
|
||||
}
|
||||
} else {
|
||||
ret[op] = obj[op];
|
||||
}
|
||||
}
|
||||
|
||||
// cast each value
|
||||
i = ops.length;
|
||||
|
||||
while (i--) {
|
||||
const op = ops[i];
|
||||
val = ret[op];
|
||||
hasDollarKey = hasDollarKey || op.startsWith('$');
|
||||
|
||||
if (val &&
|
||||
typeof val === 'object' &&
|
||||
!Buffer.isBuffer(val) &&
|
||||
(!overwrite || hasDollarKey)) {
|
||||
walkUpdatePath(schema, val, op, options, context, filter);
|
||||
} else if (overwrite && ret && typeof ret === 'object') {
|
||||
walkUpdatePath(schema, ret, '$set', options, context, filter);
|
||||
} else {
|
||||
const msg = 'Invalid atomic update value for ' + op + '. '
|
||||
+ 'Expected an object, received ' + typeof val;
|
||||
throw new Error(msg);
|
||||
}
|
||||
|
||||
if (op.startsWith('$') && utils.isEmptyObject(val)) {
|
||||
delete ret[op];
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(ret).length === 0 &&
|
||||
options.upsert &&
|
||||
Object.keys(filter).length > 0) {
|
||||
// Trick the driver into allowing empty upserts to work around
|
||||
// https://github.com/mongodb/node-mongodb-native/pull/2490
|
||||
return { $setOnInsert: filter };
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
function castPipelineOperator(op, val) {
|
||||
if (op === '$unset') {
|
||||
if (!Array.isArray(val) || val.find(v => typeof v !== 'string')) {
|
||||
throw new MongooseError('Invalid $unset in pipeline, must be ' +
|
||||
'an array of strings');
|
||||
}
|
||||
return val;
|
||||
}
|
||||
if (op === '$project') {
|
||||
if (val == null || typeof val !== 'object') {
|
||||
throw new MongooseError('Invalid $project in pipeline, must be an object');
|
||||
}
|
||||
return val;
|
||||
}
|
||||
if (op === '$addFields' || op === '$set') {
|
||||
if (val == null || typeof val !== 'object') {
|
||||
throw new MongooseError('Invalid ' + op + ' in pipeline, must be an object');
|
||||
}
|
||||
return val;
|
||||
} else if (op === '$replaceRoot' || op === '$replaceWith') {
|
||||
if (val == null || typeof val !== 'object') {
|
||||
throw new MongooseError('Invalid ' + op + ' in pipeline, must be an object');
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
throw new MongooseError('Invalid update pipeline operator: "' + op + '"');
|
||||
}
|
||||
|
||||
/*!
|
||||
* Walk each path of obj and cast its values
|
||||
* according to its schema.
|
||||
*
|
||||
* @param {Schema} schema
|
||||
* @param {Object} obj - part of a query
|
||||
* @param {String} op - the atomic operator ($pull, $set, etc)
|
||||
* @param {Object} options
|
||||
* @param {Boolean|String} [options.strict]
|
||||
* @param {Boolean} [options.omitUndefined]
|
||||
* @param {Query} context
|
||||
* @param {String} pref - path prefix (internal only)
|
||||
* @return {Bool} true if this path has keys to update
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function walkUpdatePath(schema, obj, op, options, context, filter, pref) {
|
||||
const strict = options.strict;
|
||||
const prefix = pref ? pref + '.' : '';
|
||||
const keys = Object.keys(obj);
|
||||
let i = keys.length;
|
||||
let hasKeys = false;
|
||||
let schematype;
|
||||
let key;
|
||||
let val;
|
||||
|
||||
let aggregatedError = null;
|
||||
|
||||
let useNestedStrict;
|
||||
if (options.useNestedStrict === undefined) {
|
||||
useNestedStrict = schema.options.useNestedStrict;
|
||||
} else {
|
||||
useNestedStrict = options.useNestedStrict;
|
||||
}
|
||||
|
||||
while (i--) {
|
||||
key = keys[i];
|
||||
val = obj[key];
|
||||
|
||||
// `$pull` is special because we need to cast the RHS as a query, not as
|
||||
// an update.
|
||||
if (op === '$pull') {
|
||||
schematype = schema._getSchema(prefix + key);
|
||||
if (schematype != null && schematype.schema != null) {
|
||||
obj[key] = cast(schematype.schema, obj[key], options, context);
|
||||
hasKeys = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (val && val.constructor.name === 'Object') {
|
||||
// watch for embedded doc schemas
|
||||
schematype = schema._getSchema(prefix + key);
|
||||
|
||||
if (schematype == null) {
|
||||
const _res = getEmbeddedDiscriminatorPath(schema, obj, filter, prefix + key, options);
|
||||
if (_res.schematype != null) {
|
||||
schematype = _res.schematype;
|
||||
}
|
||||
}
|
||||
|
||||
if (op !== '$setOnInsert' &&
|
||||
handleImmutable(schematype, strict, obj, key, prefix + key, context)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (schematype && schematype.caster && op in castOps) {
|
||||
// embedded doc schema
|
||||
if ('$each' in val) {
|
||||
hasKeys = true;
|
||||
try {
|
||||
obj[key] = {
|
||||
$each: castUpdateVal(schematype, val.$each, op, key, context, prefix + key)
|
||||
};
|
||||
} catch (error) {
|
||||
aggregatedError = _handleCastError(error, context, key, aggregatedError);
|
||||
}
|
||||
|
||||
if (val.$slice != null) {
|
||||
obj[key].$slice = val.$slice | 0;
|
||||
}
|
||||
|
||||
if (val.$sort) {
|
||||
obj[key].$sort = val.$sort;
|
||||
}
|
||||
|
||||
if (val.$position != null) {
|
||||
obj[key].$position = castNumber(val.$position);
|
||||
}
|
||||
} else {
|
||||
if (schematype != null && schematype.$isSingleNested) {
|
||||
// Special case to make sure `strict` bubbles down correctly to
|
||||
// single nested re: gh-8735
|
||||
let _strict = strict;
|
||||
if (useNestedStrict !== false && schematype.schema.options.hasOwnProperty('strict')) {
|
||||
_strict = schematype.schema.options.strict;
|
||||
} else if (useNestedStrict === false) {
|
||||
_strict = schema.options.strict;
|
||||
}
|
||||
try {
|
||||
obj[key] = schematype.castForQuery(val, context, { strict: _strict });
|
||||
} catch (error) {
|
||||
aggregatedError = _handleCastError(error, context, key, aggregatedError);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
obj[key] = castUpdateVal(schematype, val, op, key, context, prefix + key);
|
||||
} catch (error) {
|
||||
aggregatedError = _handleCastError(error, context, key, aggregatedError);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.omitUndefined && obj[key] === void 0) {
|
||||
delete obj[key];
|
||||
continue;
|
||||
}
|
||||
|
||||
hasKeys = true;
|
||||
}
|
||||
} else if ((op === '$currentDate') || (op in castOps && schematype)) {
|
||||
// $currentDate can take an object
|
||||
try {
|
||||
obj[key] = castUpdateVal(schematype, val, op, key, context, prefix + key);
|
||||
} catch (error) {
|
||||
aggregatedError = _handleCastError(error, context, key, aggregatedError);
|
||||
}
|
||||
|
||||
if (options.omitUndefined && obj[key] === void 0) {
|
||||
delete obj[key];
|
||||
continue;
|
||||
}
|
||||
|
||||
hasKeys = true;
|
||||
} else {
|
||||
const pathToCheck = (prefix + key);
|
||||
const v = schema._getPathType(pathToCheck);
|
||||
let _strict = strict;
|
||||
if (useNestedStrict &&
|
||||
v &&
|
||||
v.schema &&
|
||||
'strict' in v.schema.options) {
|
||||
_strict = v.schema.options.strict;
|
||||
}
|
||||
|
||||
if (v.pathType === 'undefined') {
|
||||
if (_strict === 'throw') {
|
||||
throw new StrictModeError(pathToCheck);
|
||||
} else if (_strict) {
|
||||
delete obj[key];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// gh-2314
|
||||
// we should be able to set a schema-less field
|
||||
// to an empty object literal
|
||||
hasKeys |= walkUpdatePath(schema, val, op, options, context, filter, prefix + key) ||
|
||||
(utils.isObject(val) && Object.keys(val).length === 0);
|
||||
}
|
||||
} else {
|
||||
const checkPath = (key === '$each' || key === '$or' || key === '$and' || key === '$in') ?
|
||||
pref : prefix + key;
|
||||
schematype = schema._getSchema(checkPath);
|
||||
|
||||
// You can use `$setOnInsert` with immutable keys
|
||||
if (op !== '$setOnInsert' &&
|
||||
handleImmutable(schematype, strict, obj, key, prefix + key, context)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let pathDetails = schema._getPathType(checkPath);
|
||||
|
||||
// If no schema type, check for embedded discriminators because the
|
||||
// filter or update may imply an embedded discriminator type. See #8378
|
||||
if (schematype == null) {
|
||||
const _res = getEmbeddedDiscriminatorPath(schema, obj, filter, checkPath, options);
|
||||
if (_res.schematype != null) {
|
||||
schematype = _res.schematype;
|
||||
pathDetails = _res.type;
|
||||
}
|
||||
}
|
||||
|
||||
let isStrict = strict;
|
||||
if (useNestedStrict &&
|
||||
pathDetails &&
|
||||
pathDetails.schema &&
|
||||
'strict' in pathDetails.schema.options) {
|
||||
isStrict = pathDetails.schema.options.strict;
|
||||
}
|
||||
|
||||
const skip = isStrict &&
|
||||
!schematype &&
|
||||
!/real|nested/.test(pathDetails.pathType);
|
||||
|
||||
if (skip) {
|
||||
// Even if strict is `throw`, avoid throwing an error because of
|
||||
// virtuals because of #6731
|
||||
if (isStrict === 'throw' && schema.virtuals[checkPath] == null) {
|
||||
throw new StrictModeError(prefix + key);
|
||||
} else {
|
||||
delete obj[key];
|
||||
}
|
||||
} else {
|
||||
// gh-1845 temporary fix: ignore $rename. See gh-3027 for tracking
|
||||
// improving this.
|
||||
if (op === '$rename') {
|
||||
hasKeys = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
if (prefix.length === 0 || key.indexOf('.') === -1) {
|
||||
obj[key] = castUpdateVal(schematype, val, op, key, context, prefix + key);
|
||||
} else {
|
||||
// Setting a nested dotted path that's in the schema. We don't allow paths with '.' in
|
||||
// a schema, so replace the dotted path with a nested object to avoid ending up with
|
||||
// dotted properties in the updated object. See (gh-10200)
|
||||
setDottedPath(obj, key, castUpdateVal(schematype, val, op, key, context, prefix + key));
|
||||
delete obj[key];
|
||||
}
|
||||
} catch (error) {
|
||||
aggregatedError = _handleCastError(error, context, key, aggregatedError);
|
||||
}
|
||||
|
||||
if (Array.isArray(obj[key]) && (op === '$addToSet' || op === '$push') && key !== '$each') {
|
||||
if (schematype && schematype.caster && !schematype.caster.$isMongooseArray) {
|
||||
obj[key] = { $each: obj[key] };
|
||||
}
|
||||
}
|
||||
|
||||
if (options.omitUndefined && obj[key] === void 0) {
|
||||
delete obj[key];
|
||||
continue;
|
||||
}
|
||||
|
||||
hasKeys = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aggregatedError != null) {
|
||||
throw aggregatedError;
|
||||
}
|
||||
|
||||
return hasKeys;
|
||||
}
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
function _handleCastError(error, query, key, aggregatedError) {
|
||||
if (typeof query !== 'object' || !query.options.multipleCastError) {
|
||||
throw error;
|
||||
}
|
||||
aggregatedError = aggregatedError || new ValidationError();
|
||||
aggregatedError.addError(key, error);
|
||||
return aggregatedError;
|
||||
}
|
||||
|
||||
/*!
|
||||
* These operators should be cast to numbers instead
|
||||
* of their path schema type.
|
||||
*/
|
||||
|
||||
const numberOps = {
|
||||
$pop: 1,
|
||||
$inc: 1
|
||||
};
|
||||
|
||||
/*!
|
||||
* These ops require no casting because the RHS doesn't do anything.
|
||||
*/
|
||||
|
||||
const noCastOps = {
|
||||
$unset: 1
|
||||
};
|
||||
|
||||
/*!
|
||||
* These operators require casting docs
|
||||
* to real Documents for Update operations.
|
||||
*/
|
||||
|
||||
const castOps = {
|
||||
$push: 1,
|
||||
$addToSet: 1,
|
||||
$set: 1,
|
||||
$setOnInsert: 1
|
||||
};
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
const overwriteOps = {
|
||||
$set: 1,
|
||||
$setOnInsert: 1
|
||||
};
|
||||
|
||||
/*!
|
||||
* Casts `val` according to `schema` and atomic `op`.
|
||||
*
|
||||
* @param {SchemaType} schema
|
||||
* @param {Object} val
|
||||
* @param {String} op - the atomic operator ($pull, $set, etc)
|
||||
* @param {String} $conditional
|
||||
* @param {Query} context
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function castUpdateVal(schema, val, op, $conditional, context, path) {
|
||||
if (!schema) {
|
||||
// non-existing schema path
|
||||
if (op in numberOps) {
|
||||
try {
|
||||
return castNumber(val);
|
||||
} catch (err) {
|
||||
throw new CastError('number', val, path);
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
const cond = schema.caster && op in castOps &&
|
||||
(utils.isObject(val) || Array.isArray(val));
|
||||
if (cond && !overwriteOps[op]) {
|
||||
// Cast values for ops that add data to MongoDB.
|
||||
// Ensures embedded documents get ObjectIds etc.
|
||||
let schemaArrayDepth = 0;
|
||||
let cur = schema;
|
||||
while (cur.$isMongooseArray) {
|
||||
++schemaArrayDepth;
|
||||
cur = cur.caster;
|
||||
}
|
||||
let arrayDepth = 0;
|
||||
let _val = val;
|
||||
while (Array.isArray(_val)) {
|
||||
++arrayDepth;
|
||||
_val = _val[0];
|
||||
}
|
||||
|
||||
const additionalNesting = schemaArrayDepth - arrayDepth;
|
||||
while (arrayDepth < schemaArrayDepth) {
|
||||
val = [val];
|
||||
++arrayDepth;
|
||||
}
|
||||
|
||||
let tmp = schema.applySetters(Array.isArray(val) ? val : [val], context);
|
||||
|
||||
for (let i = 0; i < additionalNesting; ++i) {
|
||||
tmp = tmp[0];
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
if (op in noCastOps) {
|
||||
return val;
|
||||
}
|
||||
if (op in numberOps) {
|
||||
// Null and undefined not allowed for $pop, $inc
|
||||
if (val == null) {
|
||||
throw new CastError('number', val, schema.path);
|
||||
}
|
||||
if (op === '$inc') {
|
||||
// Support `$inc` with long, int32, etc. (gh-4283)
|
||||
return schema.castForQueryWrapper({
|
||||
val: val,
|
||||
context: context
|
||||
});
|
||||
}
|
||||
try {
|
||||
return castNumber(val);
|
||||
} catch (error) {
|
||||
throw new CastError('number', val, schema.path);
|
||||
}
|
||||
}
|
||||
if (op === '$currentDate') {
|
||||
if (typeof val === 'object') {
|
||||
return { $type: val.$type };
|
||||
}
|
||||
return Boolean(val);
|
||||
}
|
||||
|
||||
if (/^\$/.test($conditional)) {
|
||||
return schema.castForQueryWrapper({
|
||||
$conditional: $conditional,
|
||||
val: val,
|
||||
context: context
|
||||
});
|
||||
}
|
||||
|
||||
if (overwriteOps[op]) {
|
||||
return schema.castForQueryWrapper({
|
||||
val: val,
|
||||
context: context,
|
||||
$skipQueryCastForUpdate: val != null && schema.$isMongooseArray && schema.$fullPath != null && !schema.$fullPath.match(/\d+$/),
|
||||
$applySetters: schema[schemaMixedSymbol] != null
|
||||
});
|
||||
}
|
||||
|
||||
return schema.castForQueryWrapper({ val: val, context: context });
|
||||
}
|
||||
51
node_modules/mongoose/lib/helpers/query/completeMany.js
generated
vendored
Normal file
51
node_modules/mongoose/lib/helpers/query/completeMany.js
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
'use strict';
|
||||
|
||||
const helpers = require('../../queryhelpers');
|
||||
const immediate = require('../immediate');
|
||||
|
||||
module.exports = completeMany;
|
||||
|
||||
/*!
|
||||
* Given a model and an array of docs, hydrates all the docs to be instances
|
||||
* of the model. Used to initialize docs returned from the db from `find()`
|
||||
*
|
||||
* @param {Model} model
|
||||
* @param {Array} docs
|
||||
* @param {Object} fields the projection used, including `select` from schemas
|
||||
* @param {Object} userProvidedFields the user-specified projection
|
||||
* @param {Object} opts
|
||||
* @param {Array} [opts.populated]
|
||||
* @param {ClientSession} [opts.session]
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
function completeMany(model, docs, fields, userProvidedFields, opts, callback) {
|
||||
const arr = [];
|
||||
let count = docs.length;
|
||||
const len = count;
|
||||
let error = null;
|
||||
|
||||
function init(_error) {
|
||||
if (_error != null) {
|
||||
error = error || _error;
|
||||
}
|
||||
if (error != null) {
|
||||
--count || immediate(() => callback(error));
|
||||
return;
|
||||
}
|
||||
--count || immediate(() => callback(error, arr));
|
||||
}
|
||||
|
||||
for (let i = 0; i < len; ++i) {
|
||||
arr[i] = helpers.createModel(model, docs[i], fields, userProvidedFields);
|
||||
try {
|
||||
arr[i].init(docs[i], opts, init);
|
||||
} catch (error) {
|
||||
init(error);
|
||||
}
|
||||
|
||||
if (opts.session != null) {
|
||||
arr[i].$session(opts.session);
|
||||
}
|
||||
}
|
||||
}
|
||||
84
node_modules/mongoose/lib/helpers/query/getEmbeddedDiscriminatorPath.js
generated
vendored
Normal file
84
node_modules/mongoose/lib/helpers/query/getEmbeddedDiscriminatorPath.js
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
'use strict';
|
||||
|
||||
const cleanPositionalOperators = require('../schema/cleanPositionalOperators');
|
||||
const get = require('../get');
|
||||
const getDiscriminatorByValue = require('../discriminator/getDiscriminatorByValue');
|
||||
const updatedPathsByArrayFilter = require('../update/updatedPathsByArrayFilter');
|
||||
|
||||
/*!
|
||||
* Like `schema.path()`, except with a document, because impossible to
|
||||
* determine path type without knowing the embedded discriminator key.
|
||||
*/
|
||||
|
||||
module.exports = function getEmbeddedDiscriminatorPath(schema, update, filter, path, options) {
|
||||
const parts = path.split('.');
|
||||
let schematype = null;
|
||||
let type = 'adhocOrUndefined';
|
||||
|
||||
filter = filter || {};
|
||||
update = update || {};
|
||||
const arrayFilters = options != null && Array.isArray(options.arrayFilters) ?
|
||||
options.arrayFilters : [];
|
||||
const updatedPathsByFilter = updatedPathsByArrayFilter(update);
|
||||
|
||||
for (let i = 0; i < parts.length; ++i) {
|
||||
const subpath = cleanPositionalOperators(parts.slice(0, i + 1).join('.'));
|
||||
schematype = schema.path(subpath);
|
||||
if (schematype == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
type = schema.pathType(subpath);
|
||||
if ((schematype.$isSingleNested || schematype.$isMongooseDocumentArrayElement) &&
|
||||
schematype.schema.discriminators != null) {
|
||||
const key = get(schematype, 'schema.options.discriminatorKey');
|
||||
const discriminatorValuePath = subpath + '.' + key;
|
||||
const discriminatorFilterPath =
|
||||
discriminatorValuePath.replace(/\.\d+\./, '.');
|
||||
let discriminatorKey = null;
|
||||
|
||||
if (discriminatorValuePath in filter) {
|
||||
discriminatorKey = filter[discriminatorValuePath];
|
||||
}
|
||||
if (discriminatorFilterPath in filter) {
|
||||
discriminatorKey = filter[discriminatorFilterPath];
|
||||
}
|
||||
|
||||
const wrapperPath = subpath.replace(/\.\d+$/, '');
|
||||
if (schematype.$isMongooseDocumentArrayElement &&
|
||||
get(filter[wrapperPath], '$elemMatch.' + key) != null) {
|
||||
discriminatorKey = filter[wrapperPath].$elemMatch[key];
|
||||
}
|
||||
|
||||
if (discriminatorValuePath in update) {
|
||||
discriminatorKey = update[discriminatorValuePath];
|
||||
}
|
||||
|
||||
for (const filterKey of Object.keys(updatedPathsByFilter)) {
|
||||
const schemaKey = updatedPathsByFilter[filterKey] + '.' + key;
|
||||
const arrayFilterKey = filterKey + '.' + key;
|
||||
if (schemaKey === discriminatorFilterPath) {
|
||||
const filter = arrayFilters.find(filter => filter.hasOwnProperty(arrayFilterKey));
|
||||
if (filter != null) {
|
||||
discriminatorKey = filter[arrayFilterKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (discriminatorKey == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const discriminatorSchema = getDiscriminatorByValue(schematype.caster.discriminators, discriminatorKey).schema;
|
||||
|
||||
const rest = parts.slice(i + 1).join('.');
|
||||
schematype = discriminatorSchema.path(rest);
|
||||
if (schematype != null) {
|
||||
type = discriminatorSchema._getPathType(rest);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { type: type, schematype: schematype };
|
||||
};
|
||||
28
node_modules/mongoose/lib/helpers/query/handleImmutable.js
generated
vendored
Normal file
28
node_modules/mongoose/lib/helpers/query/handleImmutable.js
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
'use strict';
|
||||
|
||||
const StrictModeError = require('../../error/strict');
|
||||
|
||||
module.exports = function handleImmutable(schematype, strict, obj, key, fullPath, ctx) {
|
||||
if (schematype == null || !schematype.options || !schematype.options.immutable) {
|
||||
return false;
|
||||
}
|
||||
let immutable = schematype.options.immutable;
|
||||
|
||||
if (typeof immutable === 'function') {
|
||||
immutable = immutable.call(ctx, ctx);
|
||||
}
|
||||
if (!immutable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strict === false) {
|
||||
return false;
|
||||
}
|
||||
if (strict === 'throw') {
|
||||
throw new StrictModeError(null,
|
||||
`Field ${fullPath} is immutable and strict = 'throw'`);
|
||||
}
|
||||
|
||||
delete obj[key];
|
||||
return true;
|
||||
};
|
||||
19
node_modules/mongoose/lib/helpers/query/hasDollarKeys.js
generated
vendored
Normal file
19
node_modules/mongoose/lib/helpers/query/hasDollarKeys.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
module.exports = function(obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
const keys = Object.keys(obj);
|
||||
const len = keys.length;
|
||||
for (let i = 0; i < len; ++i) {
|
||||
if (keys[i].startsWith('$')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
11
node_modules/mongoose/lib/helpers/query/isOperator.js
generated
vendored
Normal file
11
node_modules/mongoose/lib/helpers/query/isOperator.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
const specialKeys = new Set([
|
||||
'$ref',
|
||||
'$id',
|
||||
'$db'
|
||||
]);
|
||||
|
||||
module.exports = function isOperator(path) {
|
||||
return path.startsWith('$') && !specialKeys.has(path);
|
||||
};
|
||||
14
node_modules/mongoose/lib/helpers/query/sanitizeProjection.js
generated
vendored
Normal file
14
node_modules/mongoose/lib/helpers/query/sanitizeProjection.js
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function sanitizeProjection(projection) {
|
||||
if (projection == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const keys = Object.keys(projection);
|
||||
for (let i = 0; i < keys.length; ++i) {
|
||||
if (typeof projection[keys[i]] === 'string') {
|
||||
projection[keys[i]] = 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
49
node_modules/mongoose/lib/helpers/query/selectPopulatedFields.js
generated
vendored
Normal file
49
node_modules/mongoose/lib/helpers/query/selectPopulatedFields.js
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
'use strict';
|
||||
|
||||
const isExclusive = require('../projection/isExclusive');
|
||||
const isInclusive = require('../projection/isInclusive');
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
module.exports = function selectPopulatedFields(fields, userProvidedFields, populateOptions) {
|
||||
if (populateOptions == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const paths = Object.keys(populateOptions);
|
||||
userProvidedFields = userProvidedFields || {};
|
||||
if (isInclusive(fields)) {
|
||||
for (const path of paths) {
|
||||
if (!isPathInFields(userProvidedFields, path)) {
|
||||
fields[path] = 1;
|
||||
} else if (userProvidedFields[path] === 0) {
|
||||
delete fields[path];
|
||||
}
|
||||
}
|
||||
} else if (isExclusive(fields)) {
|
||||
for (const path of paths) {
|
||||
if (userProvidedFields[path] == null) {
|
||||
delete fields[path];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
function isPathInFields(userProvidedFields, path) {
|
||||
const pieces = path.split('.');
|
||||
const len = pieces.length;
|
||||
let cur = pieces[0];
|
||||
for (let i = 1; i < len; ++i) {
|
||||
if (userProvidedFields[cur] != null || userProvidedFields[cur + '.$'] != null) {
|
||||
return true;
|
||||
}
|
||||
cur += '.' + pieces[i];
|
||||
}
|
||||
return userProvidedFields[cur] != null || userProvidedFields[cur + '.$'] != null;
|
||||
}
|
||||
18
node_modules/mongoose/lib/helpers/query/wrapThunk.js
generated
vendored
Normal file
18
node_modules/mongoose/lib/helpers/query/wrapThunk.js
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
'use strict';
|
||||
|
||||
/*!
|
||||
* A query thunk is the function responsible for sending the query to MongoDB,
|
||||
* like `Query#_findOne()` or `Query#_execUpdate()`. The `Query#exec()` function
|
||||
* calls a thunk. The term "thunk" here is the traditional Node.js definition:
|
||||
* a function that takes exactly 1 parameter, a callback.
|
||||
*
|
||||
* This function defines common behavior for all query thunks.
|
||||
*/
|
||||
|
||||
module.exports = function wrapThunk(fn) {
|
||||
return function _wrappedThunk(cb) {
|
||||
++this._executionCount;
|
||||
|
||||
fn.call(this, cb);
|
||||
};
|
||||
};
|
||||
7
node_modules/mongoose/lib/helpers/schema/addAutoId.js
generated
vendored
Normal file
7
node_modules/mongoose/lib/helpers/schema/addAutoId.js
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function addAutoId(schema) {
|
||||
const _obj = { _id: { auto: true } };
|
||||
_obj._id[schema.options.typeKey] = 'ObjectId';
|
||||
schema.add(_obj);
|
||||
};
|
||||
44
node_modules/mongoose/lib/helpers/schema/applyPlugins.js
generated
vendored
Normal file
44
node_modules/mongoose/lib/helpers/schema/applyPlugins.js
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function applyPlugins(schema, plugins, options, cacheKey) {
|
||||
if (schema[cacheKey]) {
|
||||
return;
|
||||
}
|
||||
schema[cacheKey] = true;
|
||||
|
||||
if (!options || !options.skipTopLevel) {
|
||||
for (const plugin of plugins) {
|
||||
schema.plugin(plugin[0], plugin[1]);
|
||||
}
|
||||
}
|
||||
|
||||
options = Object.assign({}, options);
|
||||
delete options.skipTopLevel;
|
||||
|
||||
if (options.applyPluginsToChildSchemas !== false) {
|
||||
for (const path of Object.keys(schema.paths)) {
|
||||
const type = schema.paths[path];
|
||||
if (type.schema != null) {
|
||||
applyPlugins(type.schema, plugins, options, cacheKey);
|
||||
|
||||
// Recompile schema because plugins may have changed it, see gh-7572
|
||||
type.caster.prototype.$__setSchema(type.schema);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const discriminators = schema.discriminators;
|
||||
if (discriminators == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const applyPluginsToDiscriminators = options.applyPluginsToDiscriminators;
|
||||
|
||||
const keys = Object.keys(discriminators);
|
||||
for (const discriminatorKey of keys) {
|
||||
const discriminatorSchema = discriminators[discriminatorKey];
|
||||
|
||||
applyPlugins(discriminatorSchema, plugins,
|
||||
{ skipTopLevel: !applyPluginsToDiscriminators }, cacheKey);
|
||||
}
|
||||
};
|
||||
30
node_modules/mongoose/lib/helpers/schema/applyWriteConcern.js
generated
vendored
Normal file
30
node_modules/mongoose/lib/helpers/schema/applyWriteConcern.js
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
'use strict';
|
||||
|
||||
const get = require('../get');
|
||||
|
||||
module.exports = function applyWriteConcern(schema, options) {
|
||||
const writeConcern = get(schema, 'options.writeConcern', {});
|
||||
if (Object.keys(writeConcern).length != 0) {
|
||||
options.writeConcern = {};
|
||||
if (!('w' in options) && writeConcern.w != null) {
|
||||
options.writeConcern.w = writeConcern.w;
|
||||
}
|
||||
if (!('j' in options) && writeConcern.j != null) {
|
||||
options.writeConcern.j = writeConcern.j;
|
||||
}
|
||||
if (!('wtimeout' in options) && writeConcern.wtimeout != null) {
|
||||
options.writeConcern.wtimeout = writeConcern.wtimeout;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!('w' in options) && writeConcern.w != null) {
|
||||
options.w = writeConcern.w;
|
||||
}
|
||||
if (!('j' in options) && writeConcern.j != null) {
|
||||
options.j = writeConcern.j;
|
||||
}
|
||||
if (!('wtimeout' in options) && writeConcern.wtimeout != null) {
|
||||
options.wtimeout = writeConcern.wtimeout;
|
||||
}
|
||||
}
|
||||
};
|
||||
12
node_modules/mongoose/lib/helpers/schema/cleanPositionalOperators.js
generated
vendored
Normal file
12
node_modules/mongoose/lib/helpers/schema/cleanPositionalOperators.js
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* For consistency's sake, we replace positional operator `$` and array filters
|
||||
* `$[]` and `$[foo]` with `0` when looking up schema paths.
|
||||
*/
|
||||
|
||||
module.exports = function cleanPositionalOperators(path) {
|
||||
return path.
|
||||
replace(/\.\$(\[[^\]]*\])?(?=\.)/g, '.0').
|
||||
replace(/\.\$(\[[^\]]*\])?$/g, '.0');
|
||||
};
|
||||
155
node_modules/mongoose/lib/helpers/schema/getIndexes.js
generated
vendored
Normal file
155
node_modules/mongoose/lib/helpers/schema/getIndexes.js
generated
vendored
Normal file
@@ -0,0 +1,155 @@
|
||||
'use strict';
|
||||
|
||||
const get = require('../get');
|
||||
const helperIsObject = require('../isObject');
|
||||
|
||||
/*!
|
||||
* Gather all indexes defined in the schema, including single nested,
|
||||
* document arrays, and embedded discriminators.
|
||||
*/
|
||||
|
||||
module.exports = function getIndexes(schema) {
|
||||
let indexes = [];
|
||||
const schemaStack = new WeakMap();
|
||||
const indexTypes = schema.constructor.indexTypes;
|
||||
const indexByName = new Map();
|
||||
|
||||
collectIndexes(schema);
|
||||
return indexes;
|
||||
|
||||
function collectIndexes(schema, prefix, baseSchema) {
|
||||
// Ignore infinitely nested schemas, if we've already seen this schema
|
||||
// along this path there must be a cycle
|
||||
if (schemaStack.has(schema)) {
|
||||
return;
|
||||
}
|
||||
schemaStack.set(schema, true);
|
||||
|
||||
prefix = prefix || '';
|
||||
const keys = Object.keys(schema.paths);
|
||||
|
||||
for (const key of keys) {
|
||||
const path = schema.paths[key];
|
||||
if (baseSchema != null && baseSchema.paths[key]) {
|
||||
// If looking at an embedded discriminator schema, don't look at paths
|
||||
// that the
|
||||
continue;
|
||||
}
|
||||
|
||||
if (path.$isMongooseDocumentArray || path.$isSingleNested) {
|
||||
if (get(path, 'options.excludeIndexes') !== true &&
|
||||
get(path, 'schemaOptions.excludeIndexes') !== true &&
|
||||
get(path, 'schema.options.excludeIndexes') !== true) {
|
||||
collectIndexes(path.schema, prefix + key + '.');
|
||||
}
|
||||
|
||||
if (path.schema.discriminators != null) {
|
||||
const discriminators = path.schema.discriminators;
|
||||
const discriminatorKeys = Object.keys(discriminators);
|
||||
for (const discriminatorKey of discriminatorKeys) {
|
||||
collectIndexes(discriminators[discriminatorKey],
|
||||
prefix + key + '.', path.schema);
|
||||
}
|
||||
}
|
||||
|
||||
// Retained to minimize risk of backwards breaking changes due to
|
||||
// gh-6113
|
||||
if (path.$isMongooseDocumentArray) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const index = path._index || (path.caster && path.caster._index);
|
||||
|
||||
if (index !== false && index !== null && index !== undefined) {
|
||||
const field = {};
|
||||
const isObject = helperIsObject(index);
|
||||
const options = isObject ? index : {};
|
||||
const type = typeof index === 'string' ? index :
|
||||
isObject ? index.type :
|
||||
false;
|
||||
|
||||
if (type && indexTypes.indexOf(type) !== -1) {
|
||||
field[prefix + key] = type;
|
||||
} else if (options.text) {
|
||||
field[prefix + key] = 'text';
|
||||
delete options.text;
|
||||
} else {
|
||||
const isDescendingIndex = Number(index) === -1;
|
||||
field[prefix + key] = isDescendingIndex ? -1 : 1;
|
||||
}
|
||||
|
||||
delete options.type;
|
||||
if (!('background' in options)) {
|
||||
options.background = true;
|
||||
}
|
||||
if (schema.options.autoIndex != null) {
|
||||
options._autoIndex = schema.options.autoIndex;
|
||||
}
|
||||
|
||||
const indexName = options && options.name;
|
||||
if (typeof indexName === 'string') {
|
||||
if (indexByName.has(indexName)) {
|
||||
Object.assign(indexByName.get(indexName), field);
|
||||
} else {
|
||||
indexes.push([field, options]);
|
||||
indexByName.set(indexName, field);
|
||||
}
|
||||
} else {
|
||||
indexes.push([field, options]);
|
||||
indexByName.set(indexName, field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
schemaStack.delete(schema);
|
||||
|
||||
if (prefix) {
|
||||
fixSubIndexPaths(schema, prefix);
|
||||
} else {
|
||||
schema._indexes.forEach(function(index) {
|
||||
if (!('background' in index[1])) {
|
||||
index[1].background = true;
|
||||
}
|
||||
});
|
||||
indexes = indexes.concat(schema._indexes);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Checks for indexes added to subdocs using Schema.index().
|
||||
* These indexes need their paths prefixed properly.
|
||||
*
|
||||
* schema._indexes = [ [indexObj, options], [indexObj, options] ..]
|
||||
*/
|
||||
|
||||
function fixSubIndexPaths(schema, prefix) {
|
||||
const subindexes = schema._indexes;
|
||||
const len = subindexes.length;
|
||||
for (let i = 0; i < len; ++i) {
|
||||
const indexObj = subindexes[i][0];
|
||||
const indexOptions = subindexes[i][1];
|
||||
const keys = Object.keys(indexObj);
|
||||
const klen = keys.length;
|
||||
const newindex = {};
|
||||
|
||||
// use forward iteration, order matters
|
||||
for (let j = 0; j < klen; ++j) {
|
||||
const key = keys[j];
|
||||
newindex[prefix + key] = indexObj[key];
|
||||
}
|
||||
|
||||
const newIndexOptions = Object.assign({}, indexOptions);
|
||||
if (indexOptions != null && indexOptions.partialFilterExpression != null) {
|
||||
newIndexOptions.partialFilterExpression = {};
|
||||
const partialFilterExpression = indexOptions.partialFilterExpression;
|
||||
for (const key of Object.keys(partialFilterExpression)) {
|
||||
newIndexOptions.partialFilterExpression[prefix + key] =
|
||||
partialFilterExpression[key];
|
||||
}
|
||||
}
|
||||
|
||||
indexes.push([newindex, newIndexOptions]);
|
||||
}
|
||||
}
|
||||
};
|
||||
35
node_modules/mongoose/lib/helpers/schema/getPath.js
generated
vendored
Normal file
35
node_modules/mongoose/lib/helpers/schema/getPath.js
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
|
||||
/*!
|
||||
* Behaves like `Schema#path()`, except for it also digs into arrays without
|
||||
* needing to put `.0.`, so `getPath(schema, 'docArr.elProp')` works.
|
||||
*/
|
||||
|
||||
module.exports = function getPath(schema, path) {
|
||||
let schematype = schema.path(path);
|
||||
if (schematype != null) {
|
||||
return schematype;
|
||||
}
|
||||
|
||||
const pieces = path.split('.');
|
||||
let cur = '';
|
||||
let isArray = false;
|
||||
|
||||
for (const piece of pieces) {
|
||||
if (/^\d+$/.test(piece) && isArray) {
|
||||
continue;
|
||||
}
|
||||
cur = cur.length === 0 ? piece : cur + '.' + piece;
|
||||
|
||||
schematype = schema.path(cur);
|
||||
if (schematype != null && schematype.schema) {
|
||||
schema = schematype.schema;
|
||||
cur = '';
|
||||
if (schematype.$isMongooseDocumentArray) {
|
||||
isArray = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return schematype;
|
||||
};
|
||||
20
node_modules/mongoose/lib/helpers/schema/handleIdOption.js
generated
vendored
Normal file
20
node_modules/mongoose/lib/helpers/schema/handleIdOption.js
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
'use strict';
|
||||
|
||||
const addAutoId = require('./addAutoId');
|
||||
|
||||
module.exports = function handleIdOption(schema, options) {
|
||||
if (options == null || options._id == null) {
|
||||
return schema;
|
||||
}
|
||||
|
||||
schema = schema.clone();
|
||||
if (!options._id) {
|
||||
schema.remove('_id');
|
||||
schema.options._id = false;
|
||||
} else if (!schema.paths['_id']) {
|
||||
addAutoId(schema);
|
||||
schema.options._id = true;
|
||||
}
|
||||
|
||||
return schema;
|
||||
};
|
||||
24
node_modules/mongoose/lib/helpers/schema/handleTimestampOption.js
generated
vendored
Normal file
24
node_modules/mongoose/lib/helpers/schema/handleTimestampOption.js
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = handleTimestampOption;
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
function handleTimestampOption(arg, prop) {
|
||||
if (arg == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (typeof arg === 'boolean') {
|
||||
return prop;
|
||||
}
|
||||
if (typeof arg[prop] === 'boolean') {
|
||||
return arg[prop] ? prop : null;
|
||||
}
|
||||
if (!(prop in arg)) {
|
||||
return prop;
|
||||
}
|
||||
return arg[prop];
|
||||
}
|
||||
27
node_modules/mongoose/lib/helpers/schema/merge.js
generated
vendored
Normal file
27
node_modules/mongoose/lib/helpers/schema/merge.js
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function merge(s1, s2, skipConflictingPaths) {
|
||||
const paths = Object.keys(s2.tree);
|
||||
const pathsToAdd = {};
|
||||
for (const key of paths) {
|
||||
if (skipConflictingPaths && (s1.paths[key] || s1.nested[key] || s1.singleNestedPaths[key])) {
|
||||
continue;
|
||||
}
|
||||
pathsToAdd[key] = s2.tree[key];
|
||||
}
|
||||
s1.add(pathsToAdd);
|
||||
|
||||
s1.callQueue = s1.callQueue.concat(s2.callQueue);
|
||||
s1.method(s2.methods);
|
||||
s1.static(s2.statics);
|
||||
|
||||
for (const query in s2.query) {
|
||||
s1.query[query] = s2.query[query];
|
||||
}
|
||||
|
||||
for (const virtual in s2.virtuals) {
|
||||
s1.virtuals[virtual] = s2.virtuals[virtual].clone();
|
||||
}
|
||||
|
||||
s1.s.hooks.merge(s2.s.hooks, false);
|
||||
};
|
||||
45
node_modules/mongoose/lib/helpers/schematype/handleImmutable.js
generated
vendored
Normal file
45
node_modules/mongoose/lib/helpers/schematype/handleImmutable.js
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
'use strict';
|
||||
|
||||
const StrictModeError = require('../../error/strict');
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
module.exports = function(schematype) {
|
||||
if (schematype.$immutable) {
|
||||
schematype.$immutableSetter = createImmutableSetter(schematype.path,
|
||||
schematype.options.immutable);
|
||||
schematype.set(schematype.$immutableSetter);
|
||||
} else if (schematype.$immutableSetter) {
|
||||
schematype.setters = schematype.setters.
|
||||
filter(fn => fn !== schematype.$immutableSetter);
|
||||
delete schematype.$immutableSetter;
|
||||
}
|
||||
};
|
||||
|
||||
function createImmutableSetter(path, immutable) {
|
||||
return function immutableSetter(v) {
|
||||
if (this == null || this.$__ == null) {
|
||||
return v;
|
||||
}
|
||||
if (this.isNew) {
|
||||
return v;
|
||||
}
|
||||
|
||||
const _immutable = typeof immutable === 'function' ?
|
||||
immutable.call(this, this) :
|
||||
immutable;
|
||||
if (!_immutable) {
|
||||
return v;
|
||||
}
|
||||
|
||||
const _value = this.$__getValue(path);
|
||||
if (this.$__.strictMode === 'throw' && v !== _value) {
|
||||
throw new StrictModeError(path, 'Path `' + path + '` is immutable ' +
|
||||
'and strict mode is set to throw.', true);
|
||||
}
|
||||
|
||||
return _value;
|
||||
};
|
||||
}
|
||||
127
node_modules/mongoose/lib/helpers/setDefaultsOnInsert.js
generated
vendored
Normal file
127
node_modules/mongoose/lib/helpers/setDefaultsOnInsert.js
generated
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
'use strict';
|
||||
const modifiedPaths = require('./common').modifiedPaths;
|
||||
const get = require('./get');
|
||||
|
||||
/**
|
||||
* Applies defaults to update and findOneAndUpdate operations.
|
||||
*
|
||||
* @param {Object} filter
|
||||
* @param {Schema} schema
|
||||
* @param {Object} castedDoc
|
||||
* @param {Object} options
|
||||
* @method setDefaultsOnInsert
|
||||
* @api private
|
||||
*/
|
||||
|
||||
module.exports = function(filter, schema, castedDoc, options) {
|
||||
options = options || {};
|
||||
|
||||
const shouldSetDefaultsOnInsert =
|
||||
options.setDefaultsOnInsert != null ?
|
||||
options.setDefaultsOnInsert :
|
||||
schema.base.options.setDefaultsOnInsert;
|
||||
|
||||
if (!options.upsert || !shouldSetDefaultsOnInsert) {
|
||||
return castedDoc;
|
||||
}
|
||||
|
||||
const keys = Object.keys(castedDoc || {});
|
||||
const updatedKeys = {};
|
||||
const updatedValues = {};
|
||||
const numKeys = keys.length;
|
||||
const modified = {};
|
||||
|
||||
let hasDollarUpdate = false;
|
||||
|
||||
for (let i = 0; i < numKeys; ++i) {
|
||||
if (keys[i].startsWith('$')) {
|
||||
modifiedPaths(castedDoc[keys[i]], '', modified);
|
||||
hasDollarUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasDollarUpdate) {
|
||||
modifiedPaths(castedDoc, '', modified);
|
||||
}
|
||||
|
||||
const paths = Object.keys(filter);
|
||||
const numPaths = paths.length;
|
||||
for (let i = 0; i < numPaths; ++i) {
|
||||
const path = paths[i];
|
||||
const condition = filter[path];
|
||||
if (condition && typeof condition === 'object') {
|
||||
const conditionKeys = Object.keys(condition);
|
||||
const numConditionKeys = conditionKeys.length;
|
||||
let hasDollarKey = false;
|
||||
for (let j = 0; j < numConditionKeys; ++j) {
|
||||
if (conditionKeys[j].startsWith('$')) {
|
||||
hasDollarKey = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hasDollarKey) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
updatedKeys[path] = true;
|
||||
modified[path] = true;
|
||||
}
|
||||
|
||||
if (options && options.overwrite && !hasDollarUpdate) {
|
||||
// Defaults will be set later, since we're overwriting we'll cast
|
||||
// the whole update to a document
|
||||
return castedDoc;
|
||||
}
|
||||
|
||||
schema.eachPath(function(path, schemaType) {
|
||||
// Skip single nested paths if underneath a map
|
||||
const isUnderneathMap = schemaType.path.endsWith('.$*') ||
|
||||
schemaType.path.indexOf('.$*.') !== -1;
|
||||
if (schemaType.$isSingleNested && !isUnderneathMap) {
|
||||
// Only handle nested schemas 1-level deep to avoid infinite
|
||||
// recursion re: https://github.com/mongodb-js/mongoose-autopopulate/issues/11
|
||||
schemaType.schema.eachPath(function(_path, _schemaType) {
|
||||
if (_path === '_id' && _schemaType.auto) {
|
||||
// Ignore _id if auto id so we don't create subdocs
|
||||
return;
|
||||
}
|
||||
|
||||
const def = _schemaType.getDefault(null, true);
|
||||
if (!isModified(modified, path + '.' + _path) &&
|
||||
typeof def !== 'undefined') {
|
||||
castedDoc = castedDoc || {};
|
||||
castedDoc.$setOnInsert = castedDoc.$setOnInsert || {};
|
||||
castedDoc.$setOnInsert[path + '.' + _path] = def;
|
||||
updatedValues[path + '.' + _path] = def;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const def = schemaType.getDefault(null, true);
|
||||
if (!isModified(modified, path) && typeof def !== 'undefined') {
|
||||
castedDoc = castedDoc || {};
|
||||
castedDoc.$setOnInsert = castedDoc.$setOnInsert || {};
|
||||
if (get(castedDoc, path) == null) {
|
||||
castedDoc.$setOnInsert[path] = def;
|
||||
}
|
||||
updatedValues[path] = def;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return castedDoc;
|
||||
};
|
||||
|
||||
function isModified(modified, path) {
|
||||
if (modified[path]) {
|
||||
return true;
|
||||
}
|
||||
const sp = path.split('.');
|
||||
let cur = sp[0];
|
||||
for (let i = 1; i < sp.length; ++i) {
|
||||
if (modified[cur]) {
|
||||
return true;
|
||||
}
|
||||
cur += '.' + sp[i];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
3
node_modules/mongoose/lib/helpers/specialProperties.js
generated
vendored
Normal file
3
node_modules/mongoose/lib/helpers/specialProperties.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = new Set(['__proto__', 'constructor', 'prototype']);
|
||||
19
node_modules/mongoose/lib/helpers/symbols.js
generated
vendored
Normal file
19
node_modules/mongoose/lib/helpers/symbols.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
exports.arrayAtomicsSymbol = Symbol('mongoose#Array#_atomics');
|
||||
exports.arrayParentSymbol = Symbol('mongoose#Array#_parent');
|
||||
exports.arrayPathSymbol = Symbol('mongoose#Array#_path');
|
||||
exports.arraySchemaSymbol = Symbol('mongoose#Array#_schema');
|
||||
exports.documentArrayParent = Symbol('mongoose:documentArrayParent');
|
||||
exports.documentIsSelected = Symbol('mongoose#Document#isSelected');
|
||||
exports.documentIsModified = Symbol('mongoose#Document#isModified');
|
||||
exports.documentModifiedPaths = Symbol('mongoose#Document#modifiedPaths');
|
||||
exports.documentSchemaSymbol = Symbol('mongoose#Document#schema');
|
||||
exports.getSymbol = Symbol('mongoose#Document#get');
|
||||
exports.modelSymbol = Symbol('mongoose#Model');
|
||||
exports.objectIdSymbol = Symbol('mongoose#ObjectId');
|
||||
exports.populateModelSymbol = Symbol('mongoose.PopulateOptions#Model');
|
||||
exports.schemaTypeSymbol = Symbol('mongoose#schemaType');
|
||||
exports.sessionNewDocuments = Symbol('mongoose:ClientSession#newDocuments');
|
||||
exports.scopeSymbol = Symbol('mongoose#Document#scope');
|
||||
exports.validatorErrorSymbol = Symbol('mongoose:validatorError');
|
||||
3
node_modules/mongoose/lib/helpers/timers.js
generated
vendored
Normal file
3
node_modules/mongoose/lib/helpers/timers.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
'use strict';
|
||||
|
||||
exports.setTimeout = setTimeout;
|
||||
111
node_modules/mongoose/lib/helpers/timestamps/setupTimestamps.js
generated
vendored
Normal file
111
node_modules/mongoose/lib/helpers/timestamps/setupTimestamps.js
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
'use strict';
|
||||
|
||||
const applyTimestampsToChildren = require('../update/applyTimestampsToChildren');
|
||||
const applyTimestampsToUpdate = require('../update/applyTimestampsToUpdate');
|
||||
const get = require('../get');
|
||||
const handleTimestampOption = require('../schema/handleTimestampOption');
|
||||
const symbols = require('../../schema/symbols');
|
||||
|
||||
module.exports = function setupTimestamps(schema, timestamps) {
|
||||
const childHasTimestamp = schema.childSchemas.find(withTimestamp);
|
||||
|
||||
function withTimestamp(s) {
|
||||
const ts = s.schema.options.timestamps;
|
||||
return !!ts;
|
||||
}
|
||||
|
||||
if (!timestamps && !childHasTimestamp) {
|
||||
return;
|
||||
}
|
||||
|
||||
const createdAt = handleTimestampOption(timestamps, 'createdAt');
|
||||
const updatedAt = handleTimestampOption(timestamps, 'updatedAt');
|
||||
const currentTime = timestamps != null && timestamps.hasOwnProperty('currentTime') ?
|
||||
timestamps.currentTime :
|
||||
null;
|
||||
const schemaAdditions = {};
|
||||
|
||||
schema.$timestamps = { createdAt: createdAt, updatedAt: updatedAt };
|
||||
|
||||
if (updatedAt && !schema.paths[updatedAt]) {
|
||||
schemaAdditions[updatedAt] = Date;
|
||||
}
|
||||
|
||||
if (createdAt && !schema.paths[createdAt]) {
|
||||
schemaAdditions[createdAt] = Date;
|
||||
}
|
||||
|
||||
schema.add(schemaAdditions);
|
||||
|
||||
schema.pre('save', function(next) {
|
||||
const timestampOption = get(this, '$__.saveOptions.timestamps');
|
||||
if (timestampOption === false) {
|
||||
return next();
|
||||
}
|
||||
|
||||
const skipUpdatedAt = timestampOption != null && timestampOption.updatedAt === false;
|
||||
const skipCreatedAt = timestampOption != null && timestampOption.createdAt === false;
|
||||
|
||||
const defaultTimestamp = currentTime != null ?
|
||||
currentTime() :
|
||||
(this.ownerDocument ? this.ownerDocument() : this).constructor.base.now();
|
||||
const auto_id = this._id && this._id.auto;
|
||||
|
||||
if (!skipCreatedAt && createdAt && !this.get(createdAt) && this.$__isSelected(createdAt)) {
|
||||
this.$set(createdAt, auto_id ? this._id.getTimestamp() : defaultTimestamp);
|
||||
}
|
||||
|
||||
if (!skipUpdatedAt && updatedAt && (this.isNew || this.isModified())) {
|
||||
let ts = defaultTimestamp;
|
||||
if (this.isNew) {
|
||||
if (createdAt != null) {
|
||||
ts = this.$__getValue(createdAt);
|
||||
} else if (auto_id) {
|
||||
ts = this._id.getTimestamp();
|
||||
}
|
||||
}
|
||||
this.$set(updatedAt, ts);
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
schema.methods.initializeTimestamps = function() {
|
||||
const ts = currentTime != null ?
|
||||
currentTime() :
|
||||
this.constructor.base.now();
|
||||
if (createdAt && !this.get(createdAt)) {
|
||||
this.$set(createdAt, ts);
|
||||
}
|
||||
if (updatedAt && !this.get(updatedAt)) {
|
||||
this.$set(updatedAt, ts);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
_setTimestampsOnUpdate[symbols.builtInMiddleware] = true;
|
||||
|
||||
const opts = { query: true, model: false };
|
||||
schema.pre('findOneAndReplace', opts, _setTimestampsOnUpdate);
|
||||
schema.pre('findOneAndUpdate', opts, _setTimestampsOnUpdate);
|
||||
schema.pre('replaceOne', opts, _setTimestampsOnUpdate);
|
||||
schema.pre('update', opts, _setTimestampsOnUpdate);
|
||||
schema.pre('updateOne', opts, _setTimestampsOnUpdate);
|
||||
schema.pre('updateMany', opts, _setTimestampsOnUpdate);
|
||||
|
||||
function _setTimestampsOnUpdate(next) {
|
||||
const now = currentTime != null ?
|
||||
currentTime() :
|
||||
this.model.base.now();
|
||||
|
||||
// Replacing with null update should still trigger timestamps
|
||||
if (this.op === 'findOneAndReplace' && this.getUpdate() == null) {
|
||||
this.setUpdate({});
|
||||
}
|
||||
|
||||
applyTimestampsToUpdate(now, createdAt, updatedAt, this.getUpdate(),
|
||||
this.options, this.schema);
|
||||
applyTimestampsToChildren(now, this.getUpdate(), this.model.schema);
|
||||
next();
|
||||
}
|
||||
};
|
||||
11
node_modules/mongoose/lib/helpers/topology/allServersUnknown.js
generated
vendored
Normal file
11
node_modules/mongoose/lib/helpers/topology/allServersUnknown.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function allServersUnknown(topologyDescription) {
|
||||
if (topologyDescription == null ||
|
||||
topologyDescription.constructor.name !== 'TopologyDescription') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const servers = Array.from(topologyDescription.servers.values());
|
||||
return servers.length > 0 && servers.every(server => server.type === 'Unknown');
|
||||
};
|
||||
12
node_modules/mongoose/lib/helpers/topology/isAtlas.js
generated
vendored
Normal file
12
node_modules/mongoose/lib/helpers/topology/isAtlas.js
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function isAtlas(topologyDescription) {
|
||||
if (topologyDescription == null ||
|
||||
topologyDescription.constructor.name !== 'TopologyDescription') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const hostnames = Array.from(topologyDescription.servers.keys());
|
||||
return hostnames.length > 0 &&
|
||||
hostnames.every(host => host.endsWith('.mongodb.net:27017'));
|
||||
};
|
||||
15
node_modules/mongoose/lib/helpers/topology/isSSLError.js
generated
vendored
Normal file
15
node_modules/mongoose/lib/helpers/topology/isSSLError.js
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
const nonSSLMessage = 'Client network socket disconnected before secure TLS ' +
|
||||
'connection was established';
|
||||
|
||||
module.exports = function isSSLError(topologyDescription) {
|
||||
if (topologyDescription == null ||
|
||||
topologyDescription.constructor.name !== 'TopologyDescription') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const descriptions = Array.from(topologyDescription.servers.values());
|
||||
return descriptions.length > 0 &&
|
||||
descriptions.every(descr => descr.error && descr.error.message.indexOf(nonSSLMessage) !== -1);
|
||||
};
|
||||
172
node_modules/mongoose/lib/helpers/update/applyTimestampsToChildren.js
generated
vendored
Normal file
172
node_modules/mongoose/lib/helpers/update/applyTimestampsToChildren.js
generated
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
'use strict';
|
||||
|
||||
const cleanPositionalOperators = require('../schema/cleanPositionalOperators');
|
||||
const handleTimestampOption = require('../schema/handleTimestampOption');
|
||||
|
||||
module.exports = applyTimestampsToChildren;
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
function applyTimestampsToChildren(now, update, schema) {
|
||||
if (update == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const keys = Object.keys(update);
|
||||
const hasDollarKey = keys.some(key => key.startsWith('$'));
|
||||
|
||||
if (hasDollarKey) {
|
||||
if (update.$push) {
|
||||
for (const key of Object.keys(update.$push)) {
|
||||
const $path = schema.path(key);
|
||||
if (update.$push[key] &&
|
||||
$path &&
|
||||
$path.$isMongooseDocumentArray &&
|
||||
$path.schema.options.timestamps) {
|
||||
const timestamps = $path.schema.options.timestamps;
|
||||
const createdAt = handleTimestampOption(timestamps, 'createdAt');
|
||||
const updatedAt = handleTimestampOption(timestamps, 'updatedAt');
|
||||
if (update.$push[key].$each) {
|
||||
update.$push[key].$each.forEach(function(subdoc) {
|
||||
if (updatedAt != null) {
|
||||
subdoc[updatedAt] = now;
|
||||
}
|
||||
if (createdAt != null) {
|
||||
subdoc[createdAt] = now;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (updatedAt != null) {
|
||||
update.$push[key][updatedAt] = now;
|
||||
}
|
||||
if (createdAt != null) {
|
||||
update.$push[key][createdAt] = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (update.$set != null) {
|
||||
const keys = Object.keys(update.$set);
|
||||
for (const key of keys) {
|
||||
applyTimestampsToUpdateKey(schema, key, update.$set, now);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const updateKeys = Object.keys(update).filter(key => !key.startsWith('$'));
|
||||
for (const key of updateKeys) {
|
||||
applyTimestampsToUpdateKey(schema, key, update, now);
|
||||
}
|
||||
}
|
||||
|
||||
function applyTimestampsToDocumentArray(arr, schematype, now) {
|
||||
const timestamps = schematype.schema.options.timestamps;
|
||||
|
||||
if (!timestamps) {
|
||||
return;
|
||||
}
|
||||
|
||||
const len = arr.length;
|
||||
|
||||
const createdAt = handleTimestampOption(timestamps, 'createdAt');
|
||||
const updatedAt = handleTimestampOption(timestamps, 'updatedAt');
|
||||
for (let i = 0; i < len; ++i) {
|
||||
if (updatedAt != null) {
|
||||
arr[i][updatedAt] = now;
|
||||
}
|
||||
if (createdAt != null) {
|
||||
arr[i][createdAt] = now;
|
||||
}
|
||||
|
||||
applyTimestampsToChildren(now, arr[i], schematype.schema);
|
||||
}
|
||||
}
|
||||
|
||||
function applyTimestampsToSingleNested(subdoc, schematype, now) {
|
||||
const timestamps = schematype.schema.options.timestamps;
|
||||
if (!timestamps) {
|
||||
return;
|
||||
}
|
||||
|
||||
const createdAt = handleTimestampOption(timestamps, 'createdAt');
|
||||
const updatedAt = handleTimestampOption(timestamps, 'updatedAt');
|
||||
if (updatedAt != null) {
|
||||
subdoc[updatedAt] = now;
|
||||
}
|
||||
if (createdAt != null) {
|
||||
subdoc[createdAt] = now;
|
||||
}
|
||||
|
||||
applyTimestampsToChildren(now, subdoc, schematype.schema);
|
||||
}
|
||||
|
||||
function applyTimestampsToUpdateKey(schema, key, update, now) {
|
||||
// Replace positional operator `$` and array filters `$[]` and `$[.*]`
|
||||
const keyToSearch = cleanPositionalOperators(key);
|
||||
const path = schema.path(keyToSearch);
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parentSchemaTypes = [];
|
||||
const pieces = keyToSearch.split('.');
|
||||
for (let i = pieces.length - 1; i > 0; --i) {
|
||||
const s = schema.path(pieces.slice(0, i).join('.'));
|
||||
if (s != null &&
|
||||
(s.$isMongooseDocumentArray || s.$isSingleNested)) {
|
||||
parentSchemaTypes.push({ parentPath: key.split('.').slice(0, i).join('.'), parentSchemaType: s });
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(update[key]) && path.$isMongooseDocumentArray) {
|
||||
applyTimestampsToDocumentArray(update[key], path, now);
|
||||
} else if (update[key] && path.$isSingleNested) {
|
||||
applyTimestampsToSingleNested(update[key], path, now);
|
||||
} else if (parentSchemaTypes.length > 0) {
|
||||
for (const item of parentSchemaTypes) {
|
||||
const parentPath = item.parentPath;
|
||||
const parentSchemaType = item.parentSchemaType;
|
||||
const timestamps = parentSchemaType.schema.options.timestamps;
|
||||
const updatedAt = handleTimestampOption(timestamps, 'updatedAt');
|
||||
|
||||
if (!timestamps || updatedAt == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parentSchemaType.$isSingleNested) {
|
||||
// Single nested is easy
|
||||
update[parentPath + '.' + updatedAt] = now;
|
||||
} else if (parentSchemaType.$isMongooseDocumentArray) {
|
||||
let childPath = key.substr(parentPath.length + 1);
|
||||
|
||||
if (/^\d+$/.test(childPath)) {
|
||||
update[parentPath + '.' + childPath][updatedAt] = now;
|
||||
continue;
|
||||
}
|
||||
|
||||
const firstDot = childPath.indexOf('.');
|
||||
childPath = firstDot !== -1 ? childPath.substr(0, firstDot) : childPath;
|
||||
|
||||
update[parentPath + '.' + childPath + '.' + updatedAt] = now;
|
||||
}
|
||||
}
|
||||
} else if (path.schema != null && path.schema != schema && update[key]) {
|
||||
const timestamps = path.schema.options.timestamps;
|
||||
const createdAt = handleTimestampOption(timestamps, 'createdAt');
|
||||
const updatedAt = handleTimestampOption(timestamps, 'updatedAt');
|
||||
|
||||
if (!timestamps) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (updatedAt != null) {
|
||||
update[key][updatedAt] = now;
|
||||
}
|
||||
if (createdAt != null) {
|
||||
update[key][createdAt] = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
119
node_modules/mongoose/lib/helpers/update/applyTimestampsToUpdate.js
generated
vendored
Normal file
119
node_modules/mongoose/lib/helpers/update/applyTimestampsToUpdate.js
generated
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
'use strict';
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
const get = require('../get');
|
||||
|
||||
module.exports = applyTimestampsToUpdate;
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
function applyTimestampsToUpdate(now, createdAt, updatedAt, currentUpdate, options) {
|
||||
const updates = currentUpdate;
|
||||
let _updates = updates;
|
||||
const overwrite = get(options, 'overwrite', false);
|
||||
const timestamps = get(options, 'timestamps', true);
|
||||
|
||||
// Support skipping timestamps at the query level, see gh-6980
|
||||
if (!timestamps || updates == null) {
|
||||
return currentUpdate;
|
||||
}
|
||||
|
||||
const skipCreatedAt = timestamps != null && timestamps.createdAt === false;
|
||||
const skipUpdatedAt = timestamps != null && timestamps.updatedAt === false;
|
||||
|
||||
if (overwrite) {
|
||||
if (currentUpdate && currentUpdate.$set) {
|
||||
currentUpdate = currentUpdate.$set;
|
||||
updates.$set = {};
|
||||
_updates = updates.$set;
|
||||
}
|
||||
if (!skipUpdatedAt && updatedAt && !currentUpdate[updatedAt]) {
|
||||
_updates[updatedAt] = now;
|
||||
}
|
||||
if (!skipCreatedAt && createdAt && !currentUpdate[createdAt]) {
|
||||
_updates[createdAt] = now;
|
||||
}
|
||||
return updates;
|
||||
}
|
||||
currentUpdate = currentUpdate || {};
|
||||
|
||||
if (Array.isArray(updates)) {
|
||||
// Update with aggregation pipeline
|
||||
updates.push({ $set: { updatedAt: now } });
|
||||
|
||||
return updates;
|
||||
}
|
||||
|
||||
updates.$set = updates.$set || {};
|
||||
if (!skipUpdatedAt && updatedAt &&
|
||||
(!currentUpdate.$currentDate || !currentUpdate.$currentDate[updatedAt])) {
|
||||
let timestampSet = false;
|
||||
if (updatedAt.indexOf('.') !== -1) {
|
||||
const pieces = updatedAt.split('.');
|
||||
for (let i = 1; i < pieces.length; ++i) {
|
||||
const remnant = pieces.slice(-i).join('.');
|
||||
const start = pieces.slice(0, -i).join('.');
|
||||
if (currentUpdate[start] != null) {
|
||||
currentUpdate[start][remnant] = now;
|
||||
timestampSet = true;
|
||||
break;
|
||||
} else if (currentUpdate.$set && currentUpdate.$set[start]) {
|
||||
currentUpdate.$set[start][remnant] = now;
|
||||
timestampSet = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!timestampSet) {
|
||||
updates.$set[updatedAt] = now;
|
||||
}
|
||||
|
||||
if (updates.hasOwnProperty(updatedAt)) {
|
||||
delete updates[updatedAt];
|
||||
}
|
||||
}
|
||||
|
||||
if (!skipCreatedAt && createdAt) {
|
||||
if (currentUpdate[createdAt]) {
|
||||
delete currentUpdate[createdAt];
|
||||
}
|
||||
if (currentUpdate.$set && currentUpdate.$set[createdAt]) {
|
||||
delete currentUpdate.$set[createdAt];
|
||||
}
|
||||
|
||||
let timestampSet = false;
|
||||
if (createdAt.indexOf('.') !== -1) {
|
||||
const pieces = createdAt.split('.');
|
||||
for (let i = 1; i < pieces.length; ++i) {
|
||||
const remnant = pieces.slice(-i).join('.');
|
||||
const start = pieces.slice(0, -i).join('.');
|
||||
if (currentUpdate[start] != null) {
|
||||
currentUpdate[start][remnant] = now;
|
||||
timestampSet = true;
|
||||
break;
|
||||
} else if (currentUpdate.$set && currentUpdate.$set[start]) {
|
||||
currentUpdate.$set[start][remnant] = now;
|
||||
timestampSet = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!timestampSet) {
|
||||
updates.$setOnInsert = updates.$setOnInsert || {};
|
||||
updates.$setOnInsert[createdAt] = now;
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(updates.$set).length === 0) {
|
||||
delete updates.$set;
|
||||
}
|
||||
|
||||
return updates;
|
||||
}
|
||||
60
node_modules/mongoose/lib/helpers/update/castArrayFilters.js
generated
vendored
Normal file
60
node_modules/mongoose/lib/helpers/update/castArrayFilters.js
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
'use strict';
|
||||
|
||||
const castFilterPath = require('../query/castFilterPath');
|
||||
const cleanPositionalOperators = require('../schema/cleanPositionalOperators');
|
||||
const getPath = require('../schema/getPath');
|
||||
const updatedPathsByArrayFilter = require('./updatedPathsByArrayFilter');
|
||||
|
||||
module.exports = function castArrayFilters(query) {
|
||||
const arrayFilters = query.options.arrayFilters;
|
||||
if (!Array.isArray(arrayFilters)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const update = query.getUpdate();
|
||||
const schema = query.schema;
|
||||
const strictQuery = schema.options.strictQuery;
|
||||
|
||||
const updatedPathsByFilter = updatedPathsByArrayFilter(update);
|
||||
|
||||
for (const filter of arrayFilters) {
|
||||
if (filter == null) {
|
||||
throw new Error(`Got null array filter in ${arrayFilters}`);
|
||||
}
|
||||
for (const key of Object.keys(filter)) {
|
||||
if (filter[key] == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const dot = key.indexOf('.');
|
||||
let filterPath = dot === -1 ?
|
||||
updatedPathsByFilter[key] + '.0' :
|
||||
updatedPathsByFilter[key.substr(0, dot)] + '.0' + key.substr(dot);
|
||||
|
||||
if (filterPath == null) {
|
||||
throw new Error(`Filter path not found for ${key}`);
|
||||
}
|
||||
|
||||
// If there are multiple array filters in the path being updated, make sure
|
||||
// to replace them so we can get the schema path.
|
||||
filterPath = cleanPositionalOperators(filterPath);
|
||||
|
||||
const schematype = getPath(schema, filterPath);
|
||||
if (schematype == null) {
|
||||
if (!strictQuery) {
|
||||
return;
|
||||
}
|
||||
// For now, treat `strictQuery = true` and `strictQuery = 'throw'` as
|
||||
// equivalent for casting array filters. `strictQuery = true` doesn't
|
||||
// quite work in this context because we never want to silently strip out
|
||||
// array filters, even if the path isn't in the schema.
|
||||
throw new Error(`Could not find path "${filterPath}" in schema`);
|
||||
}
|
||||
if (typeof filter[key] === 'object') {
|
||||
filter[key] = castFilterPath(query, schematype, filter[key]);
|
||||
} else {
|
||||
filter[key] = schematype.castForQuery(filter[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
33
node_modules/mongoose/lib/helpers/update/modifiedPaths.js
generated
vendored
Normal file
33
node_modules/mongoose/lib/helpers/update/modifiedPaths.js
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
'use strict';
|
||||
|
||||
const _modifiedPaths = require('../common').modifiedPaths;
|
||||
|
||||
/**
|
||||
* Given an update document with potential update operators (`$set`, etc.)
|
||||
* returns an object whose keys are the directly modified paths.
|
||||
*
|
||||
* If there are any top-level keys that don't start with `$`, we assume those
|
||||
* will get wrapped in a `$set`. The Mongoose Query is responsible for wrapping
|
||||
* top-level keys in `$set`.
|
||||
*
|
||||
* @param {Object} update
|
||||
* @return {Object} modified
|
||||
*/
|
||||
|
||||
module.exports = function modifiedPaths(update) {
|
||||
const keys = Object.keys(update);
|
||||
const res = {};
|
||||
|
||||
const withoutDollarKeys = {};
|
||||
for (const key of keys) {
|
||||
if (key.startsWith('$')) {
|
||||
_modifiedPaths(update[key], '', res);
|
||||
continue;
|
||||
}
|
||||
withoutDollarKeys[key] = update[key];
|
||||
}
|
||||
|
||||
_modifiedPaths(withoutDollarKeys, '', res);
|
||||
|
||||
return res;
|
||||
};
|
||||
53
node_modules/mongoose/lib/helpers/update/moveImmutableProperties.js
generated
vendored
Normal file
53
node_modules/mongoose/lib/helpers/update/moveImmutableProperties.js
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
'use strict';
|
||||
|
||||
const get = require('../get');
|
||||
|
||||
/**
|
||||
* Given an update, move all $set on immutable properties to $setOnInsert.
|
||||
* This should only be called for upserts, because $setOnInsert bypasses the
|
||||
* strictness check for immutable properties.
|
||||
*/
|
||||
|
||||
module.exports = function moveImmutableProperties(schema, update, ctx) {
|
||||
if (update == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const keys = Object.keys(update);
|
||||
for (const key of keys) {
|
||||
const isDollarKey = key.startsWith('$');
|
||||
|
||||
if (key === '$set') {
|
||||
const updatedPaths = Object.keys(update[key]);
|
||||
for (const path of updatedPaths) {
|
||||
_walkUpdatePath(schema, update[key], path, update, ctx);
|
||||
}
|
||||
} else if (!isDollarKey) {
|
||||
_walkUpdatePath(schema, update, key, update, ctx);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
function _walkUpdatePath(schema, op, path, update, ctx) {
|
||||
const schematype = schema.path(path);
|
||||
if (schematype == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
let immutable = get(schematype, 'options.immutable', null);
|
||||
if (immutable == null) {
|
||||
return;
|
||||
}
|
||||
if (typeof immutable === 'function') {
|
||||
immutable = immutable.call(ctx, ctx);
|
||||
}
|
||||
|
||||
if (!immutable) {
|
||||
return;
|
||||
}
|
||||
|
||||
update.$setOnInsert = update.$setOnInsert || {};
|
||||
update.$setOnInsert[path] = op[path];
|
||||
delete op[path];
|
||||
}
|
||||
18
node_modules/mongoose/lib/helpers/update/removeUnusedArrayFilters.js
generated
vendored
Normal file
18
node_modules/mongoose/lib/helpers/update/removeUnusedArrayFilters.js
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* MongoDB throws an error if there's unused array filters. That is, if `options.arrayFilters` defines
|
||||
* a filter, but none of the `update` keys use it. This should be enough to filter out all unused array
|
||||
* filters.
|
||||
*/
|
||||
|
||||
module.exports = function removeUnusedArrayFilters(update, arrayFilters) {
|
||||
const updateKeys = Object.keys(update).map(key => Object.keys(update[key])).reduce((cur, arr) => cur.concat(arr), []);
|
||||
return arrayFilters.filter(obj => {
|
||||
const firstKey = Object.keys(obj)[0];
|
||||
const firstDot = firstKey.indexOf('.');
|
||||
const arrayFilterKey = firstDot === -1 ? firstKey : firstKey.slice(0, firstDot);
|
||||
|
||||
return updateKeys.find(key => key.includes('$[' + arrayFilterKey + ']')) != null;
|
||||
});
|
||||
};
|
||||
24
node_modules/mongoose/lib/helpers/update/updatedPathsByArrayFilter.js
generated
vendored
Normal file
24
node_modules/mongoose/lib/helpers/update/updatedPathsByArrayFilter.js
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
'use strict';
|
||||
|
||||
const modifiedPaths = require('./modifiedPaths');
|
||||
|
||||
module.exports = function updatedPathsByArrayFilter(update) {
|
||||
const updatedPaths = modifiedPaths(update);
|
||||
|
||||
return Object.keys(updatedPaths).reduce((cur, path) => {
|
||||
const matches = path.match(/\$\[[^\]]+\]/g);
|
||||
if (matches == null) {
|
||||
return cur;
|
||||
}
|
||||
for (const match of matches) {
|
||||
const firstMatch = path.indexOf(match);
|
||||
if (firstMatch !== path.lastIndexOf(match)) {
|
||||
throw new Error(`Path '${path}' contains the same array filter multiple times`);
|
||||
}
|
||||
cur[match.substring(2, match.length - 1)] = path.
|
||||
substr(0, firstMatch - 1).
|
||||
replace(/\$\[[^\]]+\]/g, '0');
|
||||
}
|
||||
return cur;
|
||||
}, {});
|
||||
};
|
||||
257
node_modules/mongoose/lib/helpers/updateValidators.js
generated
vendored
Normal file
257
node_modules/mongoose/lib/helpers/updateValidators.js
generated
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
'use strict';
|
||||
|
||||
/*!
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
const ValidationError = require('../error/validation');
|
||||
const cleanPositionalOperators = require('./schema/cleanPositionalOperators');
|
||||
const flatten = require('./common').flatten;
|
||||
const modifiedPaths = require('./common').modifiedPaths;
|
||||
|
||||
/**
|
||||
* Applies validators and defaults to update and findOneAndUpdate operations,
|
||||
* specifically passing a null doc as `this` to validators and defaults
|
||||
*
|
||||
* @param {Query} query
|
||||
* @param {Schema} schema
|
||||
* @param {Object} castedDoc
|
||||
* @param {Object} options
|
||||
* @method runValidatorsOnUpdate
|
||||
* @api private
|
||||
*/
|
||||
|
||||
module.exports = function(query, schema, castedDoc, options, callback) {
|
||||
let _keys;
|
||||
const keys = Object.keys(castedDoc || {});
|
||||
let updatedKeys = {};
|
||||
let updatedValues = {};
|
||||
const isPull = {};
|
||||
const arrayAtomicUpdates = {};
|
||||
const numKeys = keys.length;
|
||||
let hasDollarUpdate = false;
|
||||
const modified = {};
|
||||
let currentUpdate;
|
||||
let key;
|
||||
let i;
|
||||
|
||||
for (i = 0; i < numKeys; ++i) {
|
||||
if (keys[i].startsWith('$')) {
|
||||
hasDollarUpdate = true;
|
||||
if (keys[i] === '$push' || keys[i] === '$addToSet') {
|
||||
_keys = Object.keys(castedDoc[keys[i]]);
|
||||
for (let ii = 0; ii < _keys.length; ++ii) {
|
||||
currentUpdate = castedDoc[keys[i]][_keys[ii]];
|
||||
if (currentUpdate && currentUpdate.$each) {
|
||||
arrayAtomicUpdates[_keys[ii]] = (arrayAtomicUpdates[_keys[ii]] || []).
|
||||
concat(currentUpdate.$each);
|
||||
} else {
|
||||
arrayAtomicUpdates[_keys[ii]] = (arrayAtomicUpdates[_keys[ii]] || []).
|
||||
concat([currentUpdate]);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
modifiedPaths(castedDoc[keys[i]], '', modified);
|
||||
const flat = flatten(castedDoc[keys[i]], null, null, schema);
|
||||
const paths = Object.keys(flat);
|
||||
const numPaths = paths.length;
|
||||
for (let j = 0; j < numPaths; ++j) {
|
||||
const updatedPath = cleanPositionalOperators(paths[j]);
|
||||
key = keys[i];
|
||||
// With `$pull` we might flatten `$in`. Skip stuff nested under `$in`
|
||||
// for the rest of the logic, it will get handled later.
|
||||
if (updatedPath.includes('$')) {
|
||||
continue;
|
||||
}
|
||||
if (key === '$set' || key === '$setOnInsert' ||
|
||||
key === '$pull' || key === '$pullAll') {
|
||||
updatedValues[updatedPath] = flat[paths[j]];
|
||||
isPull[updatedPath] = key === '$pull' || key === '$pullAll';
|
||||
} else if (key === '$unset') {
|
||||
updatedValues[updatedPath] = undefined;
|
||||
}
|
||||
updatedKeys[updatedPath] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasDollarUpdate) {
|
||||
modifiedPaths(castedDoc, '', modified);
|
||||
updatedValues = flatten(castedDoc, null, null, schema);
|
||||
updatedKeys = Object.keys(updatedValues);
|
||||
}
|
||||
|
||||
const updates = Object.keys(updatedValues);
|
||||
const numUpdates = updates.length;
|
||||
const validatorsToExecute = [];
|
||||
const validationErrors = [];
|
||||
|
||||
const alreadyValidated = [];
|
||||
|
||||
const context = options && options.context === 'query' ? query : null;
|
||||
function iter(i, v) {
|
||||
const schemaPath = schema._getSchema(updates[i]);
|
||||
if (schemaPath == null) {
|
||||
return;
|
||||
}
|
||||
if (schemaPath.instance === 'Mixed' && schemaPath.path !== updates[i]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (v && Array.isArray(v.$in)) {
|
||||
v.$in.forEach((v, i) => {
|
||||
validatorsToExecute.push(function(callback) {
|
||||
schemaPath.doValidate(
|
||||
v,
|
||||
function(err) {
|
||||
if (err) {
|
||||
err.path = updates[i] + '.$in.' + i;
|
||||
validationErrors.push(err);
|
||||
}
|
||||
callback(null);
|
||||
},
|
||||
context,
|
||||
{ updateValidator: true });
|
||||
});
|
||||
});
|
||||
} else {
|
||||
if (isPull[updates[i]] &&
|
||||
schemaPath.$isMongooseArray) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (schemaPath.$isMongooseDocumentArrayElement && v != null && v.$__ != null) {
|
||||
alreadyValidated.push(updates[i]);
|
||||
validatorsToExecute.push(function(callback) {
|
||||
schemaPath.doValidate(v, function(err) {
|
||||
if (err) {
|
||||
err.path = updates[i];
|
||||
validationErrors.push(err);
|
||||
return callback(null);
|
||||
}
|
||||
|
||||
v.validate(function(err) {
|
||||
if (err) {
|
||||
if (err.errors) {
|
||||
for (const key of Object.keys(err.errors)) {
|
||||
const _err = err.errors[key];
|
||||
_err.path = updates[i] + '.' + key;
|
||||
validationErrors.push(_err);
|
||||
}
|
||||
} else {
|
||||
err.path = updates[i];
|
||||
validationErrors.push(err);
|
||||
}
|
||||
}
|
||||
callback(null);
|
||||
});
|
||||
}, context, { updateValidator: true });
|
||||
});
|
||||
} else {
|
||||
validatorsToExecute.push(function(callback) {
|
||||
for (const path of alreadyValidated) {
|
||||
if (updates[i].startsWith(path + '.')) {
|
||||
return callback(null);
|
||||
}
|
||||
}
|
||||
|
||||
schemaPath.doValidate(v, function(err) {
|
||||
if (schemaPath.schema != null &&
|
||||
schemaPath.schema.options.storeSubdocValidationError === false &&
|
||||
err instanceof ValidationError) {
|
||||
return callback(null);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
err.path = updates[i];
|
||||
validationErrors.push(err);
|
||||
}
|
||||
callback(null);
|
||||
}, context, { updateValidator: true });
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = 0; i < numUpdates; ++i) {
|
||||
iter(i, updatedValues[updates[i]]);
|
||||
}
|
||||
|
||||
const arrayUpdates = Object.keys(arrayAtomicUpdates);
|
||||
for (const arrayUpdate of arrayUpdates) {
|
||||
let schemaPath = schema._getSchema(arrayUpdate);
|
||||
if (schemaPath && schemaPath.$isMongooseDocumentArray) {
|
||||
validatorsToExecute.push(function(callback) {
|
||||
schemaPath.doValidate(
|
||||
arrayAtomicUpdates[arrayUpdate],
|
||||
getValidationCallback(arrayUpdate, validationErrors, callback),
|
||||
options && options.context === 'query' ? query : null);
|
||||
});
|
||||
} else {
|
||||
schemaPath = schema._getSchema(arrayUpdate + '.0');
|
||||
for (const atomicUpdate of arrayAtomicUpdates[arrayUpdate]) {
|
||||
validatorsToExecute.push(function(callback) {
|
||||
schemaPath.doValidate(
|
||||
atomicUpdate,
|
||||
getValidationCallback(arrayUpdate, validationErrors, callback),
|
||||
options && options.context === 'query' ? query : null,
|
||||
{ updateValidator: true });
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (callback != null) {
|
||||
let numValidators = validatorsToExecute.length;
|
||||
if (numValidators === 0) {
|
||||
return _done(callback);
|
||||
}
|
||||
for (const validator of validatorsToExecute) {
|
||||
validator(function() {
|
||||
if (--numValidators <= 0) {
|
||||
_done(callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return function(callback) {
|
||||
let numValidators = validatorsToExecute.length;
|
||||
if (numValidators === 0) {
|
||||
return _done(callback);
|
||||
}
|
||||
for (const validator of validatorsToExecute) {
|
||||
validator(function() {
|
||||
if (--numValidators <= 0) {
|
||||
_done(callback);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function _done(callback) {
|
||||
if (validationErrors.length) {
|
||||
const err = new ValidationError(null);
|
||||
|
||||
for (const validationError of validationErrors) {
|
||||
err.addError(validationError.path, validationError);
|
||||
}
|
||||
|
||||
return callback(err);
|
||||
}
|
||||
callback(null);
|
||||
}
|
||||
|
||||
function getValidationCallback(arrayUpdate, validationErrors, callback) {
|
||||
return function(err) {
|
||||
if (err) {
|
||||
err.path = arrayUpdate;
|
||||
validationErrors.push(err);
|
||||
}
|
||||
callback(null);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user