UNPKG

mingo

Version:

MongoDB query language for in-memory objects

248 lines (247 loc) 9.43 kB
var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var updater_exports = {}; __export(updater_exports, { update: () => update, updateMany: () => updateMany, updateOne: () => updateOne }); module.exports = __toCommonJS(updater_exports); var import_internal = require("./core/_internal"); var import_lazy = require("./lazy"); var booleanOperators = __toESM(require("./operators/expression/boolean")); var comparisonOperators = __toESM(require("./operators/expression/comparison")); var import_addFields = require("./operators/pipeline/addFields"); var import_project = require("./operators/pipeline/project"); var import_replaceRoot = require("./operators/pipeline/replaceRoot"); var import_replaceWith = require("./operators/pipeline/replaceWith"); var import_set = require("./operators/pipeline/set"); var import_sort = require("./operators/pipeline/sort"); var import_unset = require("./operators/pipeline/unset"); var queryOperators = __toESM(require("./operators/query")); var UPDATE_OPERATORS = __toESM(require("./operators/update")); var import_internal2 = require("./operators/update/_internal"); var import_query = require("./query"); var import_util = require("./util"); const PIPELINE_OPERATORS = { $addFields: import_addFields.$addFields, $set: import_set.$set, $project: import_project.$project, $unset: import_unset.$unset, $replaceRoot: import_replaceRoot.$replaceRoot, $replaceWith: import_replaceWith.$replaceWith }; function update(obj, modifier, arrayFilters, condition, options) { const docs = [obj]; const res = updateOne( docs, condition || {}, modifier, { arrayFilters, cloneMode: options?.cloneMode ?? "copy" }, options?.queryOptions ); return res.modifiedFields ?? []; } function updateMany(documents, condition, modifier, updateConfig = {}, options) { const { modifiedCount, matchedCount } = updateDocuments( documents, condition, modifier, updateConfig, options ); return { modifiedCount, matchedCount }; } function updateOne(documents, condition, modifier, updateConfig = {}, options) { return updateDocuments(documents, condition, modifier, updateConfig, { ...options, firstOnly: true }); } function updateDocuments(documents, condition, modifier, updateConfig = {}, options) { options ||= {}; const firstOnly = options?.firstOnly ?? false; const opts = import_internal.ComputeOptions.init({ ...options, collation: Object.assign({}, options?.collation, updateConfig?.collation) }).update({ condition, updateConfig: { cloneMode: "copy", ...updateConfig }, variables: updateConfig.let }); opts.context.addExpressionOps(booleanOperators).addExpressionOps(comparisonOperators).addQueryOps(queryOperators).addPipelineOps(PIPELINE_OPERATORS); const filterExists = Object.keys(condition).length > 0; const matchedDocs = /* @__PURE__ */ new Map(); let docsIter = (0, import_lazy.Lazy)(documents); if (filterExists) { const query = new import_query.Query(condition, opts); docsIter = docsIter.filter((o, i) => { if (query.test(o)) { matchedDocs.set(o, i); return true; } return false; }); } let modifiedIndex = -1; if (firstOnly) { const indexes = /* @__PURE__ */ new Map(); if (updateConfig.sort) { if (!filterExists) { docsIter = docsIter.map((o, i) => { indexes.set(o, i); return o; }); } docsIter = (0, import_sort.$sort)(docsIter, updateConfig.sort, opts); } docsIter = docsIter.take(1); const firstDoc = docsIter.collect()[0]; modifiedIndex = matchedDocs.get(firstDoc) ?? indexes.get(firstDoc) ?? 0; } const foundDocs = docsIter.collect(); if (foundDocs.length === 0) return { matchedCount: 0, modifiedCount: 0 }; if ((0, import_util.isArray)(modifier)) { const indexes = firstOnly ? [modifiedIndex] : Array.from(matchedDocs.values()); const hashes = indexes.length ? indexes.map((i) => (0, import_util.hashCode)(documents[i])) : foundDocs.map((o) => (0, import_util.hashCode)(o)); const output2 = { matchedCount: hashes.length, modifiedCount: 0 }; const oldFirstDoc = firstOnly ? (0, import_util.cloneDeep)(documents[indexes[0]]) : void 0; let updateIter = (0, import_lazy.Lazy)(foundDocs); for (const stage of modifier) { const [op, expr] = Object.entries(stage)[0]; const pipelineOp = PIPELINE_OPERATORS[op]; (0, import_util.assert)(pipelineOp, `Unknown pipeline operator: '${op}'.`); updateIter = pipelineOp(updateIter, expr, opts); } const matches = updateIter.collect(); if (indexes.length) { (0, import_util.assert)( indexes.length === matches.length, "bug: indexes and result size must match." ); for (let i = 0; i < indexes.length; i++) { if ((0, import_util.hashCode)(matches[i]) !== hashes[i]) { documents[indexes[i]] = matches[i]; output2.modifiedCount++; } } } else { for (let i = 0; i < documents.length; i++) { if ((0, import_util.hashCode)(matches[i]) !== hashes[i]) { documents[i] = matches[i]; output2.modifiedCount++; } } } if (firstOnly && output2.modifiedCount) { const newDoc = documents[indexes[0]]; const modifiedFields2 = getModifiedFields( modifier, oldFirstDoc, newDoc ); (0, import_util.assert)(modifiedFields2.length, "bug: failed to retrieve modified fields"); Object.assign(output2, { modifiedFields: modifiedFields2, modifiedIndex }); } return output2; } const unknownOp = Object.keys(modifier).find((op) => !UPDATE_OPERATORS[op]); (0, import_util.assert)(!unknownOp, `Unknown update operator: '${unknownOp}'.`); const arrayFilters = updateConfig?.arrayFilters ?? []; opts.update({ updateParams: (0, import_internal2.buildParams)( Object.values(modifier), arrayFilters, opts ) }); const matchedCount = foundDocs.length; const output = { matchedCount, modifiedCount: 0 }; const modifiedFields = []; for (const doc of foundDocs) { let modified = false; for (const [op, expr] of Object.entries(modifier)) { const mutate = UPDATE_OPERATORS[op]; const fields = mutate(doc, expr, arrayFilters, opts); if (fields.length) { modified = true; if (firstOnly) Array.prototype.push.apply(modifiedFields, fields); } } output.modifiedCount += +modified; } if (firstOnly && modifiedFields.length) { modifiedFields.sort(); Object.assign(output, { modifiedFields, modifiedIndex }); } return output; } function getModifiedFields(pipeline, oldDoc, newDoc) { const stageFields = []; for (const stage of pipeline) { const op = Object.keys(stage)[0]; switch (op) { case "$addFields": case "$set": case "$project": case "$replaceWith": stageFields.push(...Object.keys(stage[op])); break; case "$unset": stageFields.push(...(0, import_util.ensureArray)(stage[op])); break; case "$replaceRoot": stageFields.push( ...Object.keys(stage[op]?.newRoot || []) ); break; } } const stageFieldsSet = new Set(stageFields.sort()); const conflictDetector = new import_internal2.Trie(); const modifiedFields = []; for (const key of stageFieldsSet) { if (conflictDetector.add(key) && !(0, import_util.isEqual)((0, import_util.resolve)(newDoc, key), (0, import_util.resolve)(oldDoc, key))) { modifiedFields.push(key); } } for (const key of Object.keys(oldDoc)) { if (stageFieldsSet.has(key)) continue; if (!conflictDetector.add(key) || !(0, import_util.isEqual)(newDoc[key], oldDoc[key])) { modifiedFields.push(key); } } const topLevelFilter = new import_internal2.Trie(); return modifiedFields.sort().filter((key) => topLevelFilter.add(key)); } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { update, updateMany, updateOne });