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