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; | ||
|  | } |