dld_backend/node_modules/mongoose/lib/helpers/setDefaultsOnInsert.js
2025-10-30 12:13:02 +05:30

162 lines
4.0 KiB
JavaScript

'use strict';
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;
let hasDollarUpdate = false;
for (let i = 0; i < numKeys; ++i) {
if (keys[i].charAt(0) === '$') {
hasDollarUpdate = true;
break;
}
}
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].charAt(0) === '$') {
hasDollarKey = true;
break;
}
}
if (hasDollarKey) {
continue;
}
}
updatedKeys[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 (typeof def === 'undefined') {
return;
}
const pathPieces = schemaType.splitPath();
if (pathPieces.includes('$*')) {
// Skip defaults underneath maps. We should never do `$setOnInsert` on a path with `$*`
return;
}
if (isModified(castedDoc, updatedKeys, path, pathPieces, hasDollarUpdate)) {
return;
}
castedDoc = castedDoc || {};
castedDoc.$setOnInsert = castedDoc.$setOnInsert || {};
if (get(castedDoc, path) == null) {
castedDoc.$setOnInsert[path] = def;
}
updatedValues[path] = def;
});
return castedDoc;
};
function isModified(castedDoc, updatedKeys, path, pathPieces, hasDollarUpdate) {
// Check if path is in filter (updatedKeys)
if (updatedKeys[path]) {
return true;
}
// Check if any parent path is in filter
let cur = pathPieces[0];
for (let i = 1; i < pathPieces.length; ++i) {
if (updatedKeys[cur]) {
return true;
}
cur += '.' + pathPieces[i];
}
// Check if path is modified in the update
if (hasDollarUpdate) {
// Check each update operator
for (const key in castedDoc) {
if (key.charAt(0) === '$') {
if (pathExistsInUpdate(castedDoc[key], path, pathPieces)) {
return true;
}
}
}
} else {
// No dollar operators, check the castedDoc directly
if (pathExistsInUpdate(castedDoc, path, pathPieces)) {
return true;
}
}
return false;
}
function pathExistsInUpdate(update, targetPath, pathPieces) {
if (update == null || typeof update !== 'object') {
return false;
}
// Check exact match
if (update.hasOwnProperty(targetPath)) {
return true;
}
// Check if any parent path exists
let cur = pathPieces[0];
for (let i = 1; i < pathPieces.length; ++i) {
if (update.hasOwnProperty(cur)) {
return true;
}
cur += '.' + pathPieces[i];
}
// Check if any child path exists (e.g., path is "a" and update has "a.b")
const prefix = targetPath + '.';
for (const key in update) {
if (key.startsWith(prefix)) {
return true;
}
}
return false;
}