UNPKG

mingo

Version:

MongoDB query language for in-memory objects

200 lines (199 loc) 7.86 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 internal_exports = {}; __export(internal_exports, { DEFAULT_OPTIONS: () => DEFAULT_OPTIONS, Trie: () => Trie, applyUpdate: () => applyUpdate, buildParams: () => buildParams, clone: () => clone, walkExpression: () => walkExpression }); module.exports = __toCommonJS(internal_exports); var import_internal = require("../../core/_internal"); var booleanOperators = __toESM(require("../../operators/expression/boolean")); var comparisonOperators = __toESM(require("../../operators/expression/comparison")); var queryOperators = __toESM(require("../../operators/query")); var import_query = require("../../query"); var import_internal2 = require("../../util/_internal"); const DEFAULT_OPTIONS = import_internal.ComputeOptions.init({ context: import_internal.Context.init().addQueryOps(queryOperators).addExpressionOps(booleanOperators).addExpressionOps(comparisonOperators) }).update({ updateConfig: { cloneMode: "copy" } }); const clone = (val, opts) => { const mode = opts?.local?.updateConfig?.cloneMode ?? "copy"; switch (mode) { case "deep": return (0, import_internal2.cloneDeep)(val); case "copy": { if ((0, import_internal2.isDate)(val)) return new Date(val); if ((0, import_internal2.isArray)(val)) return val.slice(); if ((0, import_internal2.isObject)(val)) return Object.assign({}, val); if ((0, import_internal2.isRegExp)(val)) return new RegExp(val); return val; } default: return val; } }; const FIRST_ONLY = "$"; const ARRAY_WIDE = "$[]"; const applyUpdate = (o, n, q, f, opts) => { const { selector, position: c, next } = n; if (!c) { let b = false; const g = (u, k) => b = Boolean(f(u, k)) || b; (0, import_internal2.walk)(o, selector, g, opts); return b; } const arr = (0, import_internal2.resolve)(o, selector); if (!(0, import_internal2.isArray)(arr) || !arr.length) return false; if (c === FIRST_ONLY) { const i = arr.findIndex((e) => q[selector].test({ [selector]: [e] })); if (i === -1) return false; return next ? applyUpdate(arr[i], next, q, f, opts) : f(arr, i); } return arr.map((e, i) => { if (c !== ARRAY_WIDE && q[c] && !q[c].test({ [c]: [e] })) return false; return next ? applyUpdate(e, next, q, f, opts) : f(arr, i); }).some(Boolean); }; const ERR_MISSING_FIELD = "You must include the array field for '.$' as part of the query document."; const ERR_IMMUTABLE_FIELD = (path, idKey) => `Performing an update on the path '${path}' would modify the immutable field '${idKey}'.`; function walkExpression(expr, arrayFilters, options, callback) { const opts = options instanceof import_internal.ComputeOptions ? options : import_internal.ComputeOptions.init(options); const params = opts.local.updateParams ?? buildParams([expr], arrayFilters, opts); const modified = []; for (const [key, val] of Object.entries(expr)) { const { node, queries } = params[key]; if (callback(val, node, queries)) modified.push(node.selector); } return modified.sort(); } function buildParams(exprList, arrayFilters, options) { const params = {}; arrayFilters ||= []; const filterIndexMap = Object.fromEntries( arrayFilters.map((o, i) => [Object.entries(o).pop()[0].split(".")[0], i]) ); const { condition } = options.local; const queryKeys = condition && Object.keys(condition); const conflictDetector = new Trie(); for (const expr of exprList) { for (const selector of Object.keys(expr)) { const identifiers = []; const node = selector.includes("$") ? { selector: void 0 } : { selector }; if (!node.selector) { selector.split(".").reduce((n, v) => { if (v === FIRST_ONLY || v === ARRAY_WIDE) { n.position = v; } else if (v.startsWith("$[") && v.endsWith("]")) { const id = v.slice(2, -1); (0, import_internal2.assert)( /^[a-z]+\w*$/.test(id), `The filter <identifier> must begin with a lowercase letter and contain only alphanumeric characters. '${v}' is invalid.` ); identifiers.push(id); n.position = id; } else if (!n.selector) { n.selector = v; } else if (!n.position) { n.selector += "." + v; } else { n.next = { selector: v }; return n.next; } return n; }, node); } const queries = {}; if (identifiers.length) { const filters = {}; identifiers.forEach((v) => { filters[v] = arrayFilters[filterIndexMap[v]]; }); for (const [k, c] of Object.entries(filters)) { queries[k] = new import_query.Query(c, options); } } if (node.position === FIRST_ONLY) { const field = node.selector; (0, import_internal2.assert)(queryKeys && queryKeys.length, ERR_MISSING_FIELD); const matches = queryKeys.filter( (k2) => k2 === field || k2.startsWith(field + ".") ); (0, import_internal2.assert)(matches.length === 1, ERR_MISSING_FIELD); const k = matches[0]; queries[field] = new import_query.Query({ [k]: condition[k] }, options); } const idKey = options.idKey; (0, import_internal2.assert)( node.selector !== idKey && !node.selector.startsWith(`${idKey}.`), ERR_IMMUTABLE_FIELD(node.selector, idKey) ); (0, import_internal2.assert)( conflictDetector.add(node.selector), `updating the path '${node.selector}' would create a conflict at '${node.selector}'` ); params[selector] = { node, queries }; } } return params; } class Trie { constructor() { this.root = { children: /* @__PURE__ */ new Map(), isTerminal: false }; } add(selector) { const parts = selector.split("."); let current = this.root; for (const part of parts) { if (current.isTerminal) return false; if (!current.children.has(part)) { current.children.set(part, { children: /* @__PURE__ */ new Map(), isTerminal: false }); } current = current.children.get(part); } if (current.isTerminal || current.children.size) return false; return current.isTerminal = true; } } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { DEFAULT_OPTIONS, Trie, applyUpdate, buildParams, clone, walkExpression });