123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- '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 {Schema} schema
- * @param {Object} doc POJO
- * @param {string} path
- */
- 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;
- }
- }
- }
- }
- 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, []);
- };
|