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.
		
		
		
		
		
			
		
			
				
					185 lines
				
				5.5 KiB
			
		
		
			
		
	
	
					185 lines
				
				5.5 KiB
			| 
											3 years ago
										 | 'use strict'; | ||
|  | 
 | ||
|  | const cleanPositionalOperators = require('../schema/cleanPositionalOperators'); | ||
|  | const handleTimestampOption = require('../schema/handleTimestampOption'); | ||
|  | 
 | ||
|  | module.exports = applyTimestampsToChildren; | ||
|  | 
 | ||
|  | /*! | ||
|  |  * ignore | ||
|  |  */ | ||
|  | 
 | ||
|  | function applyTimestampsToChildren(now, update, schema) { | ||
|  |   if (update == null) { | ||
|  |     return; | ||
|  |   } | ||
|  | 
 | ||
|  |   const keys = Object.keys(update); | ||
|  |   const hasDollarKey = keys.some(key => key.startsWith('$')); | ||
|  | 
 | ||
|  |   if (hasDollarKey) { | ||
|  |     if (update.$push) { | ||
|  |       _applyTimestampToUpdateOperator(update.$push); | ||
|  |     } | ||
|  |     if (update.$addToSet) { | ||
|  |       _applyTimestampToUpdateOperator(update.$addToSet); | ||
|  |     } | ||
|  |     if (update.$set != null) { | ||
|  |       const keys = Object.keys(update.$set); | ||
|  |       for (const key of keys) { | ||
|  |         applyTimestampsToUpdateKey(schema, key, update.$set, now); | ||
|  |       } | ||
|  |     } | ||
|  |     if (update.$setOnInsert != null) { | ||
|  |       const keys = Object.keys(update.$setOnInsert); | ||
|  |       for (const key of keys) { | ||
|  |         applyTimestampsToUpdateKey(schema, key, update.$setOnInsert, now); | ||
|  |       } | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   const updateKeys = Object.keys(update).filter(key => !key.startsWith('$')); | ||
|  |   for (const key of updateKeys) { | ||
|  |     applyTimestampsToUpdateKey(schema, key, update, now); | ||
|  |   } | ||
|  | 
 | ||
|  |   function _applyTimestampToUpdateOperator(op) { | ||
|  |     for (const key of Object.keys(op)) { | ||
|  |       const $path = schema.path(key.replace(/\.\$\./i, '.').replace(/.\$$/, '')); | ||
|  |       if (op[key] && | ||
|  |           $path && | ||
|  |           $path.$isMongooseDocumentArray && | ||
|  |           $path.schema.options.timestamps) { | ||
|  |         const timestamps = $path.schema.options.timestamps; | ||
|  |         const createdAt = handleTimestampOption(timestamps, 'createdAt'); | ||
|  |         const updatedAt = handleTimestampOption(timestamps, 'updatedAt'); | ||
|  |         if (op[key].$each) { | ||
|  |           op[key].$each.forEach(function(subdoc) { | ||
|  |             if (updatedAt != null) { | ||
|  |               subdoc[updatedAt] = now; | ||
|  |             } | ||
|  |             if (createdAt != null) { | ||
|  |               subdoc[createdAt] = now; | ||
|  |             } | ||
|  |           }); | ||
|  |         } else { | ||
|  |           if (updatedAt != null) { | ||
|  |             op[key][updatedAt] = now; | ||
|  |           } | ||
|  |           if (createdAt != null) { | ||
|  |             op[key][createdAt] = now; | ||
|  |           } | ||
|  |         } | ||
|  |       } | ||
|  |     } | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | function applyTimestampsToDocumentArray(arr, schematype, now) { | ||
|  |   const timestamps = schematype.schema.options.timestamps; | ||
|  | 
 | ||
|  |   if (!timestamps) { | ||
|  |     return; | ||
|  |   } | ||
|  | 
 | ||
|  |   const len = arr.length; | ||
|  | 
 | ||
|  |   const createdAt = handleTimestampOption(timestamps, 'createdAt'); | ||
|  |   const updatedAt = handleTimestampOption(timestamps, 'updatedAt'); | ||
|  |   for (let i = 0; i < len; ++i) { | ||
|  |     if (updatedAt != null) { | ||
|  |       arr[i][updatedAt] = now; | ||
|  |     } | ||
|  |     if (createdAt != null) { | ||
|  |       arr[i][createdAt] = now; | ||
|  |     } | ||
|  | 
 | ||
|  |     applyTimestampsToChildren(now, arr[i], schematype.schema); | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | function applyTimestampsToSingleNested(subdoc, schematype, now) { | ||
|  |   const timestamps = schematype.schema.options.timestamps; | ||
|  |   if (!timestamps) { | ||
|  |     return; | ||
|  |   } | ||
|  | 
 | ||
|  |   const createdAt = handleTimestampOption(timestamps, 'createdAt'); | ||
|  |   const updatedAt = handleTimestampOption(timestamps, 'updatedAt'); | ||
|  |   if (updatedAt != null) { | ||
|  |     subdoc[updatedAt] = now; | ||
|  |   } | ||
|  |   if (createdAt != null) { | ||
|  |     subdoc[createdAt] = now; | ||
|  |   } | ||
|  | 
 | ||
|  |   applyTimestampsToChildren(now, subdoc, schematype.schema); | ||
|  | } | ||
|  | 
 | ||
|  | function applyTimestampsToUpdateKey(schema, key, update, now) { | ||
|  |   // Replace positional operator `$` and array filters `$[]` and `$[.*]`
 | ||
|  |   const keyToSearch = cleanPositionalOperators(key); | ||
|  |   const path = schema.path(keyToSearch); | ||
|  |   if (!path) { | ||
|  |     return; | ||
|  |   } | ||
|  | 
 | ||
|  |   const parentSchemaTypes = []; | ||
|  |   const pieces = keyToSearch.split('.'); | ||
|  |   for (let i = pieces.length - 1; i > 0; --i) { | ||
|  |     const s = schema.path(pieces.slice(0, i).join('.')); | ||
|  |     if (s != null && | ||
|  |       (s.$isMongooseDocumentArray || s.$isSingleNested)) { | ||
|  |       parentSchemaTypes.push({ parentPath: key.split('.').slice(0, i).join('.'), parentSchemaType: s }); | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   if (Array.isArray(update[key]) && path.$isMongooseDocumentArray) { | ||
|  |     applyTimestampsToDocumentArray(update[key], path, now); | ||
|  |   } else if (update[key] && path.$isSingleNested) { | ||
|  |     applyTimestampsToSingleNested(update[key], path, now); | ||
|  |   } else if (parentSchemaTypes.length > 0) { | ||
|  |     for (const item of parentSchemaTypes) { | ||
|  |       const parentPath = item.parentPath; | ||
|  |       const parentSchemaType = item.parentSchemaType; | ||
|  |       const timestamps = parentSchemaType.schema.options.timestamps; | ||
|  |       const updatedAt = handleTimestampOption(timestamps, 'updatedAt'); | ||
|  | 
 | ||
|  |       if (!timestamps || updatedAt == null) { | ||
|  |         continue; | ||
|  |       } | ||
|  | 
 | ||
|  |       if (parentSchemaType.$isSingleNested) { | ||
|  |         // Single nested is easy
 | ||
|  |         update[parentPath + '.' + updatedAt] = now; | ||
|  |       } else if (parentSchemaType.$isMongooseDocumentArray) { | ||
|  |         let childPath = key.substr(parentPath.length + 1); | ||
|  | 
 | ||
|  |         if (/^\d+$/.test(childPath)) { | ||
|  |           update[parentPath + '.' + childPath][updatedAt] = now; | ||
|  |           continue; | ||
|  |         } | ||
|  | 
 | ||
|  |         const firstDot = childPath.indexOf('.'); | ||
|  |         childPath = firstDot !== -1 ? childPath.substr(0, firstDot) : childPath; | ||
|  | 
 | ||
|  |         update[parentPath + '.' + childPath + '.' + updatedAt] = now; | ||
|  |       } | ||
|  |     } | ||
|  |   } else if (path.schema != null && path.schema != schema && update[key]) { | ||
|  |     const timestamps = path.schema.options.timestamps; | ||
|  |     const createdAt = handleTimestampOption(timestamps, 'createdAt'); | ||
|  |     const updatedAt = handleTimestampOption(timestamps, 'updatedAt'); | ||
|  | 
 | ||
|  |     if (!timestamps) { | ||
|  |       return; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (updatedAt != null) { | ||
|  |       update[key][updatedAt] = now; | ||
|  |     } | ||
|  |     if (createdAt != null) { | ||
|  |       update[key][createdAt] = now; | ||
|  |     } | ||
|  |   } | ||
|  | } |