You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					652 lines
				
				18 KiB
			
		
		
			
		
	
	
					652 lines
				
				18 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								'use strict';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*!
							 | 
						||
| 
								 | 
							
								 * Module dependencies.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const $exists = require('./operators/exists');
							 | 
						||
| 
								 | 
							
								const $type = require('./operators/type');
							 | 
						||
| 
								 | 
							
								const MongooseError = require('../error/mongooseError');
							 | 
						||
| 
								 | 
							
								const SchemaArrayOptions = require('../options/SchemaArrayOptions');
							 | 
						||
| 
								 | 
							
								const SchemaType = require('../schematype');
							 | 
						||
| 
								 | 
							
								const CastError = SchemaType.CastError;
							 | 
						||
| 
								 | 
							
								const Mixed = require('./mixed');
							 | 
						||
| 
								 | 
							
								const arrayDepth = require('../helpers/arrayDepth');
							 | 
						||
| 
								 | 
							
								const cast = require('../cast');
							 | 
						||
| 
								 | 
							
								const get = require('../helpers/get');
							 | 
						||
| 
								 | 
							
								const isOperator = require('../helpers/query/isOperator');
							 | 
						||
| 
								 | 
							
								const util = require('util');
							 | 
						||
| 
								 | 
							
								const utils = require('../utils');
							 | 
						||
| 
								 | 
							
								const castToNumber = require('./operators/helpers').castToNumber;
							 | 
						||
| 
								 | 
							
								const geospatial = require('./operators/geospatial');
							 | 
						||
| 
								 | 
							
								const getDiscriminatorByValue = require('../helpers/discriminator/getDiscriminatorByValue');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								let MongooseArray;
							 | 
						||
| 
								 | 
							
								let EmbeddedDoc;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const isNestedArraySymbol = Symbol('mongoose#isNestedArray');
							 | 
						||
| 
								 | 
							
								const emptyOpts = Object.freeze({});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Array SchemaType constructor
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {String} key
							 | 
						||
| 
								 | 
							
								 * @param {SchemaType} cast
							 | 
						||
| 
								 | 
							
								 * @param {Object} options
							 | 
						||
| 
								 | 
							
								 * @inherits SchemaType
							 | 
						||
