mingo
Version:
MongoDB query language for in-memory objects
178 lines (177 loc) • 7.28 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var densify_exports = {};
__export(densify_exports, {
$densify: () => $densify
});
module.exports = __toCommonJS(densify_exports);
var import_lazy = require("../../lazy");
var import_util = require("../../util");
var import_internal = require("../expression/date/_internal");
var import_dateAdd = require("../expression/date/dateAdd");
var import_sort = require("./sort");
const $densify = (collection, expr, options) => {
const { step, bounds, unit } = expr.range;
if (unit) {
(0, import_util.assert)(import_internal.TIME_UNITS.includes(unit), "");
(0, import_util.assert)(
Number.isInteger(step) && step > 0,
"The step parameter in a range statement must be a whole number when densifying a date range."
);
} else {
(0, import_util.assert)(
(0, import_util.isNumber)(step) && step > 0,
"The step parameter in a range statement must be a strictly positive numeric value."
);
}
if ((0, import_util.isArray)(bounds)) {
(0, import_util.assert)(
!!bounds && bounds.length === 2,
"A bounding array in a range statement must have exactly two elements."
);
(0, import_util.assert)(
(bounds.every(import_util.isNumber) || bounds.every(import_util.isDate)) && bounds[0] < bounds[1],
"A bounding array must be an ascending array of either two dates or two numbers."
);
(0, import_util.assert)(
unit && !bounds.some(import_util.isNumber),
"Numeric bounds may not have unit parameter."
);
}
if (expr.partitionByFields) {
(0, import_util.assert)(
(0, import_util.isArray)(expr.partitionByFields),
"$densify: `partitionByFields` must be an array of strings"
);
}
collection = (0, import_sort.$sort)(collection, { [expr.field]: 1 }, options);
const computeNextValue = (value) => {
return (0, import_util.isNumber)(value) ? value + step : (0, import_dateAdd.$dateAdd)(null, { startDate: value, unit, amount: step }, options);
};
const isValidUnit = !!unit && import_internal.TIME_UNITS.includes(unit);
const getFieldValue = (o) => {
const v = (0, import_util.resolve)(o, expr.field);
(0, import_util.assert)(
(0, import_util.isNil)(v) || (0, import_util.isDate)(v) && isValidUnit || (0, import_util.isNumber)(v) && !isValidUnit,
"$densify: Densify field type must be numeric with 'unit' unspecified, or a date with 'unit' specified."
);
return v;
};
const peekItem = new Array();
const nilFieldsIterator = (0, import_lazy.Lazy)(() => {
const item = collection.next();
const fieldValue = getFieldValue(item.value);
if ((0, import_util.isNil)(fieldValue)) return item;
peekItem.push(item);
return { done: true };
});
const nextDensifyValueMap = import_util.HashMap.init();
const [lower, upper] = (0, import_util.isArray)(bounds) ? bounds : [bounds, bounds];
let maxFieldValue = void 0;
const updateMaxFieldValue = (value) => {
maxFieldValue = maxFieldValue === void 0 || maxFieldValue < value ? value : maxFieldValue;
};
const rootKey = [];
const densifyIterator = (0, import_lazy.Lazy)(() => {
const item = peekItem.length > 0 ? peekItem.pop() : collection.next();
if (item.done) return item;
let partitionKey = rootKey;
if ((0, import_util.isArray)(expr.partitionByFields)) {
partitionKey = expr.partitionByFields.map(
(k) => (0, import_util.resolve)(item.value, k)
);
(0, import_util.assert)(
partitionKey.every(import_util.isString),
"$densify: Partition fields must evaluate to string values."
);
}
(0, import_util.assert)((0, import_util.isObject)(item.value), "$densify: collection must contain documents");
const itemValue = getFieldValue(item.value);
if (!nextDensifyValueMap.has(partitionKey)) {
if (lower == "full") {
if (!nextDensifyValueMap.has(rootKey)) {
nextDensifyValueMap.set(rootKey, itemValue);
}
nextDensifyValueMap.set(partitionKey, nextDensifyValueMap.get(rootKey));
} else if (lower == "partition") {
nextDensifyValueMap.set(partitionKey, itemValue);
} else {
nextDensifyValueMap.set(partitionKey, lower);
}
}
const densifyValue = nextDensifyValueMap.get(partitionKey);
if (
// current item field value is lower than current densify value.
itemValue <= densifyValue || // range value equals or exceeds upper bound
upper != "full" && upper != "partition" && densifyValue >= upper
) {
if (densifyValue <= itemValue) {
nextDensifyValueMap.set(partitionKey, computeNextValue(densifyValue));
}
updateMaxFieldValue(itemValue);
return item;
}
nextDensifyValueMap.set(partitionKey, computeNextValue(densifyValue));
updateMaxFieldValue(densifyValue);
const denseObj = { [expr.field]: densifyValue };
if (partitionKey) {
partitionKey.forEach((v, i) => {
denseObj[expr.partitionByFields[i]] = v;
});
}
peekItem.push(item);
return { done: false, value: denseObj };
});
if (lower !== "full") return (0, import_lazy.concat)(nilFieldsIterator, densifyIterator);
let paritionIndex = -1;
let partitionKeysSet = void 0;
const fullBoundsIterator = (0, import_lazy.Lazy)(() => {
if (paritionIndex === -1) {
const fullDensifyValue = nextDensifyValueMap.get(rootKey);
nextDensifyValueMap.delete(rootKey);
partitionKeysSet = Array.from(nextDensifyValueMap.keys());
if (partitionKeysSet.length === 0) {
partitionKeysSet.push(rootKey);
nextDensifyValueMap.set(rootKey, fullDensifyValue);
}
paritionIndex++;
}
do {
const partitionKey = partitionKeysSet[paritionIndex];
const partitionMaxValue = nextDensifyValueMap.get(partitionKey);
if (partitionMaxValue < maxFieldValue) {
nextDensifyValueMap.set(
partitionKey,
computeNextValue(partitionMaxValue)
);
const denseObj = { [expr.field]: partitionMaxValue };
partitionKey.forEach((v, i) => {
denseObj[expr.partitionByFields[i]] = v;
});
return { done: false, value: denseObj };
}
paritionIndex++;
} while (paritionIndex < partitionKeysSet.length);
return { done: true };
});
return (0, import_lazy.concat)(nilFieldsIterator, densifyIterator, fullBoundsIterator);
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
$densify
});