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.
		
		
		
		
		
			
		
			
				
					128 lines
				
				3.6 KiB
			
		
		
			
		
	
	
					128 lines
				
				3.6 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								'use strict';
							 | 
						||
| 
								 | 
							
								const modifiedPaths = require('./common').modifiedPaths;
							 | 
						||
| 
								 | 
							
								const get = require('./get');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Applies defaults to update and findOneAndUpdate operations.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {Object} filter
							 | 
						||
| 
								 | 
							
								 * @param {Schema} schema
							 | 
						||
| 
								 | 
							
								 * @param {Object} castedDoc
							 | 
						||
| 
								 | 
							
								 * @param {Object} options
							 | 
						||
| 
								 | 
							
								 * @method setDefaultsOnInsert
							 | 
						||
| 
								 | 
							
								 * @api private
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								module.exports = function(filter, schema, castedDoc, options) {
							 | 
						||
| 
								 | 
							
								  options = options || {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const shouldSetDefaultsOnInsert =
							 | 
						||
| 
								 | 
							
								    options.setDefaultsOnInsert != null ?
							 | 
						||
| 
								 | 
							
								      options.setDefaultsOnInsert :
							 | 
						||
| 
								 | 
							
								      schema.base.options.setDefaultsOnInsert;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (!options.upsert || !shouldSetDefaultsOnInsert) {
							 | 
						||
| 
								 | 
							
								    return castedDoc;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const keys = Object.keys(castedDoc || {});
							 | 
						||
| 
								 | 
							
								  const updatedKeys = {};
							 | 
						||
| 
								 | 
							
								  const updatedValues = {};
							 | 
						||
| 
								 | 
							
								  const numKeys = keys.length;
							 | 
						||
| 
								 | 
							
								  const modified = {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  let hasDollarUpdate = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (let i = 0; i < numKeys; ++i) {
							 | 
						||
| 
								 | 
							
								    if (keys[i].startsWith('$')) {
							 | 
						||
| 
								 | 
							
								      modifiedPaths(castedDoc[keys[i]], '', modified);
							 | 
						||
| 
								 | 
							
								      hasDollarUpdate = true;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (!hasDollarUpdate) {
							 | 
						||
| 
								 | 
							
								    modifiedPaths(castedDoc, '', modified);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const paths = Object.keys(filter);
							 | 
						||
| 
								 | 
							
								  const numPaths = paths.length;
							 | 
						||
| 
								 | 
							
								  for (let i = 0; i < numPaths; ++i) {
							 | 
						||
| 
								 | 
							
								    const path = paths[i];
							 | 
						||
| 
								 | 
							
								    const condition = filter[path];
							 | 
						||
| 
								 | 
							
								    if (condition && typeof condition === 'object') {
							 | 
						||
| 
								 | 
							
								      const conditionKeys = Object.keys(condition);
							 | 
						||
| 
								 | 
							
								      const numConditionKeys = conditionKeys.length;
							 | 
						||
| 
								 | 
							
								      let hasDollarKey = false;
							 | 
						||
| 
								 | 
							
								      for (let j = 0; j < numConditionKeys; ++j) {
							 | 
						||
| 
								 | 
							
								        if (conditionKeys[j].startsWith('$')) {
							 | 
						||
| 
								 | 
							
								          hasDollarKey = true;
							 | 
						||
| 
								 | 
							
								          break;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      if (hasDollarKey) {
							 | 
						||
| 
								 | 
							
								        continue;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    updatedKeys[path] = true;
							 | 
						||
| 
								 | 
							
								    modified[path] = true;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (options && options.overwrite && !hasDollarUpdate) {
							 | 
						||
| 
								 | 
							
								    // Defaults will be set later, since we're overwriting we'll cast
							 | 
						||
| 
								 | 
							
								    // the whole update to a document
							 | 
						||
| 
								 | 
							
								    return castedDoc;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  schema.eachPath(function(path, schemaType) {
							 | 
						||
| 
								 | 
							
								    // Skip single nested paths if underneath a map
							 | 
						||
| 
								 | 
							
								    const isUnderneathMap = schemaType.path.endsWith('.$*') ||
							 | 
						||
| 
								 | 
							
								      schemaType.path.indexOf('.$*.') !== -1;
							 | 
						||
| 
								 | 
							
								    if (schemaType.$isSingleNested && !isUnderneathMap) {
							 | 
						||
| 
								 | 
							
								      // Only handle nested schemas 1-level deep to avoid infinite
							 | 
						||
| 
								 | 
							
								      // recursion re: https://github.com/mongodb-js/mongoose-autopopulate/issues/11
							 | 
						||
| 
								 | 
							
								      schemaType.schema.eachPath(function(_path, _schemaType) {
							 | 
						||
| 
								 | 
							
								        if (_path === '_id' && _schemaType.auto) {
							 | 
						||
| 
								 | 
							
								          // Ignore _id if auto id so we don't create subdocs
							 | 
						||
| 
								 | 
							
								          return;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        const def = _schemaType.getDefault(null, true);
							 | 
						||
| 
								 | 
							
								        if (!isModified(modified, path + '.' + _path) &&
							 | 
						||
| 
								 | 
							
								            typeof def !== 'undefined') {
							 | 
						||
| 
								 | 
							
								          castedDoc = castedDoc || {};
							 | 
						||
| 
								 | 
							
								          castedDoc.$setOnInsert = castedDoc.$setOnInsert || {};
							 | 
						||
| 
								 | 
							
								          castedDoc.$setOnInsert[path + '.' + _path] = def;
							 | 
						||
| 
								 | 
							
								          updatedValues[path + '.' + _path] = def;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      });
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      const def = schemaType.getDefault(null, true);
							 | 
						||
| 
								 | 
							
								      if (!isModified(modified, path) && typeof def !== 'undefined') {
							 | 
						||
| 
								 | 
							
								        castedDoc = castedDoc || {};
							 | 
						||
| 
								 | 
							
								        castedDoc.$setOnInsert = castedDoc.$setOnInsert || {};
							 | 
						||
| 
								 | 
							
								        if (get(castedDoc, path) == null) {
							 | 
						||
| 
								 | 
							
								          castedDoc.$setOnInsert[path] = def;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        updatedValues[path] = def;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return castedDoc;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function isModified(modified, path) {
							 | 
						||
| 
								 | 
							
								  if (modified[path]) {
							 | 
						||
| 
								 | 
							
								    return true;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  const sp = path.split('.');
							 | 
						||
| 
								 | 
							
								  let cur = sp[0];
							 | 
						||
| 
								 | 
							
								  for (let i = 1; i < sp.length; ++i) {
							 | 
						||
| 
								 | 
							
								    if (modified[cur]) {
							 | 
						||
| 
								 | 
							
								      return true;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    cur += '.' + sp[i];
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return false;
							 | 
						||
| 
								 | 
							
								}
							 |