| 
								 | 
							
								 * @api public
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function SchemaArray(key, cast, options, schemaOptions) {
							 | 
						||
| 
								 | 
							
								  // lazy load
							 | 
						||
| 
								 | 
							
								  EmbeddedDoc || (EmbeddedDoc = require('../types').Embedded);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  let typeKey = 'type';
							 | 
						||
| 
								 | 
							
								  if (schemaOptions && schemaOptions.typeKey) {
							 | 
						||
| 
								 | 
							
								    typeKey = schemaOptions.typeKey;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  this.schemaOptions = schemaOptions;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (cast) {
							 | 
						||
| 
								 | 
							
								    let castOptions = {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (utils.isPOJO(cast)) {
							 | 
						||
| 
								 | 
							
								      if (cast[typeKey]) {
							 | 
						||
| 
								 | 
							
								        // support { type: Woot }
							 | 
						||
| 
								 | 
							
								        castOptions = utils.clone(cast); // do not alter user arguments
							 | 
						||
| 
								 | 
							
								        delete castOptions[typeKey];
							 | 
						||
| 
								 | 
							
								        cast = cast[typeKey];
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        cast = Mixed;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (options != null && options.ref != null && castOptions.ref == null) {
							 | 
						||
| 
								 | 
							
								      castOptions.ref = options.ref;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (cast === Object) {
							 | 
						||
| 
								 | 
							
								      cast = Mixed;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // support { type: 'String' }
							 | 
						||
| 
								 | 
							
								    const name = typeof cast === 'string'
							 | 
						||
| 
								 | 
							
								      ? cast
							 | 
						||
| 
								 | 
							
								      : utils.getFunctionName(cast);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const Types = require('./index.js');
							 | 
						||
| 
								 | 
							
								    const caster = Types.hasOwnProperty(name) ? Types[name] : cast;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this.casterConstructor = caster;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (this.casterConstructor instanceof SchemaArray) {
							 | 
						||
| 
								 | 
							
								      this.casterConstructor[isNestedArraySymbol] = true;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (typeof caster === 'function' &&
							 | 
						||
| 
								 | 
							
								        !caster.$isArraySubdocument &&
							 | 
						||
| 
								 | 
							
								        !caster.$isSchemaMap) {
							 | 
						||
| 
								 | 
							
								      const path = this.caster instanceof EmbeddedDoc ? null : key;
							 | 
						||
| 
								 | 
							
								      this.caster = new caster(path, castOptions);
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      this.caster = caster;
							 | 
						||
| 
								 | 
							
								      if (!(this.caster instanceof EmbeddedDoc)) {
							 | 
						||
| 
								 | 
							
								        this.caster.path = key;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this.$embeddedSchemaType = this.caster;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this.$isMongooseArray = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  SchemaType.call(this, key, options, 'Array');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  let defaultArr;
							 | 
						||
| 
								 | 
							
								  let fn;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (this.defaultValue != null) {
							 | 
						||
| 
								 | 
							
								    defaultArr = this.defaultValue;
							 | 
						||
| 
								 | 
							
								    fn = typeof defaultArr === 'function';
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (!('defaultValue' in this) || this.defaultValue !== void 0) {
							 | 
						||
| 
								 | 
							
								    const defaultFn = function() {
							 | 
						||
| 
								 | 
							
								      let arr = [];
							 | 
						||
| 
								 | 
							
								      if (fn) {
							 | 
						||
| 
								 | 
							
								        arr = defaultArr.call(this);
							 | 
						||
| 
								 | 
							
								      } else if (defaultArr != null) {
							 | 
						||
| 
								 | 
							
								        arr = arr.concat(defaultArr);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      // Leave it up to `cast()` to convert the array
							 | 
						||
| 
								 | 
							
								      return arr;
							 | 
						||
| 
								 | 
							
								    };
							 | 
						||
| 
								 | 
							
								    defaultFn.$runBeforeSetters = !fn;
							 | 
						||
| 
								 | 
							
								    this.default(defaultFn);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * This schema type's name, to defend against minifiers that mangle
							 | 
						||
| 
								 | 
							
								 * function names.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @api public
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								SchemaArray.schemaName = 'Array';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Options for all arrays.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * - `castNonArrays`: `true` by default. If `false`, Mongoose will throw a CastError when a value isn't an array. If `true`, Mongoose will wrap the provided value in an array before casting.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @static options
							 | 
						||
| 
								 | 
							
								 * @api public
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								SchemaArray.options = { castNonArrays: true };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*!
							 | 
						||
| 
								 | 
							
								 * ignore
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								SchemaArray.defaultOptions = {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Sets a default option for all Array instances.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * ####Example:
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 *     // Make all Array instances have `required` of true by default.
							 | 
						||
| 
								 | 
							
								 *     mongoose.Schema.Array.set('required', true);
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 *     const User = mongoose.model('User', new Schema({ test: Array }));
							 | 
						||
| 
								 | 
							
								 *     new User({ }).validateSync().errors.test.message; // Path `test` is required.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {String} option - The option you'd like to set the value for
							 | 
						||
| 
								 | 
							
								 * @param {*} value - value for option
							 | 
						||
| 
								 | 
							
								 * @return {undefined}
							 | 
						||
| 
								 | 
							
								 * @function set
							 | 
						||
| 
								 | 
							
								 * @api public
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								SchemaArray.set = SchemaType.set;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*!
							 | 
						||
| 
								 | 
							
								 * Inherits from SchemaType.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								SchemaArray.prototype = Object.create(SchemaType.prototype);
							 | 
						||
| 
								 | 
							
								SchemaArray.prototype.constructor = SchemaArray;
							 | 
						||
| 
								 | 
							
								SchemaArray.prototype.OptionsConstructor = SchemaArrayOptions;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*!
							 | 
						||
| 
								 | 
							
								 * ignore
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								SchemaArray._checkRequired = SchemaType.prototype.checkRequired;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Override the function the required validator uses to check whether an array
							 | 
						||
| 
								 | 
							
								 * passes the `required` check.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * ####Example:
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 *     // Require non-empty array to pass `required` check
							 | 
						||
| 
								 | 
							
								 *     mongoose.Schema.Types.Array.checkRequired(v => Array.isArray(v) && v.length);
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 *     const M = mongoose.model({ arr: { type: Array, required: true } });
							 | 
						||
| 
								 | 
							
								 *     new M({ arr: [] }).validateSync(); // `null`, validation fails!
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {Function} fn
							 | 
						||
| 
								 | 
							
								 * @return {Function}
							 | 
						||
| 
								 | 
							
								 * @function checkRequired
							 | 
						||
| 
								 | 
							
								 * @api public
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								SchemaArray.checkRequired = SchemaType.checkRequired;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Check if the given value satisfies the `required` validator.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {Any} value
							 | 
						||
| 
								 | 
							
								 * @param {Document} doc
							 | 
						||
| 
								 | 
							
								 * @return {Boolean}
							 | 
						||
| 
								 | 
							
								 * @api public
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								SchemaArray.prototype.checkRequired = function checkRequired(value, doc) {
							 | 
						||
| 
								 | 
							
								  if (SchemaType._isRef(this, value, doc, true)) {
							 | 
						||
| 
								 | 
							
								    return !!value;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // `require('util').inherits()` does **not** copy static properties, and
							 | 
						||
| 
								 | 
							
								  // plugins like mongoose-float use `inherits()` for pre-ES6.
							 | 
						||
| 
								 | 
							
								  const _checkRequired = typeof this.constructor.checkRequired == 'function' ?
							 | 
						||
| 
								 | 
							
								    this.constructor.checkRequired() :
							 | 
						||
| 
								 | 
							
								    SchemaArray.checkRequired();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return _checkRequired(value);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Adds an enum validator if this is an array of strings or numbers. Equivalent to
							 | 
						||
| 
								 | 
							
								 * `SchemaString.prototype.enum()` or `SchemaNumber.prototype.enum()`
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {String|Object} [args...] enumeration values
							 | 
						||
| 
								 | 
							
								 * @return {SchemaArray} this
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								SchemaArray.prototype.enum = function() {
							 | 
						||
| 
								 | 
							
								  let arr = this;
							 | 
						||
| 
								 | 
							
								  while (true) {
							 | 
						||
| 
								 | 
							
								    const instance = get(arr, 'caster.instance');
							 | 
						||
| 
								 | 
							
								    if (instance === 'Array') {
							 | 
						||
| 
								 | 
							
								      arr = arr.caster;
							 | 
						||
| 
								 | 
							
								      continue;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (instance !== 'String' && instance !== 'Number') {
							 | 
						||
| 
								 | 
							
								      throw new Error('`enum` can only be set on an array of strings or numbers ' +
							 | 
						||
| 
								 | 
							
								        ', not ' + instance);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    break;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  let enumArray = arguments;
							 | 
						||
| 
								 | 
							
								  if (!Array.isArray(arguments) && utils.isObject(arguments)) {
							 | 
						||
| 
								 | 
							
								    enumArray = utils.object.vals(enumArray);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  arr.caster.enum.apply(arr.caster, enumArray);
							 | 
						||
| 
								 | 
							
								  return this;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Overrides the getters application for the population special-case
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {Object} value
							 | 
						||
| 
								 | 
							
								 * @param {Object} scope
							 | 
						||
| 
								 | 
							
								 * @api private
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								SchemaArray.prototype.applyGetters = function(value, scope) {
							 | 
						||
| 
								 | 
							
								  if (scope != null && scope.$__ != null && scope.populated(this.path)) {
							 | 
						||
| 
								 | 
							
								    // means the object id was populated
							 | 
						||
| 
								 | 
							
								    return value;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const ret = SchemaType.prototype.applyGetters.call(this, value, scope);
							 | 
						||
| 
								 | 
							
								  if (Array.isArray(ret)) {
							 | 
						||
| 
								 | 
							
								    const len = ret.length;
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < len; ++i) {
							 | 
						||
| 
								 | 
							
								      ret[i] = this.caster.applyGetters(ret[i], scope);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return ret;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								SchemaArray.prototype._applySetters = function(value, scope, init, priorVal) {
							 | 
						||
| 
								 | 
							
								  if (this.casterConstructor.$isMongooseArray &&
							 | 
						||
| 
								 | 
							
								      SchemaArray.options.castNonArrays &&
							 | 
						||
| 
								 | 
							
								      !this[isNestedArraySymbol]) {
							 | 
						||
| 
								 | 
							
								    // Check nesting levels and wrap in array if necessary
							 | 
						||
| 
								 | 
							
								    let depth = 0;
							 | 
						||
| 
								 | 
							
								    let arr = this;
							 | 
						||
| 
								 | 
							
								    while (arr != null &&
							 | 
						||
| 
								 | 
							
								      arr.$isMongooseArray &&
							 | 
						||
| 
								 | 
							
								      !arr.$isMongooseDocumentArray) {
							 | 
						||
| 
								 | 
							
								      ++depth;
							 | 
						||
| 
								 | 
							
								      arr = arr.casterConstructor;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // No need to wrap empty arrays
							 | 
						||
| 
								 | 
							
								    if (value != null && value.length > 0) {
							 | 
						||
| 
								 | 
							
								      const valueDepth = arrayDepth(value);
							 | 
						||
| 
								 | 
							
								      if (valueDepth.min === valueDepth.max && valueDepth.max < depth && valueDepth.containsNonArrayItem) {
							 | 
						||
| 
								 | 
							
								        for (let i = valueDepth.max; i < depth; ++i) {
							 | 
						||
| 
								 | 
							
								          value = [value];
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return SchemaType.prototype._applySetters.call(this, value, scope, init, priorVal);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Casts values for set().
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {Object} value
							 | 
						||
| 
								 | 
							
								 * @param {Document} doc document that triggers the casting
							 | 
						||
| 
								 | 
							
								 * @param {Boolean} init whether this is an initialization cast
							 | 
						||
| 
								 | 
							
								 * @api private
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								SchemaArray.prototype.cast = function(value, doc, init, prev, options) {
							 | 
						||
| 
								 | 
							
								  // lazy load
							 | 
						||
| 
								 | 
							
								  MongooseArray || (MongooseArray = require('../types').Array);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  let i;
							 | 
						||
| 
								 | 
							
								  let l;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (Array.isArray(value)) {
							 | 
						||
| 
								 | 
							
								    const len = value.length;
							 | 
						||
| 
								 | 
							
								    if (!len && doc) {
							 | 
						||
| 
								 | 
							
								      const indexes = doc.schema.indexedPaths();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      const arrayPath = this.path;
							 | 
						||
| 
								 | 
							
								      for (i = 0, l = indexes.length; i < l; ++i) {
							 | 
						||
| 
								 | 
							
								        const pathIndex = indexes[i][0][arrayPath];
							 | 
						||
| 
								 | 
							
								        if (pathIndex === '2dsphere' || pathIndex === '2d') {
							 | 
						||
| 
								 | 
							
								          return;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // Special case: if this index is on the parent of what looks like
							 | 
						||
| 
								 | 
							
								      // GeoJSON, skip setting the default to empty array re: #1668, #3233
							 | 
						||
| 
								 | 
							
								      const arrayGeojsonPath = this.path.endsWith('.coordinates') ?
							 | 
						||
| 
								 | 
							
								        this.path.substr(0, this.path.lastIndexOf('.')) : null;
							 | 
						||
| 
								 | 
							
								      if (arrayGeojsonPath != null) {
							 | 
						||
| 
								 | 
							
								        for (i = 0, l = indexes.length; i < l; ++i) {
							 | 
						||
| 
								 | 
							
								          const pathIndex = indexes[i][0][arrayGeojsonPath];
							 | 
						||
| 
								 | 
							
								          if (pathIndex === '2dsphere') {
							 | 
						||
| 
								 | 
							
								            return;
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    options = options || emptyOpts;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    value = MongooseArray(value, options.path || this._arrayPath || this.path, doc, this);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (init && doc != null && doc.$__ != null && doc.populated(this.path)) {
							 | 
						||
| 
								 | 
							
								      return value;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const caster = this.caster;
							 | 
						||
| 
								 | 
							
								    const isMongooseArray = caster.$isMongooseArray;
							 | 
						||
| 
								 | 
							
								    const isArrayOfNumbers = caster.instance === 'Number';
							 | 
						||
| 
								 | 
							
								    if (caster && this.casterConstructor !== Mixed) {
							 | 
						||
| 
								 | 
							
								      try {
							 | 
						||
| 
								 | 
							
								        for (i = 0; i < len; i++) {
							 | 
						||
| 
								 | 
							
								          // Special case: number arrays disallow undefined.
							 | 
						||
| 
								 | 
							
								          // Re: gh-840
							 | 
						||
| 
								 | 
							
								          // See commit 1298fe92d2c790a90594bd08199e45a4a09162a6
							 | 
						||
| 
								 | 
							
								          if (isArrayOfNumbers && value[i] === void 0) {
							 | 
						||
| 
								 | 
							
								            throw new MongooseError('Mongoose number arrays disallow storing undefined');
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								          const opts = {};
							 | 
						||
| 
								 | 
							
								          // Perf: creating `arrayPath` is expensive for large arrays.
							 | 
						||
| 
								 | 
							
								          // We only need `arrayPath` if this is a nested array, so
							 | 
						||
| 
								 | 
							
								          // skip if possible.
							 | 
						||
| 
								 | 
							
								          if (isMongooseArray) {
							 | 
						||
| 
								 | 
							
								            if (options.arrayPath != null) {
							 | 
						||
| 
								 | 
							
								              opts.arrayPathIndex = i;
							 | 
						||
| 
								 | 
							
								            } else if (caster._arrayParentPath != null) {
							 | 
						||
| 
								 | 
							
								              opts.arrayPathIndex = i;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								          value[i] = caster.applySetters(value[i], doc, init, void 0, opts);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      } catch (e) {
							 | 
						||
| 
								 | 
							
								        // rethrow
							 | 
						||
| 
								 | 
							
								        throw new CastError('[' + e.kind + ']', util.inspect(value), this.path + '.' + i, e, this);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return value;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (init || SchemaArray.options.castNonArrays) {
							 | 
						||
| 
								 | 
							
								    // gh-2442: if we're loading this from the db and its not an array, mark
							 | 
						||
| 
								 | 
							
								    // the whole array as modified.
							 | 
						||
| 
								 | 
							
								    if (!!doc && !!init) {
							 | 
						||
| 
								 | 
							
								      doc.markModified(this.path);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return this.cast([value], doc, init);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  throw new CastError('Array', util.inspect(value), this.path, null, this);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*!
							 | 
						||
| 
								 | 
							
								 * ignore
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								SchemaArray.prototype._castForPopulate = function _castForPopulate(value, doc) {
							 | 
						||
| 
								 | 
							
								  // lazy load
							 | 
						||
| 
								 | 
							
								  MongooseArray || (MongooseArray = require('../types').Array);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (Array.isArray(value)) {
							 | 
						||
| 
								 | 
							
								    let i;
							 | 
						||
| 
								 | 
							
								    const len = value.length;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const caster = this.caster;
							 | 
						||
| 
								 | 
							
								    if (caster && this.casterConstructor !== Mixed) {
							 | 
						||
| 
								 | 
							
								      try {
							 | 
						||
| 
								 | 
							
								        for (i = 0; i < len; i++) {
							 | 
						||
| 
								 | 
							
								          const opts = {};
							 | 
						||
| 
								 | 
							
								          // Perf: creating `arrayPath` is expensive for large arrays.
							 | 
						||
| 
								 | 
							
								          // We only need `arrayPath` if this is a nested array, so
							 | 
						||
| 
								 | 
							
								          // skip if possible.
							 | 
						||
| 
								 | 
							
								          if (caster.$isMongooseArray && caster._arrayParentPath != null) {
							 | 
						||
| 
								 | 
							
								            opts.arrayPathIndex = i;
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          value[i] = caster.cast(value[i], doc, false, void 0, opts);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      } catch (e) {
							 | 
						||
| 
								 | 
							
								        // rethrow
							 | 
						||
| 
								 | 
							
								        throw new CastError('[' + e.kind + ']', util.inspect(value), this.path + '.' + i, e, this);
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return value;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  throw new CastError('Array', util.inspect(value), this.path, null, this);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*!
							 | 
						||
| 
								 | 
							
								 * Ignore
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								SchemaArray.prototype.discriminator = function(name, schema) {
							 | 
						||
| 
								 | 
							
								  let arr = this;
							 | 
						||
| 
								 | 
							
								  while (arr.$isMongooseArray && !arr.$isMongooseDocumentArray) {
							 | 
						||
| 
								 | 
							
								    arr = arr.casterConstructor;
							 | 
						||
| 
								 | 
							
								    if (arr == null || typeof arr === 'function') {
							 | 
						||
| 
								 | 
							
								      throw new MongooseError('You can only add an embedded discriminator on ' +
							 | 
						||
| 
								 | 
							
								        'a document array, ' + this.path + ' is a plain array');
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return arr.discriminator(name, schema);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*!
							 | 
						||
| 
								 | 
							
								 * ignore
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								SchemaArray.prototype.clone = function() {
							 | 
						||
| 
								 | 
							
								  const options = Object.assign({}, this.options);
							 | 
						||
| 
								 | 
							
								  const schematype = new this.constructor(this.path, this.caster, options, this.schemaOptions);
							 | 
						||
| 
								 | 
							
								  schematype.validators = this.validators.slice();
							 | 
						||
| 
								 | 
							
								  if (this.requiredValidator !== undefined) {
							 | 
						||
| 
								 | 
							
								    schematype.requiredValidator = this.requiredValidator;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return schematype;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Casts values for queries.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {String} $conditional
							 | 
						||
| 
								 | 
							
								 * @param {any} [value]
							 | 
						||
| 
								 | 
							
								 * @api private
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								SchemaArray.prototype.castForQuery = function($conditional, value) {
							 | 
						||
| 
								 | 
							
								  let handler;
							 | 
						||
| 
								 | 
							
								  let val;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (arguments.length === 2) {
							 | 
						||
| 
								 | 
							
								    handler = this.$conditionalHandlers[$conditional];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!handler) {
							 | 
						||
| 
								 | 
							
								      throw new Error('Can\'t use ' + $conditional + ' with Array.');
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    val = handler.call(this, value);
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    val = $conditional;
							 | 
						||
| 
								 | 
							
								    let Constructor = this.casterConstructor;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (val &&
							 | 
						||
| 
								 | 
							
								        Constructor.discriminators &&
							 | 
						||
| 
								 | 
							
								        Constructor.schema &&
							 | 
						||
| 
								 | 
							
								        Constructor.schema.options &&
							 | 
						||
| 
								 | 
							
								        Constructor.schema.options.discriminatorKey) {
							 | 
						||
| 
								 | 
							
								      if (typeof val[Constructor.schema.options.discriminatorKey] === 'string' &&
							 | 
						||
| 
								 | 
							
								          Constructor.discriminators[val[Constructor.schema.options.discriminatorKey]]) {
							 | 
						||
| 
								 | 
							
								        Constructor = Constructor.discriminators[val[Constructor.schema.options.discriminatorKey]];
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        const constructorByValue = getDiscriminatorByValue(Constructor.discriminators, val[Constructor.schema.options.discriminatorKey]);
							 | 
						||
| 
								 | 
							
								        if (constructorByValue) {
							 | 
						||
| 
								 | 
							
								          Constructor = constructorByValue;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const proto = this.casterConstructor.prototype;
							 | 
						||
| 
								 | 
							
								    let method = proto && (proto.castForQuery || proto.cast);
							 | 
						||
| 
								 | 
							
								    if (!method && Constructor.castForQuery) {
							 | 
						||
| 
								 | 
							
								      method = Constructor.castForQuery;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    const caster = this.caster;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (Array.isArray(val)) {
							 | 
						||
| 
								 | 
							
								      this.setters.reverse().forEach(setter => {
							 | 
						||
| 
								 | 
							
								        val = setter.call(this, val, this);
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								      val = val.map(function(v) {
							 | 
						||
| 
								 | 
							
								        if (utils.isObject(v) && v.$elemMatch) {
							 | 
						||
| 
								 | 
							
								          return v;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (method) {
							 | 
						||
| 
								 | 
							
								          v = method.call(caster, v);
							 | 
						||
| 
								 | 
							
								          return v;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (v != null) {
							 | 
						||
| 
								 | 
							
								          v = new Constructor(v);
							 | 
						||
| 
								 | 
							
								          return v;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return v;
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								    } else if (method) {
							 | 
						||
| 
								 | 
							
								      val = method.call(caster, val);
							 | 
						||
| 
								 | 
							
								    } else if (val != null) {
							 | 
						||
| 
								 | 
							
								      val = new Constructor(val);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return val;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function cast$all(val) {
							 | 
						||
| 
								 | 
							
								  if (!Array.isArray(val)) {
							 | 
						||
| 
								 | 
							
								    val = [val];
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  val = val.map(function(v) {
							 | 
						||
| 
								 | 
							
								    if (utils.isObject(v)) {
							 | 
						||
| 
								 | 
							
								      const o = {};
							 | 
						||
| 
								 | 
							
								      o[this.path] = v;
							 | 
						||
| 
								 | 
							
								      return cast(this.casterConstructor.schema, o)[this.path];
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return v;
							 | 
						||
| 
								 | 
							
								  }, this);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return this.castForQuery(val);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function cast$elemMatch(val) {
							 | 
						||
| 
								 | 
							
								  const keys = Object.keys(val);
							 | 
						||
| 
								 | 
							
								  const numKeys = keys.length;
							 | 
						||
| 
								 | 
							
								  for (let i = 0; i < numKeys; ++i) {
							 | 
						||
| 
								 | 
							
								    const key = keys[i];
							 | 
						||
| 
								 | 
							
								    const value = val[key];
							 | 
						||
| 
								 | 
							
								    if (isOperator(key) && value != null) {
							 | 
						||
| 
								 | 
							
								      val[key] = this.castForQuery(key, value);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Is this an embedded discriminator and is the discriminator key set?
							 | 
						||
| 
								 | 
							
								  // If so, use the discriminator schema. See gh-7449
							 | 
						||
| 
								 | 
							
								  const discriminatorKey = get(this,
							 | 
						||
| 
								 | 
							
								    'casterConstructor.schema.options.discriminatorKey');
							 | 
						||
| 
								 | 
							
								  const discriminators = get(this, 'casterConstructor.schema.discriminators', {});
							 | 
						||
| 
								 | 
							
								  if (discriminatorKey != null &&
							 | 
						||
| 
								 | 
							
								      val[discriminatorKey] != null &&
							 | 
						||
| 
								 | 
							
								      discriminators[val[discriminatorKey]] != null) {
							 | 
						||
| 
								 | 
							
								    return cast(discriminators[val[discriminatorKey]], val);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return cast(this.casterConstructor.schema, val);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const handle = SchemaArray.prototype.$conditionalHandlers = {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								handle.$all = cast$all;
							 | 
						||
| 
								 | 
							
								handle.$options = String;
							 | 
						||
| 
								 | 
							
								handle.$elemMatch = cast$elemMatch;
							 | 
						||
| 
								 | 
							
								handle.$geoIntersects = geospatial.cast$geoIntersects;
							 | 
						||
| 
								 | 
							
								handle.$or = createLogicalQueryOperatorHandler('$or');
							 | 
						||
| 
								 | 
							
								handle.$and = createLogicalQueryOperatorHandler('$and');
							 | 
						||
| 
								 | 
							
								handle.$nor = createLogicalQueryOperatorHandler('$nor');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function createLogicalQueryOperatorHandler(op) {
							 | 
						||
| 
								 | 
							
								  return function logicalQueryOperatorHandler(val) {
							 | 
						||
| 
								 | 
							
								    if (!Array.isArray(val)) {
							 | 
						||
| 
								 | 
							
								      throw new TypeError('conditional ' + op + ' requires an array');
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const ret = [];
							 | 
						||
| 
								 | 
							
								    for (const obj of val) {
							 | 
						||
| 
								 | 
							
								      ret.push(cast(this.casterConstructor.schema, obj));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return ret;
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								handle.$near =
							 | 
						||
| 
								 | 
							
								handle.$nearSphere = geospatial.cast$near;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								handle.$within =
							 | 
						||
| 
								 | 
							
								handle.$geoWithin = geospatial.cast$within;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								handle.$size =
							 | 
						||
| 
								 | 
							
								handle.$minDistance =
							 | 
						||
| 
								 | 
							
								handle.$maxDistance = castToNumber;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								handle.$exists = $exists;
							 | 
						||
| 
								 | 
							
								handle.$type = $type;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								handle.$eq =
							 | 
						||
| 
								 | 
							
								handle.$gt =
							 | 
						||
| 
								 | 
							
								handle.$gte =
							 | 
						||
| 
								 | 
							
								handle.$lt =
							 | 
						||
| 
								 | 
							
								handle.$lte =
							 | 
						||
| 
								 | 
							
								handle.$ne =
							 | 
						||
| 
								 | 
							
								handle.$regex = SchemaArray.prototype.castForQuery;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// `$in` is special because you can also include an empty array in the query
							 | 
						||
| 
								 | 
							
								// like `$in: [1, []]`, see gh-5913
							 | 
						||
| 
								 | 
							
								handle.$nin = SchemaType.prototype.$conditionalHandlers.$nin;
							 | 
						||
| 
								 | 
							
								handle.$in = SchemaType.prototype.$conditionalHandlers.$in;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*!
							 | 
						||
| 
								 | 
							
								 * Module exports.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								module.exports = SchemaArray;
							 |