gx
chenyc
2025-06-12 7b72ac13a83764a662159d4a49b7fffb90476ecb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
'use strict';
 
const SkipPopulateValue = require('./SkipPopulateValue');
const parentPaths = require('../path/parentPaths');
 
module.exports = function createPopulateQueryFilter(ids, _match, _foreignField, model, skipInvalidIds) {
  const match = _formatMatch(_match);
 
  if (_foreignField.size === 1) {
    const foreignField = Array.from(_foreignField)[0];
    const foreignSchemaType = model.schema.path(foreignField);
    if (foreignField !== '_id' || !match['_id']) {
      ids = _filterInvalidIds(ids, foreignSchemaType, skipInvalidIds);
      match[foreignField] = { $in: ids };
    }
 
    const _parentPaths = parentPaths(foreignField);
    for (let i = 0; i < _parentPaths.length - 1; ++i) {
      const cur = _parentPaths[i];
      if (match[cur] != null && match[cur].$elemMatch != null) {
        match[cur].$elemMatch[foreignField.slice(cur.length + 1)] = { $in: ids };
        delete match[foreignField];
        break;
      }
    }
  } else {
    const $or = [];
    if (Array.isArray(match.$or)) {
      match.$and = [{ $or: match.$or }, { $or: $or }];
      delete match.$or;
    } else {
      match.$or = $or;
    }
    for (const foreignField of _foreignField) {
      if (foreignField !== '_id' || !match['_id']) {
        const foreignSchemaType = model.schema.path(foreignField);
        ids = _filterInvalidIds(ids, foreignSchemaType, skipInvalidIds);
        $or.push({ [foreignField]: { $in: ids } });
      }
    }
  }
 
  return match;
};
 
/*!
 * Optionally filter out invalid ids that don't conform to foreign field's schema
 * to avoid cast errors (gh-7706)
 */
 
function _filterInvalidIds(ids, foreignSchemaType, skipInvalidIds) {
  ids = ids.filter(v => !(v instanceof SkipPopulateValue));
  if (!skipInvalidIds) {
    return ids;
  }
  return ids.filter(id => {
    try {
      foreignSchemaType.cast(id);
      return true;
    } catch (err) {
      return false;
    }
  });
}
 
/*!
 * Format `mod.match` given that it may be an array that we need to $or if
 * the client has multiple docs with match functions
 */
 
function _formatMatch(match) {
  if (Array.isArray(match)) {
    if (match.length > 1) {
      return { $or: [].concat(match.map(m => Object.assign({}, m))) };
    }
    return Object.assign({}, match[0]);
  }
  return Object.assign({}, match);
}