mirror of
https://github.com/JonasunderscoreJones/jonas_jones-api.git
synced 2025-10-24 01:29:19 +02:00
some progress
This commit is contained in:
parent
aea93a5527
commit
e3c15bd288
1388 changed files with 306946 additions and 68323 deletions
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;
|
||||
};
|
||||
125
node_modules/mongoose/lib/helpers/populate/assignRawDocsToIdStructure.js
generated
vendored
Normal file
125
node_modules/mongoose/lib/helpers/populate/assignRawDocsToIdStructure.js
generated
vendored
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
'use strict';
|
||||
|
||||
const clone = require('../../helpers/clone');
|
||||
const leanPopulateMap = require('./leanPopulateMap');
|
||||
const modelSymbol = require('../symbols').modelSymbol;
|
||||
const utils = require('../../utils');
|
||||
|
||||
module.exports = assignRawDocsToIdStructure;
|
||||
|
||||
const kHasArray = Symbol('assignRawDocsToIdStructure.hasArray');
|
||||
|
||||
/**
|
||||
* 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} resultDocs
|
||||
* @param {Array} resultOrder
|
||||
* @param {Object} options
|
||||
* @param {Boolean} recursed
|
||||
* @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;
|
||||
|
||||
if (utils.isMongooseArray(rawIds)) {
|
||||
rawIds = rawIds.__array;
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
const len = rawIds.length;
|
||||
|
||||
if (sorting && recursed && options[kHasArray] === undefined) {
|
||||
options[kHasArray] = false;
|
||||
for (const key in resultOrder) {
|
||||
if (Array.isArray(resultOrder[key])) {
|
||||
options[kHasArray] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < len; ++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 === false) {
|
||||
// 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 = clone(doc);
|
||||
leanPopulateMap.set(doc, _model);
|
||||
} else {
|
||||
doc = doc.constructor.hydrate(doc._doc);
|
||||
}
|
||||
}
|
||||
|
||||
if (recursed) {
|
||||
if (doc) {
|
||||
if (sorting) {
|
||||
const _resultOrder = resultOrder[sid];
|
||||
if (options[kHasArray]) {
|
||||
// If result arrays, rely on the MongoDB server response for ordering
|
||||
newOrder.push(doc);
|
||||
} else {
|
||||
newOrder[_resultOrder] = 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;
|
||||
});
|
||||
}
|
||||
}
|
||||
341
node_modules/mongoose/lib/helpers/populate/assignVals.js
generated
vendored
Normal file
341
node_modules/mongoose/lib/helpers/populate/assignVals.js
generated
vendored
Normal file
|
|
@ -0,0 +1,341 @@
|
|||
'use strict';
|
||||
|
||||
const MongooseMap = require('../../types/map');
|
||||
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 markArraySubdocsPopulated = require('./markArraySubdocsPopulated');
|
||||
const mpath = require('mpath');
|
||||
const sift = require('sift').default;
|
||||
const utils = require('../../utils');
|
||||
const { populateModelSymbol } = require('../symbols');
|
||||
|
||||
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;
|
||||
}
|
||||
if (val === void 0) {
|
||||
return val;
|
||||
}
|
||||
|
||||
const _allIds = o.allIds[i];
|
||||
|
||||
if (o.path.endsWith('.$*')) {
|
||||
// Skip maps re: gh-12494
|
||||
return valueFilter(val, options, populateOptions, _allIds);
|
||||
}
|
||||
|
||||
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
|
||||
if (Array.isArray(valueToSet)) {
|
||||
valueToSet = valueToSet.map(v => v == null ? void 0 : v);
|
||||
}
|
||||
mpath.set(_path, valueToSet, docs[i], void 0, setValue, false);
|
||||
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].$__) {
|
||||
o.allOptions.options[populateModelSymbol] = o.allOptions.model;
|
||||
docs[i].$populated(_path, o.unpopulatedValues[i], o.allOptions.options);
|
||||
|
||||
if (valueToSet != null && valueToSet.$__ != null) {
|
||||
valueToSet.$__.wasPopulated = { value: o.unpopulatedValues[i] };
|
||||
}
|
||||
|
||||
if (valueToSet instanceof Map && !valueToSet.$isMongooseMap) {
|
||||
valueToSet = new MongooseMap(valueToSet, _path, docs[i], docs[i].schema.path(_path).$__schemaType);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
if (docs[i].$__) {
|
||||
markArraySubdocsPopulated(docs[i], [o.allOptions.options]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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) || el === null)) {
|
||||
return v.map(el => {
|
||||
if (el == null) {
|
||||
return 0;
|
||||
}
|
||||
if (Array.isArray(el)) {
|
||||
return el.filter(el => el != null).length;
|
||||
}
|
||||
return 1;
|
||||
});
|
||||
}
|
||||
return v.filter(el => el != null).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.
|
||||
* @param {Any} val
|
||||
* @param {Object} assignmentOpts
|
||||
* @param {Object} populateOptions
|
||||
* @param {Function} [populateOptions.transform]
|
||||
* @param {Boolean} allIds
|
||||
* @api private
|
||||
*/
|
||||
|
||||
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 (!populateOptions.retainNullValues && subdoc == null) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
const rLen = ret.length;
|
||||
// Since we don't want to have to create a new mongoosearray, make sure to
|
||||
// modify the array in place
|
||||
while (val.length > rLen) {
|
||||
Array.prototype.pop.apply(val, []);
|
||||
}
|
||||
let i = 0;
|
||||
if (utils.isMongooseArray(val)) {
|
||||
for (i = 0; i < rLen; ++i) {
|
||||
val.set(i, ret[i], true);
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < rLen; ++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
|
||||
* @param {Document} subdoc
|
||||
* @param {Object} assignmentOpts
|
||||
* @api private
|
||||
*/
|
||||
|
||||
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.
|
||||
* @param {Any} obj
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function isPopulatedObject(obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Array.isArray(obj) ||
|
||||
obj.$isMongooseMap ||
|
||||
obj.$__ != null ||
|
||||
leanPopulateMap.has(obj);
|
||||
}
|
||||
|
||||
function noop(v) {
|
||||
return v;
|
||||
}
|
||||
97
node_modules/mongoose/lib/helpers/populate/createPopulateQueryFilter.js
generated
vendored
Normal file
97
node_modules/mongoose/lib/helpers/populate/createPopulateQueryFilter.js
generated
vendored
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
'use strict';
|
||||
|
||||
const SkipPopulateValue = require('./SkipPopulateValue');
|
||||
const parentPaths = require('../path/parentPaths');
|
||||
const { trusted } = require('../query/trusted');
|
||||
const hasDollarKeys = require('../query/hasDollarKeys');
|
||||
|
||||
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] = trusted({ $in: ids });
|
||||
} else if (foreignField === '_id' && match['_id']) {
|
||||
const userSpecifiedMatch = hasDollarKeys(match[foreignField]) ?
|
||||
match[foreignField] :
|
||||
{ $eq: match[foreignField] };
|
||||
match[foreignField] = { ...trusted({ $in: ids }), ...userSpecifiedMatch };
|
||||
}
|
||||
|
||||
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)] = trusted({ $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 } });
|
||||
} else if (foreignField === '_id' && match['_id']) {
|
||||
const userSpecifiedMatch = hasDollarKeys(match[foreignField]) ?
|
||||
match[foreignField] :
|
||||
{ $eq: match[foreignField] };
|
||||
match[foreignField] = { ...trusted({ $in: ids }), ...userSpecifiedMatch };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return match;
|
||||
};
|
||||
|
||||
/**
|
||||
* Optionally filter out invalid ids that don't conform to foreign field's schema
|
||||
* to avoid cast errors (gh-7706)
|
||||
* @param {Array} ids
|
||||
* @param {SchemaType} foreignSchemaType
|
||||
* @param {Boolean} [skipInvalidIds]
|
||||
* @api private
|
||||
*/
|
||||
|
||||
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
|
||||
* @param {Array|Any} match
|
||||
* @api private
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
733
node_modules/mongoose/lib/helpers/populate/getModelsMapForPopulate.js
generated
vendored
Normal file
733
node_modules/mongoose/lib/helpers/populate/getModelsMapForPopulate.js
generated
vendored
Normal file
|
|
@ -0,0 +1,733 @@
|
|||
'use strict';
|
||||
|
||||
const MongooseError = require('../../error/index');
|
||||
const SkipPopulateValue = require('./SkipPopulateValue');
|
||||
const clone = require('../clone');
|
||||
const get = require('../get');
|
||||
const getDiscriminatorByValue = require('../discriminator/getDiscriminatorByValue');
|
||||
const getConstructorName = require('../getConstructorName');
|
||||
const getSchemaTypes = require('./getSchemaTypes');
|
||||
const getVirtual = require('./getVirtual');
|
||||
const lookupLocalFields = require('./lookupLocalFields');
|
||||
const mpath = require('mpath');
|
||||
const modelNamesFromRefPath = require('./modelNamesFromRefPath');
|
||||
const utils = require('../../utils');
|
||||
|
||||
const modelSymbol = require('../symbols').modelSymbol;
|
||||
const populateModelSymbol = require('../symbols').populateModelSymbol;
|
||||
const schemaMixedSymbol = require('../../schema/symbols').schemaMixedSymbol;
|
||||
const StrictPopulate = require('../../error/strictPopulate');
|
||||
|
||||
module.exports = function getModelsMapForPopulate(model, docs, options) {
|
||||
let doc;
|
||||
const len = docs.length;
|
||||
const map = [];
|
||||
const modelNameFromQuery = options.model && options.model.modelName || options.model;
|
||||
let schema;
|
||||
let refPath;
|
||||
let modelNames;
|
||||
const available = {};
|
||||
|
||||
const modelSchema = model.schema;
|
||||
|
||||
// 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]) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const _virtualRes = getVirtual(model.schema, options.path);
|
||||
const virtual = _virtualRes == null ? null : _virtualRes.virtual;
|
||||
if (virtual != null) {
|
||||
return _virtualPopulate(model, docs, options, _virtualRes);
|
||||
}
|
||||
|
||||
let allSchemaTypes = getSchemaTypes(model, modelSchema, null, options.path);
|
||||
allSchemaTypes = Array.isArray(allSchemaTypes) ? allSchemaTypes : [allSchemaTypes].filter(v => v != null);
|
||||
|
||||
if (allSchemaTypes.length === 0 && options.strictPopulate !== false && options._localModel != null) {
|
||||
return new StrictPopulate(options._fullPath || options.path);
|
||||
}
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
doc = docs[i];
|
||||
let justOne = null;
|
||||
|
||||
const docSchema = doc != null && doc.$__ != null ? doc.$__schema : modelSchema;
|
||||
schema = getSchemaTypes(model, docSchema, 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;
|
||||
}
|
||||
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 = false;
|
||||
let normalizedRefPath = null;
|
||||
let schemaOptions = null;
|
||||
let modelNamesInOrder = null;
|
||||
|
||||
if (schema != null && schema.instance === 'Embedded') {
|
||||
if (schema.options.ref) {
|
||||
const data = {
|
||||
localField: options.path + '._id',
|
||||
foreignField: '_id',
|
||||
justOne: true
|
||||
};
|
||||
const res = _getModelNames(doc, schema, modelNameFromQuery, model);
|
||||
|
||||
const unpopulatedValue = mpath.get(options.path, doc);
|
||||
const id = mpath.get('_id', unpopulatedValue);
|
||||
addModelNamesToMap(model, map, available, res.modelNames, options, data, id, doc, schemaOptions, unpopulatedValue);
|
||||
}
|
||||
// No-op if no `ref` set. See gh-11538
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Array.isArray(schema)) {
|
||||
const schemasArray = schema;
|
||||
for (const _schema of schemasArray) {
|
||||
let _modelNames;
|
||||
let res;
|
||||
try {
|
||||
res = _getModelNames(doc, _schema, modelNameFromQuery, model);
|
||||
_modelNames = res.modelNames;
|
||||
isRefPath = isRefPath || res.isRefPath;
|
||||
normalizedRefPath = normalizedRefPath || 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, modelNameFromQuery, model);
|
||||
modelNames = res.modelNames;
|
||||
isRefPath = res.isRefPath;
|
||||
normalizedRefPath = normalizedRefPath || res.refPath;
|
||||
justOne = res.justOne;
|
||||
schemaOptions = get(schema, 'options.populate', null);
|
||||
// Dedupe, because `refPath` can return duplicates of the same model name,
|
||||
// and that causes perf issues.
|
||||
if (isRefPath) {
|
||||
modelNamesInOrder = modelNames;
|
||||
modelNames = Array.from(new Set(modelNames));
|
||||
}
|
||||
} catch (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!modelNames) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const data = {};
|
||||
const localField = options.path;
|
||||
const 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 (schema && !schema[schemaMixedSymbol]) {
|
||||
// Skip Mixed types because we explicitly don't do casting on those.
|
||||
if (options.path.endsWith('.' + schema.path) || options.path === schema.path) {
|
||||
justOne = Array.isArray(schema) ?
|
||||
schema.every(schema => !schema.$isMongooseArray) :
|
||||
!schema.$isMongooseArray;
|
||||
}
|
||||
}
|
||||
|
||||
if (!modelNames) {
|
||||
continue;
|
||||
}
|
||||
|
||||
data.isVirtual = false;
|
||||
data.justOne = justOne;
|
||||
data.localField = localField;
|
||||
data.foreignField = foreignField;
|
||||
|
||||
// Get local fields
|
||||
const ret = _getLocalFieldValues(doc, localField, model, options, null, schema);
|
||||
|
||||
const id = String(utils.getValue(foreignField, doc));
|
||||
options._docs[id] = Array.isArray(ret) ? ret.slice() : ret;
|
||||
|
||||
let match = get(options, 'match', null);
|
||||
|
||||
const hasMatchFunction = typeof match === 'function';
|
||||
if (hasMatchFunction) {
|
||||
match = match.call(doc, doc);
|
||||
}
|
||||
data.match = match;
|
||||
data.hasMatchFunction = hasMatchFunction;
|
||||
data.isRefPath = isRefPath;
|
||||
data.modelNamesInOrder = modelNamesInOrder;
|
||||
|
||||
if (isRefPath) {
|
||||
const embeddedDiscriminatorModelNames = _findRefPathForDiscriminators(doc,
|
||||
modelSchema, data, options, normalizedRefPath, ret);
|
||||
|
||||
modelNames = embeddedDiscriminatorModelNames || modelNames;
|
||||
}
|
||||
|
||||
try {
|
||||
addModelNamesToMap(model, map, available, modelNames, options, data, ret, doc, schemaOptions);
|
||||
} catch (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return map;
|
||||
|
||||
function _getModelNames(doc, schema, modelNameFromQuery, model) {
|
||||
let modelNames;
|
||||
let isRefPath = false;
|
||||
let justOne = null;
|
||||
|
||||
const originalSchema = schema;
|
||||
if (schema && schema.instance === 'Array') {
|
||||
schema = schema.caster;
|
||||
}
|
||||
if (schema && schema.$isSchemaMap) {
|
||||
schema = schema.$__schemaType;
|
||||
}
|
||||
|
||||
const ref = schema && schema.options && schema.options.ref;
|
||||
refPath = schema && schema.options && schema.options.refPath;
|
||||
if (schema != null &&
|
||||
schema[schemaMixedSymbol] &&
|
||||
!ref &&
|
||||
!refPath &&
|
||||
!modelNameFromQuery) {
|
||||
return { modelNames: null };
|
||||
}
|
||||
|
||||
if (modelNameFromQuery) {
|
||||
modelNames = [modelNameFromQuery]; // query options
|
||||
} else if (refPath != null) {
|
||||
if (typeof refPath === 'function') {
|
||||
const subdocPath = options.path.slice(0, options.path.length - schema.path.length - 1);
|
||||
const vals = mpath.get(subdocPath, doc, lookupLocalFields);
|
||||
const subdocsBeingPopulated = Array.isArray(vals) ?
|
||||
utils.array.flatten(vals) :
|
||||
(vals ? [vals] : []);
|
||||
|
||||
modelNames = new Set();
|
||||
for (const subdoc of subdocsBeingPopulated) {
|
||||
refPath = refPath.call(subdoc, subdoc, options.path);
|
||||
modelNamesFromRefPath(refPath, doc, options.path, modelSchema, options._queryProjection).
|
||||
forEach(name => modelNames.add(name));
|
||||
}
|
||||
modelNames = Array.from(modelNames);
|
||||
} else {
|
||||
modelNames = modelNamesFromRefPath(refPath, doc, options.path, modelSchema, options._queryProjection);
|
||||
}
|
||||
|
||||
isRefPath = true;
|
||||
} else {
|
||||
let ref;
|
||||
let refPath;
|
||||
let schemaForCurrentDoc;
|
||||
let discriminatorValue;
|
||||
let modelForCurrentDoc = model;
|
||||
const discriminatorKey = model.schema.options.discriminatorKey;
|
||||
|
||||
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 = _getModelFromConn(model.db, discriminatorValue);
|
||||
} catch (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
schemaForCurrentDoc = modelForCurrentDoc.schema._getSchema(options.path);
|
||||
|
||||
if (schemaForCurrentDoc && schemaForCurrentDoc.caster) {
|
||||
schemaForCurrentDoc = schemaForCurrentDoc.caster;
|
||||
}
|
||||
} else {
|
||||
schemaForCurrentDoc = schema;
|
||||
}
|
||||
|
||||
if (originalSchema && originalSchema.path.endsWith('.$*')) {
|
||||
justOne = !originalSchema.$isMongooseArray && !originalSchema._arrayPath;
|
||||
} else if (schemaForCurrentDoc != null) {
|
||||
justOne = !schemaForCurrentDoc.$isMongooseArray && !schemaForCurrentDoc._arrayPath;
|
||||
}
|
||||
|
||||
if ((ref = get(schemaForCurrentDoc, 'options.ref')) != null) {
|
||||
if (schemaForCurrentDoc != null &&
|
||||
typeof ref === 'function' &&
|
||||
options.path.endsWith('.' + schemaForCurrentDoc.path)) {
|
||||
// Ensure correct context for ref functions: subdoc, not top-level doc. See gh-8469
|
||||
modelNames = new Set();
|
||||
|
||||
const subdocPath = options.path.slice(0, options.path.length - schemaForCurrentDoc.path.length - 1);
|
||||
const vals = mpath.get(subdocPath, doc, lookupLocalFields);
|
||||
const subdocsBeingPopulated = Array.isArray(vals) ?
|
||||
utils.array.flatten(vals) :
|
||||
(vals ? [vals] : []);
|
||||
for (const subdoc of subdocsBeingPopulated) {
|
||||
modelNames.add(handleRefFunction(ref, subdoc));
|
||||
}
|
||||
|
||||
if (subdocsBeingPopulated.length === 0) {
|
||||
modelNames = [handleRefFunction(ref, doc)];
|
||||
} else {
|
||||
modelNames = Array.from(modelNames);
|
||||
}
|
||||
} else {
|
||||
ref = handleRefFunction(ref, doc);
|
||||
modelNames = [ref];
|
||||
}
|
||||
} else if ((schemaForCurrentDoc = get(schema, 'options.refPath')) != null) {
|
||||
isRefPath = true;
|
||||
if (typeof refPath === 'function') {
|
||||
const subdocPath = options.path.slice(0, options.path.length - schemaForCurrentDoc.path.length - 1);
|
||||
const vals = mpath.get(subdocPath, doc, lookupLocalFields);
|
||||
const subdocsBeingPopulated = Array.isArray(vals) ?
|
||||
utils.array.flatten(vals) :
|
||||
(vals ? [vals] : []);
|
||||
|
||||
modelNames = new Set();
|
||||
for (const subdoc of subdocsBeingPopulated) {
|
||||
refPath = refPath.call(subdoc, subdoc, options.path);
|
||||
modelNamesFromRefPath(refPath, doc, options.path, modelSchema, options._queryProjection).
|
||||
forEach(name => modelNames.add(name));
|
||||
}
|
||||
modelNames = Array.from(modelNames);
|
||||
} else {
|
||||
modelNames = modelNamesFromRefPath(refPath, doc, options.path, modelSchema, options._queryProjection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!modelNames) {
|
||||
// `Model.populate()` on a POJO with no known local model. Default to using the `Model`
|
||||
if (options._localModel == null) {
|
||||
modelNames = [model.modelName];
|
||||
} else {
|
||||
return { modelNames: modelNames, justOne: justOne, isRefPath: isRefPath, refPath: refPath };
|
||||
}
|
||||
}
|
||||
|
||||
if (!Array.isArray(modelNames)) {
|
||||
modelNames = [modelNames];
|
||||
}
|
||||
|
||||
return { modelNames: modelNames, justOne: justOne, isRefPath: isRefPath, refPath: refPath };
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
function _virtualPopulate(model, docs, options, _virtualRes) {
|
||||
const map = [];
|
||||
const available = {};
|
||||
const virtual = _virtualRes.virtual;
|
||||
|
||||
for (const doc of docs) {
|
||||
let modelNames = null;
|
||||
const data = {};
|
||||
|
||||
// localField and foreignField
|
||||
let localField;
|
||||
const virtualPrefix = _virtualRes.nestedSchemaPath ?
|
||||
_virtualRes.nestedSchemaPath + '.' : '';
|
||||
if (typeof options.localField === 'string') {
|
||||
localField = options.localField;
|
||||
} else 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;
|
||||
}
|
||||
data.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;
|
||||
}
|
||||
let foreignField = virtual.options.foreignField;
|
||||
|
||||
if (!localField || !foreignField) {
|
||||
return new MongooseError('If you are populating a virtual, you must set the ' +
|
||||
'localField and foreignField options');
|
||||
}
|
||||
|
||||
if (typeof localField === 'function') {
|
||||
localField = localField.call(doc, doc);
|
||||
}
|
||||
if (typeof foreignField === 'function') {
|
||||
foreignField = foreignField.call(doc, doc);
|
||||
}
|
||||
|
||||
data.isRefPath = false;
|
||||
|
||||
// `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()`
|
||||
let justOne = null;
|
||||
if ('justOne' in options && options.justOne !== void 0) {
|
||||
justOne = options.justOne;
|
||||
}
|
||||
|
||||
if (virtual.options.refPath) {
|
||||
modelNames =
|
||||
modelNamesFromRefPath(virtual.options.refPath, doc, options.path);
|
||||
justOne = !!virtual.options.justOne;
|
||||
data.isRefPath = true;
|
||||
} else if (virtual.options.ref) {
|
||||
let normalizedRef;
|
||||
if (typeof virtual.options.ref === 'function' && !virtual.options.ref[modelSymbol]) {
|
||||
normalizedRef = virtual.options.ref.call(doc, doc);
|
||||
} else {
|
||||
normalizedRef = virtual.options.ref;
|
||||
}
|
||||
justOne = !!virtual.options.justOne;
|
||||
// When referencing nested arrays, the ref should be an Array
|
||||
// of modelNames.
|
||||
if (Array.isArray(normalizedRef)) {
|
||||
modelNames = normalizedRef;
|
||||
} else {
|
||||
modelNames = [normalizedRef];
|
||||
}
|
||||
}
|
||||
|
||||
data.isVirtual = true;
|
||||
data.virtual = virtual;
|
||||
data.justOne = justOne;
|
||||
|
||||
// `match`
|
||||
let match = get(options, 'match', null) ||
|
||||
get(data, 'virtual.options.match', null) ||
|
||||
get(data, '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), model.schema);
|
||||
hasMatchFunction = true;
|
||||
}
|
||||
|
||||
localField = localField[0];
|
||||
foreignField = foreignField[0];
|
||||
}
|
||||
data.localField = localField;
|
||||
data.foreignField = foreignField;
|
||||
data.match = match;
|
||||
data.hasMatchFunction = hasMatchFunction;
|
||||
|
||||
// Get local fields
|
||||
const ret = _getLocalFieldValues(doc, localField, model, options, virtual);
|
||||
|
||||
try {
|
||||
addModelNamesToMap(model, map, available, modelNames, options, data, ret, doc);
|
||||
} catch (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
function addModelNamesToMap(model, map, available, modelNames, options, data, ret, doc, schemaOptions, unpopulatedValue) {
|
||||
// `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;
|
||||
|
||||
unpopulatedValue = unpopulatedValue === void 0 ? ret : unpopulatedValue;
|
||||
if (Array.isArray(unpopulatedValue)) {
|
||||
unpopulatedValue = utils.cloneArrays(unpopulatedValue);
|
||||
}
|
||||
|
||||
if (modelNames == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
let k = modelNames.length;
|
||||
while (k--) {
|
||||
const modelName = modelNames[k];
|
||||
if (modelName == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Model;
|
||||
if (options.model && options.model[modelSymbol]) {
|
||||
Model = options.model;
|
||||
} else if (modelName[modelSymbol]) {
|
||||
Model = modelName;
|
||||
} else {
|
||||
try {
|
||||
Model = _getModelFromConn(connection, modelName);
|
||||
} catch (err) {
|
||||
if (ret !== void 0) {
|
||||
throw err;
|
||||
}
|
||||
Model = null;
|
||||
}
|
||||
}
|
||||
|
||||
let ids = ret;
|
||||
const flat = Array.isArray(ret) ? utils.array.flatten(ret) : [];
|
||||
|
||||
const modelNamesForRefPath = data.modelNamesInOrder ? data.modelNamesInOrder : modelNames;
|
||||
if (data.isRefPath && Array.isArray(ret) && flat.length === modelNamesForRefPath.length) {
|
||||
ids = flat.filter((val, i) => modelNamesForRefPath[i] === modelName);
|
||||
}
|
||||
|
||||
const perDocumentLimit = options.perDocumentLimit == null ?
|
||||
get(options, 'options.perDocumentLimit', null) :
|
||||
options.perDocumentLimit;
|
||||
|
||||
if (!available[modelName] || perDocumentLimit != null) {
|
||||
const currentOptions = {
|
||||
model: Model
|
||||
};
|
||||
if (data.isVirtual && get(data.virtual, 'options.options')) {
|
||||
currentOptions.options = clone(data.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: data.hasMatchFunction ? [data.match] : data.match,
|
||||
docs: [doc],
|
||||
ids: [ids],
|
||||
allIds: [ret],
|
||||
unpopulatedValues: [unpopulatedValue],
|
||||
localField: new Set([data.localField]),
|
||||
foreignField: new Set([data.foreignField]),
|
||||
justOne: data.justOne,
|
||||
isVirtual: data.isVirtual,
|
||||
virtual: data.virtual,
|
||||
count: data.count,
|
||||
[populateModelSymbol]: Model
|
||||
};
|
||||
map.push(available[modelName]);
|
||||
} else {
|
||||
available[modelName].localField.add(data.localField);
|
||||
available[modelName].foreignField.add(data.foreignField);
|
||||
available[modelName].docs.push(doc);
|
||||
available[modelName].ids.push(ids);
|
||||
available[modelName].allIds.push(ret);
|
||||
available[modelName].unpopulatedValues.push(unpopulatedValue);
|
||||
if (data.hasMatchFunction) {
|
||||
available[modelName].match.push(data.match);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _getModelFromConn(conn, modelName) {
|
||||
/* If this connection has a parent from `useDb()`, bubble up to parent's models */
|
||||
if (conn.models[modelName] == null && conn._parent != null) {
|
||||
return _getModelFromConn(conn._parent, modelName);
|
||||
}
|
||||
|
||||
return conn.model(modelName);
|
||||
}
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
function handleRefFunction(ref, doc) {
|
||||
if (typeof ref === 'function' && !ref[modelSymbol]) {
|
||||
return ref.call(doc, doc);
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
function _getLocalFieldValues(doc, localField, model, options, virtual, schema) {
|
||||
// Get Local fields
|
||||
const localFieldPathType = model.schema._getPathType(localField);
|
||||
const localFieldPath = localFieldPathType === 'real' ?
|
||||
model.schema.path(localField) :
|
||||
localFieldPathType.schema;
|
||||
const localFieldGetters = localFieldPath && localFieldPath.getters ?
|
||||
localFieldPath.getters : [];
|
||||
|
||||
localField = localFieldPath != null && localFieldPath.instance === 'Embedded' ? localField + '._id' : localField;
|
||||
|
||||
const _populateOptions = get(options, 'options', {});
|
||||
|
||||
const getters = 'getters' in _populateOptions ?
|
||||
_populateOptions.getters :
|
||||
get(virtual, 'options.getters', false);
|
||||
if (localFieldGetters.length !== 0 && getters) {
|
||||
const hydratedDoc = (doc.$__ != null) ? doc : model.hydrate(doc);
|
||||
const localFieldValue = utils.getValue(localField, doc);
|
||||
if (Array.isArray(localFieldValue)) {
|
||||
const localFieldHydratedValue = utils.getValue(localField.split('.').slice(0, -1), hydratedDoc);
|
||||
return localFieldValue.map((localFieldArrVal, localFieldArrIndex) =>
|
||||
localFieldPath.applyGetters(localFieldArrVal, localFieldHydratedValue[localFieldArrIndex]));
|
||||
} else {
|
||||
return localFieldPath.applyGetters(localFieldValue, hydratedDoc);
|
||||
}
|
||||
} else {
|
||||
return convertTo_id(mpath.get(localField, doc, lookupLocalFields), schema);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the _id of `val` if a Document or Array of Documents.
|
||||
*
|
||||
* @param {Array|Document|Any} val
|
||||
* @param {Schema} schema
|
||||
* @return {Array|Document|Any}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
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)) {
|
||||
const rawVal = val.__array != null ? val.__array : val;
|
||||
for (let i = 0; i < rawVal.length; ++i) {
|
||||
if (rawVal[i] != null && rawVal[i].$__ != null) {
|
||||
rawVal[i] = rawVal[i]._id;
|
||||
}
|
||||
}
|
||||
if (utils.isMongooseArray(val) && 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 (getConstructorName(val) === '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')`
|
||||
// then `val` will already be a map
|
||||
if (val instanceof Map) {
|
||||
return Array.from(val.values());
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
function _findRefPathForDiscriminators(doc, modelSchema, data, options, normalizedRefPath, 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 (!data.isRefPath || normalizedRefPath == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pieces = normalizedRefPath.split('.');
|
||||
let cur = '';
|
||||
let modelNames = void 0;
|
||||
for (let i = 0; i < pieces.length; ++i) {
|
||||
const piece = pieces[i];
|
||||
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.substring(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(data.localField.substring(cur.length + 1), subdoc);
|
||||
ret.forEach((v, i) => {
|
||||
if (v === docValue) {
|
||||
ret[i] = SkipPopulateValue(v);
|
||||
}
|
||||
});
|
||||
continue;
|
||||
}
|
||||
const modelName = utils.getValue(pieces.slice(i + 1).join('.'), subdoc);
|
||||
modelNames.push(modelName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modelNames;
|
||||
}
|
||||
233
node_modules/mongoose/lib/helpers/populate/getSchemaTypes.js
generated
vendored
Normal file
233
node_modules/mongoose/lib/helpers/populate/getSchemaTypes.js
generated
vendored
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
'use strict';
|
||||
|
||||
/*!
|
||||
* ignore
|
||||
*/
|
||||
|
||||
const Mixed = require('../../schema/mixed');
|
||||
const get = require('../get');
|
||||
const getDiscriminatorByValue = require('../discriminator/getDiscriminatorByValue');
|
||||
const leanPopulateMap = require('./leanPopulateMap');
|
||||
const mpath = require('mpath');
|
||||
|
||||
const populateModelSymbol = require('../symbols').populateModelSymbol;
|
||||
|
||||
/**
|
||||
* Given a model and its schema, find all possible schema types for `path`,
|
||||
* including searching through discriminators. If `doc` is specified, will
|
||||
* use the doc's values for discriminator keys when searching, otherwise
|
||||
* will search all discriminators.
|
||||
*
|
||||
* @param {Model} model
|
||||
* @param {Schema} schema
|
||||
* @param {Object} doc POJO
|
||||
* @param {string} path
|
||||
* @api private
|
||||
*/
|
||||
|
||||
module.exports = function getSchemaTypes(model, schema, doc, path) {
|
||||
const pathschema = schema.path(path);
|
||||
const topLevelDoc = doc;
|
||||
if (pathschema) {
|
||||
return pathschema;
|
||||
}
|
||||
|
||||
const discriminatorKey = schema.discriminatorMapping &&
|
||||
schema.discriminatorMapping.key;
|
||||
if (discriminatorKey && model != null) {
|
||||
if (doc != null && doc[discriminatorKey] != null) {
|
||||
const discriminator = getDiscriminatorByValue(model.discriminators, doc[discriminatorKey]);
|
||||
schema = discriminator ? discriminator.schema : schema;
|
||||
} else if (model.discriminators != null) {
|
||||
return Object.keys(model.discriminators).reduce((arr, name) => {
|
||||
const disc = model.discriminators[name];
|
||||
return arr.concat(getSchemaTypes(disc, disc.schema, null, path));
|
||||
}, []);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (foundschema.$isSchemaMap && foundschema.$__schemaType instanceof Mixed) {
|
||||
return foundschema.$__schemaType;
|
||||
}
|
||||
|
||||
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();
|
||||
40
node_modules/mongoose/lib/helpers/populate/lookupLocalFields.js
generated
vendored
Normal file
40
node_modules/mongoose/lib/helpers/populate/lookupLocalFields.js
generated
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
'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;
|
||||
}
|
||||
if (val === void 0) {
|
||||
return void 0;
|
||||
}
|
||||
if (cur instanceof Map) {
|
||||
cur.set(path, val);
|
||||
} else {
|
||||
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]);
|
||||
}
|
||||
|
||||
if (cur instanceof Map) {
|
||||
return cur.get(path);
|
||||
}
|
||||
|
||||
return cur[path];
|
||||
};
|
||||
47
node_modules/mongoose/lib/helpers/populate/markArraySubdocsPopulated.js
generated
vendored
Normal file
47
node_modules/mongoose/lib/helpers/populate/markArraySubdocsPopulated.js
generated
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
'use strict';
|
||||
|
||||
const utils = require('../../utils');
|
||||
|
||||
/**
|
||||
* If populating a path within a document array, make sure each
|
||||
* subdoc within the array knows its subpaths are populated.
|
||||
*
|
||||
* #### Example:
|
||||
*
|
||||
* const doc = await Article.findOne().populate('comments.author');
|
||||
* doc.comments[0].populated('author'); // Should be set
|
||||
*
|
||||
* @param {Document} doc
|
||||
* @param {Object} [populated]
|
||||
* @api private
|
||||
*/
|
||||
|
||||
module.exports = function markArraySubdocsPopulated(doc, populated) {
|
||||
if (doc._id == null || populated == null || populated.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const id = String(doc._id);
|
||||
for (const item of populated) {
|
||||
if (item.isVirtual) {
|
||||
continue;
|
||||
}
|
||||
const path = item.path;
|
||||
const pieces = path.split('.');
|
||||
for (let i = 0; i < pieces.length - 1; ++i) {
|
||||
const subpath = pieces.slice(0, i + 1).join('.');
|
||||
const rest = pieces.slice(i + 1).join('.');
|
||||
const val = doc.get(subpath);
|
||||
if (val == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (utils.isMongooseDocumentArray(val)) {
|
||||
for (let j = 0; j < val.length; ++j) {
|
||||
val[j].populated(rest, item._docs[id] == null ? void 0 : item._docs[id][j], item);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
68
node_modules/mongoose/lib/helpers/populate/modelNamesFromRefPath.js
generated
vendored
Normal file
68
node_modules/mongoose/lib/helpers/populate/modelNamesFromRefPath.js
generated
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
'use strict';
|
||||
|
||||
const MongooseError = require('../../error/mongooseError');
|
||||
const isPathExcluded = require('../projection/isPathExcluded');
|
||||
const lookupLocalFields = require('./lookupLocalFields');
|
||||
const mpath = require('mpath');
|
||||
const util = require('util');
|
||||
const utils = require('../../utils');
|
||||
|
||||
const hasNumericPropRE = /(\.\d+$|\.\d+\.)/g;
|
||||
|
||||
module.exports = function modelNamesFromRefPath(refPath, doc, populatedPath, modelSchema, queryProjection) {
|
||||
if (refPath == null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (typeof refPath === 'string' && queryProjection != null && isPathExcluded(queryProjection, refPath)) {
|
||||
throw new MongooseError('refPath `' + refPath + '` must not be excluded in projection, got ' +
|
||||
util.inspect(queryProjection));
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
if (hasNumericPropRE.test(populatedPath)) {
|
||||
const chunks = populatedPath.split(hasNumericPropRE);
|
||||
|
||||
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.substring(0, chunk.length) + chunks[i + 1];
|
||||
_remaining = _remaining.substring(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');
|
||||
}
|
||||
}
|
||||
|
||||
const refValue = mpath.get(_refPath, doc, lookupLocalFields);
|
||||
let modelNames = Array.isArray(refValue) ? refValue : [refValue];
|
||||
modelNames = utils.array.flatten(modelNames);
|
||||
return modelNames;
|
||||
}
|
||||
|
||||
const refValue = mpath.get(refPath, doc, lookupLocalFields);
|
||||
|
||||
let modelNames;
|
||||
if (modelSchema != null && modelSchema.virtuals.hasOwnProperty(refPath)) {
|
||||
modelNames = [modelSchema.virtuals[refPath].applyGetters(void 0, doc)];
|
||||
} else {
|
||||
modelNames = Array.isArray(refValue) ? refValue : [refValue];
|
||||
}
|
||||
|
||||
modelNames = utils.array.flatten(modelNames);
|
||||
|
||||
return modelNames;
|
||||
};
|
||||
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 }));
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue