'use strict';
|
|
const SkipPopulateValue = require('./SkipPopulateValue');
|
const parentPaths = require('../path/parentPaths');
|
|
module.exports = function createPopulateQueryFilter(ids, _match, _foreignField, model, skipInvalidIds) {
|
const match = _formatMatch(_match);
|
|
if (_foreignField.size === 1) {
|
const foreignField = Array.from(_foreignField)[0];
|
const foreignSchemaType = model.schema.path(foreignField);
|
if (foreignField !== '_id' || !match['_id']) {
|
ids = _filterInvalidIds(ids, foreignSchemaType, skipInvalidIds);
|
match[foreignField] = { $in: ids };
|
}
|
|
const _parentPaths = parentPaths(foreignField);
|
for (let i = 0; i < _parentPaths.length - 1; ++i) {
|
const cur = _parentPaths[i];
|
if (match[cur] != null && match[cur].$elemMatch != null) {
|
match[cur].$elemMatch[foreignField.slice(cur.length + 1)] = { $in: ids };
|
delete match[foreignField];
|
break;
|
}
|
}
|
} else {
|
const $or = [];
|
if (Array.isArray(match.$or)) {
|
match.$and = [{ $or: match.$or }, { $or: $or }];
|
delete match.$or;
|
} else {
|
match.$or = $or;
|
}
|
for (const foreignField of _foreignField) {
|
if (foreignField !== '_id' || !match['_id']) {
|
const foreignSchemaType = model.schema.path(foreignField);
|
ids = _filterInvalidIds(ids, foreignSchemaType, skipInvalidIds);
|
$or.push({ [foreignField]: { $in: ids } });
|
}
|
}
|
}
|
|
return match;
|
};
|
|
/*!
|
* Optionally filter out invalid ids that don't conform to foreign field's schema
|
* to avoid cast errors (gh-7706)
|
*/
|
|
function _filterInvalidIds(ids, foreignSchemaType, skipInvalidIds) {
|
ids = ids.filter(v => !(v instanceof SkipPopulateValue));
|
if (!skipInvalidIds) {
|
return ids;
|
}
|
return ids.filter(id => {
|
try {
|
foreignSchemaType.cast(id);
|
return true;
|
} catch (err) {
|
return false;
|
}
|
});
|
}
|
|
/*!
|
* Format `mod.match` given that it may be an array that we need to $or if
|
* the client has multiple docs with match functions
|
*/
|
|
function _formatMatch(match) {
|
if (Array.isArray(match)) {
|
if (match.length > 1) {
|
return { $or: [].concat(match.map(m => Object.assign({}, m))) };
|
}
|
return Object.assign({}, match[0]);
|
}
|
return Object.assign({}, match);
|
}
|