HTMLify
setDefaultsOnInsert.js
Views: 6 | Author: cody
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | '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) { const keys = Object.keys(castedDoc || {}); const updatedKeys = {}; const updatedValues = {}; const numKeys = keys.length; const modified = {}; let hasDollarUpdate = false; options = options || {}; if (!options.upsert || !options.setDefaultsOnInsert) { return castedDoc; } 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; } |