summaryrefslogtreecommitdiffstats
path: root/node_modules/mongoose/lib/helpers/populate
diff options
context:
space:
mode:
authorGravatar Piotr Russ <mail@pruss.it> 2020-11-16 00:10:28 +0100
committerGravatar Piotr Russ <mail@pruss.it> 2020-11-16 00:10:28 +0100
commite06ec920f7a5d784e674c4c4b4e6d1da3dc7391d (patch)
tree55713f725f77b44ebfec86e4eec3ce33e71458ca /node_modules/mongoose/lib/helpers/populate
downloadwebsite_creator-e06ec920f7a5d784e674c4c4b4e6d1da3dc7391d.tar.gz
website_creator-e06ec920f7a5d784e674c4c4b4e6d1da3dc7391d.tar.bz2
website_creator-e06ec920f7a5d784e674c4c4b4e6d1da3dc7391d.zip
api, login, auth
Diffstat (limited to 'node_modules/mongoose/lib/helpers/populate')
-rw-r--r--node_modules/mongoose/lib/helpers/populate/SkipPopulateValue.js10
-rw-r--r--node_modules/mongoose/lib/helpers/populate/assignRawDocsToIdStructure.js98
-rw-r--r--node_modules/mongoose/lib/helpers/populate/assignVals.js274
-rw-r--r--node_modules/mongoose/lib/helpers/populate/getModelsMapForPopulate.js522
-rw-r--r--node_modules/mongoose/lib/helpers/populate/getSchemaTypes.js198
-rw-r--r--node_modules/mongoose/lib/helpers/populate/getVirtual.js72
-rw-r--r--node_modules/mongoose/lib/helpers/populate/leanPopulateMap.js7
-rw-r--r--node_modules/mongoose/lib/helpers/populate/lookupLocalFields.js26
-rw-r--r--node_modules/mongoose/lib/helpers/populate/normalizeRefPath.js45
-rw-r--r--node_modules/mongoose/lib/helpers/populate/removeDeselectedForeignField.js31
-rw-r--r--node_modules/mongoose/lib/helpers/populate/validateRef.js19
11 files changed, 1302 insertions, 0 deletions
diff --git a/node_modules/mongoose/lib/helpers/populate/SkipPopulateValue.js b/node_modules/mongoose/lib/helpers/populate/SkipPopulateValue.js
new file mode 100644
index 0000000..5d46cfd
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/populate/SkipPopulateValue.js
@@ -0,0 +1,10 @@
+'use strict';
+
+module.exports = function SkipPopulateValue(val) {
+ if (!(this instanceof SkipPopulateValue)) {
+ return new SkipPopulateValue(val);
+ }
+
+ this.val = val;
+ return this;
+}; \ No newline at end of file
diff --git a/node_modules/mongoose/lib/helpers/populate/assignRawDocsToIdStructure.js b/node_modules/mongoose/lib/helpers/populate/assignRawDocsToIdStructure.js
new file mode 100644
index 0000000..843c148
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/populate/assignRawDocsToIdStructure.js
@@ -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;
+ });
+ }
+} \ No newline at end of file
diff --git a/node_modules/mongoose/lib/helpers/populate/assignVals.js b/node_modules/mongoose/lib/helpers/populate/assignVals.js
new file mode 100644
index 0000000..9fd51d8
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/populate/assignVals.js
@@ -0,0 +1,274 @@
+'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 = get(o, 'allOptions.options.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
+ 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;
+
+ function setValue(val) {
+ if (count) {
+ return val;
+ }
+ if (val instanceof SkipPopulateValue) {
+ return val.val;
+ }
+
+ 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, populatedModel);
+ } else if (o.justOne === false && !Array.isArray(val)) {
+ return valueFilter([val], options, populateOptions, populatedModel);
+ }
+ return valueFilter(val, options, populateOptions, populatedModel);
+ }
+
+ for (let i = 0; i < docs.length; ++i) {
+ const existingVal = mpath.get(o.path, docs[i], lookupLocalFields);
+ if (existingVal == null && !getVirtual(o.originalModel.schema, o.path)) {
+ continue;
+ }
+
+ let valueToSet;
+ if (count) {
+ valueToSet = numDocs(rawIds[i]);
+ } else if (Array.isArray(o.match)) {
+ valueToSet = Array.isArray(rawIds[i]) ?
+ sift(o.match[i], rawIds[i]) :
+ sift(o.match[i], [rawIds[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(o.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(o.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(o.path, valueToSet, docs[i], setValue);
+ continue;
+ }
+
+ const parts = o.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) {
+ return;
+ }
+ 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') {
+ return;
+ }
+ }
+ if (docs[i].$__) {
+ docs[i].populated(o.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(o.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) {
+ if (Array.isArray(val)) {
+ // find logic
+ const ret = [];
+ const numValues = val.length;
+ for (let i = 0; i < numValues; ++i) {
+ const subdoc = val[i];
+ if (!isPopulatedObject(subdoc) && (!populateOptions.retainNullValues || subdoc != null)) {
+ continue;
+ }
+ 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)) {
+ maybeRemoveId(val, assignmentOpts);
+ return val;
+ }
+
+ if (val instanceof Map) {
+ return val;
+ }
+
+ if (populateOptions.justOne === true) {
+ return (val == null ? val : null);
+ }
+ if (populateOptions.justOne === false) {
+ return [];
+ }
+ return val;
+}
+
+/*!
+ * 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);
+} \ No newline at end of file
diff --git a/node_modules/mongoose/lib/helpers/populate/getModelsMapForPopulate.js b/node_modules/mongoose/lib/helpers/populate/getModelsMapForPopulate.js
new file mode 100644
index 0000000..66baf19
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/populate/getModelsMapForPopulate.js
@@ -0,0 +1,522 @@
+'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;
+ let modelForFindSchema;
+
+ 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 (modelSchema.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;
+
+ 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;
+ } 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 {
+ 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.
+ 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);
+ }
+
+ 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;
+
+ let match = get(options, 'match', null) ||
+ get(currentOptions, 'match', null) ||
+ get(options, 'virtual.options.match', null) ||
+ get(options, 'virtual.options.options.match', null);
+
+ const hasMatchFunction = typeof match === 'function';
+ if (hasMatchFunction) {
+ match = match.call(doc, doc);
+ }
+
+ // 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);
+ }
+ 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;
+
+ if (!schema && discriminatorKey) {
+ modelForFindSchema = utils.getValue(discriminatorKey, doc);
+ if (modelForFindSchema) {
+ // `modelForFindSchema` is the discriminator value, so we might need
+ // find the discriminated model name
+ const discriminatorModel = getDiscriminatorByValue(model, modelForFindSchema);
+ if (discriminatorModel != null) {
+ modelForCurrentDoc = discriminatorModel;
+ } else {
+ try {
+ modelForCurrentDoc = model.db.model(modelForFindSchema);
+ } 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 (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().cast(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;
+} \ No newline at end of file
diff --git a/node_modules/mongoose/lib/helpers/populate/getSchemaTypes.js b/node_modules/mongoose/lib/helpers/populate/getSchemaTypes.js
new file mode 100644
index 0000000..15660df
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/populate/getSchemaTypes.js
@@ -0,0 +1,198 @@
+'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) {
+ if (doc == null || keys.indexOf(discriminator) !== -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.
+ // Ignore discriminators.
+ let type = foundschema;
+ while (type.$isMongooseArray && !type.$isMongooseDocumentArray) {
+ type = type.casterConstructor;
+ }
+ return search(
+ parts.slice(p),
+ type.schema,
+ null,
+ nestedPath.concat(parts.slice(0, p))
+ );
+ }
+ }
+
+ 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) {
+ 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, []);
+};
diff --git a/node_modules/mongoose/lib/helpers/populate/getVirtual.js b/node_modules/mongoose/lib/helpers/populate/getVirtual.js
new file mode 100644
index 0000000..fc1641d
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/populate/getVirtual.js
@@ -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;
+ }
+}
diff --git a/node_modules/mongoose/lib/helpers/populate/leanPopulateMap.js b/node_modules/mongoose/lib/helpers/populate/leanPopulateMap.js
new file mode 100644
index 0000000..a333124
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/populate/leanPopulateMap.js
@@ -0,0 +1,7 @@
+'use strict';
+
+/*!
+ * ignore
+ */
+
+module.exports = new WeakMap(); \ No newline at end of file
diff --git a/node_modules/mongoose/lib/helpers/populate/lookupLocalFields.js b/node_modules/mongoose/lib/helpers/populate/lookupLocalFields.js
new file mode 100644
index 0000000..08ed763
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/populate/lookupLocalFields.js
@@ -0,0 +1,26 @@
+'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) {
+ 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];
+}; \ No newline at end of file
diff --git a/node_modules/mongoose/lib/helpers/populate/normalizeRefPath.js b/node_modules/mongoose/lib/helpers/populate/normalizeRefPath.js
new file mode 100644
index 0000000..233b741
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/populate/normalizeRefPath.js
@@ -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;
+}; \ No newline at end of file
diff --git a/node_modules/mongoose/lib/helpers/populate/removeDeselectedForeignField.js b/node_modules/mongoose/lib/helpers/populate/removeDeselectedForeignField.js
new file mode 100644
index 0000000..39b893a
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/populate/removeDeselectedForeignField.js
@@ -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);
+ }
+ }
+ }
+}; \ No newline at end of file
diff --git a/node_modules/mongoose/lib/helpers/populate/validateRef.js b/node_modules/mongoose/lib/helpers/populate/validateRef.js
new file mode 100644
index 0000000..9dc2b6f
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/populate/validateRef.js
@@ -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 }));
+} \ No newline at end of file