Spaces:
Runtime error
Runtime error
File size: 3,321 Bytes
9756872 | 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 123 124 125 126 127 128 129 130 131 132 133 | '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 === false) {
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
if (schemaType.path === '_id' && schemaType.options.auto) {
return;
}
const def = schemaType.getDefault(null, true);
if (isModified(modified, path)) {
return;
}
if (typeof def === 'undefined') {
return;
}
if (schemaType.splitPath().includes('$*')) {
// Skip defaults underneath maps. We should never do `$setOnInsert` on a path with `$*`
return;
}
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;
}
// Is any parent path of `path` modified?
const sp = path.split('.');
let cur = sp[0];
for (let i = 1; i < sp.length; ++i) {
if (modified[cur]) {
return true;
}
cur += '.' + sp[i];
}
// Is any child of `path` modified?
const modifiedKeys = Object.keys(modified);
if (modifiedKeys.length) {
const parentPath = path + '.';
for (const modifiedPath of modifiedKeys) {
if (modifiedPath.slice(0, path.length + 1) === parentPath) {
return true;
}
}
}
return false;
}
|