UNPKG

popurelate

Version:

Easily populate and filter documents using data from related collections.

217 lines (216 loc) 8.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.haveCommonPath = exports.recomposePipelines = exports.canBeMerged = void 0; const errors_1 = require("../errors"); const path_1 = require("../path"); const deps_1 = require("./deps"); const is_1 = require("./is"); function canBeMerged(pipeline1, pipeline2, finder, actuallyMerge) { var _a, _b, _c, _d; const nah = actuallyMerge ? null : false; const type1 = is_1.getPipelineType(pipeline1.pipeline); const type2 = is_1.getPipelineType(pipeline2.pipeline); if (pipeline1.removable || pipeline2.removable) return nah; if (type1 === null || type2 === null) return nah; if (!pipeline1.useOptimizer || !pipeline2.useOptimizer) return nah; if (type1 !== type2) return nah; if (pipeline1.IAmChangingFields && pipeline2.IAmChangingFields && pipeline1.IAmChangingFields.isChangingEveryField !== pipeline2.IAmChangingFields.isChangingEveryField) { return nah; } if (type1 === "query") { if (haveCommonElement(pipeline1.IAmDependedOnFields, pipeline2.IAmDependedOnFields) || !canQueriesBeMerged(pipeline1.pipeline, pipeline2.pipeline)) { return nah; } if (!actuallyMerge) return true; return mergeWrapped(pipeline1, pipeline2, finder, mergeQueryPipelines(pipeline1.pipeline, pipeline2.pipeline)); } if (type1 === "addFields") { if (((_a = pipeline1.IAmChangingFields) === null || _a === void 0 ? void 0 : _a.isChangingEveryField) || ((_b = pipeline2.IAmChangingFields) === null || _b === void 0 ? void 0 : _b.isChangingEveryField)) { return nah; } if (exports.haveCommonPath(((_c = pipeline1.IAmChangingFields) === null || _c === void 0 ? void 0 : _c.fields) || new Set(), ((_d = pipeline2.IAmChangingFields) === null || _d === void 0 ? void 0 : _d.fields) || new Set())) { return nah; } // strict check if (!actuallyMerge) return true; return mergeWrapped(pipeline1, pipeline2, finder, mergeAddFieldsPipelines(pipeline1.pipeline, pipeline2.pipeline)); } if (type1 === "populate") { const pip1 = pipeline1.pipeline; const pip2 = pipeline2.pipeline; if (pip1.field === pip2.field && canPopulatesBeMErged(pip1, pip2)) { if (!actuallyMerge) return true; return mergeWrapped(pipeline1, pipeline2, finder, mergePopulatePipelines(pipeline1.pipeline, pipeline2.pipeline)); } return nah; } return nah; } exports.canBeMerged = canBeMerged; const canQueriesBeMerged = (p1, p2) => { return !haveCommonElement(Object.keys(p1.query), new Set(Object.keys(p2.query))); }; const canPopulatesBeMErged = (p1, p2) => { if (!arePopulationsSameWithoucChildren(p1, p2)) { return false; } const children1 = p1.populate.children; const children2 = p2.populate.children; if (children1 && children2) { const keys = new Set(Object.keys(children1).concat(Object.keys(children2))); for (const key of keys) { const child1 = children1[key]; const child2 = children2[key]; if (!child1 || !child2) continue; if (!canPopulatesBeMErged(child1, child2)) return false; } } return true; }; const arePopulationsSameWithoucChildren = (p1, p2) => { if (!haveSameValues(p1, p2, ["field", "parentIdField", "myIdField"])) { return false; } if (!haveSameValues(p1.populate, p2.populate, [ "modelName", "localField", "foreignField", "globalPathPrefix", "localPathPrefix", "matchesMany", "required", "through", ])) { return false; } return true; }; const haveSameValues = (obj1, obj2, keys) => { for (const key of keys) { if (obj1[key] !== obj2[key]) return false; } return true; }; const recomposePipelines = (pipelines, finder) => { let firstIndex = -1; let secondIndex = -1; for (let i = 0; i < pipelines.length; ++i) { if (pipelines[i].removable) continue; if (secondIndex === -1) { secondIndex = i; continue; } if (secondIndex !== i) firstIndex = secondIndex; secondIndex = i; const p1 = pipelines[firstIndex]; const p2 = pipelines[secondIndex]; const mergable = canBeMerged(p1, p2, finder, true); if (!mergable) continue; pipelines.splice(secondIndex, 1, mergable); pipelines.splice(firstIndex, 1); i--; } return pipelines; }; exports.recomposePipelines = recomposePipelines; const haveCommonElement = (set1, set2) => { for (const el of set1) { if (set2.has(el)) return true; } return false; }; const haveCommonPath = (set1, set2) => { for (const field1 of set1) { for (const field2 of set2) { if (path_1.isSubPathOf(field1, field2) || path_1.isSubPathOf(field2, field1)) { return true; } } } return false; }; exports.haveCommonPath = haveCommonPath; const mergeQueryPipelines = (p1, p2) => { return Object.assign(Object.assign({}, p1), { query: Object.assign(Object.assign({}, p1.query), p2.query) }); }; const mergeAddFieldsPipelines = (p1, p2) => { return Object.assign(Object.assign({}, p1), { addFields: Object.assign(Object.assign({}, p1.addFields), p2.addFields) }); }; const mergePopulatePipelines = (p1, p2) => { const newObj = Object.assign(Object.assign({}, p1), { populate: Object.assign(Object.assign({}, p1.populate), { alreadyPopulated: !!p1.populate.alreadyPopulated && !!p2.populate.alreadyPopulated, children: {} }) }); if (p1.populate.children) { for (const key in p1.populate.children) { const child1 = p1.populate.children[key]; const child2 = p2.populate.children && p2.populate.children[key]; newObj.populate.children[key] = !child2 ? child1 : mergePopulatePipelines(child1, child2); } } if (p2.populate.children) { for (const key in p2.populate.children) { if (p1.populate.children && p1.populate.children[key]) continue; const child = p2.populate.children[key]; newObj.populate.children[key] = child; } } if (Object.keys(newObj.populate.children).length === 0) { delete newObj.populate.children; } return newObj; }; const mergeWrapped = (w1, w2, finder, mergedPipeline) => { const newObj = Object.assign(Object.assign({}, w1), { IAmDependedOnFields: union(w1.IAmDependedOnFields, w2.IAmDependedOnFields), IAmChangingFields: unionChangingFields(w1.IAmChangingFields, w2.IAmChangingFields), pipeline: mergedPipeline, IAmDependedOnPipelineIds: new Set(w1.IAmDependedOnPipelineIds), pipelineIdsDepenedOnMe: new Set(w1.pipelineIdsDepenedOnMe) }); finder[newObj.id] = newObj; deps_1.deps.transferDependences(w2.id, newObj.id, finder); return newObj; }; const union = (set1, set2) => { const newSet = new Set(set1); for (const elem of set2) newSet.add(elem); return newSet; }; const unionChangingFields = (f1, f2) => { if (!f1) return f2; if (!f2) return f1; if (f1.isChangingEveryField) { if (!f2.isChangingEveryField) { throw new errors_1.QueryBuilderError("Merging changing isChangingEveryField=true with isChangingEveryField=false is not supproted"); } return { isChangingEveryField: true, except: union(f1.except, f2.except), }; } else { if (f2.isChangingEveryField) { throw new errors_1.QueryBuilderError("Merging changing isChangingEveryField=false with isChangingEveryField=true is not supproted"); } return { isChangingEveryField: false, fields: union(f1.fields, f2.fields), }; } };