64 lines
1.8 KiB
JavaScript
64 lines
1.8 KiB
JavaScript
import { Aggregator } from "../../aggregator";
|
|
import { computeValue } from "../../core";
|
|
import {
|
|
ensureArray,
|
|
flatten,
|
|
isArray,
|
|
isString,
|
|
resolve,
|
|
ValueMap
|
|
} from "../../util";
|
|
const $lookup = (collection, expr, options) => {
|
|
const joinColl = isString(expr.from) ? options?.collectionResolver(expr.from) : expr.from;
|
|
const { let: letExpr, pipeline, foreignField, localField } = expr;
|
|
const subQueryPipeline = pipeline || [];
|
|
let lookupEq = (_) => [true, []];
|
|
if (foreignField && localField) {
|
|
const map = ValueMap.init(options.hashFunction);
|
|
for (const doc of joinColl) {
|
|
ensureArray(resolve(doc, foreignField) ?? null).forEach((v) => {
|
|
const xs = map.get(v);
|
|
const arr = xs ?? [];
|
|
arr.push(doc);
|
|
if (arr !== xs) map.set(v, arr);
|
|
});
|
|
}
|
|
lookupEq = (o) => {
|
|
const local = resolve(o, localField) ?? null;
|
|
if (isArray(local)) {
|
|
if (subQueryPipeline.length) {
|
|
return [local.some((v) => map.has(v)), null];
|
|
}
|
|
const result2 = Array.from(
|
|
new Set(flatten(local.map((v) => map.get(v), options.hashFunction)))
|
|
);
|
|
return [result2.length > 0, result2];
|
|
}
|
|
const result = map.get(local) ?? null;
|
|
return [result !== null, result ?? []];
|
|
};
|
|
if (subQueryPipeline.length === 0) {
|
|
return collection.map((obj) => {
|
|
return {
|
|
...obj,
|
|
[expr.as]: lookupEq(obj).pop()
|
|
};
|
|
});
|
|
}
|
|
}
|
|
const agg = new Aggregator(subQueryPipeline, options);
|
|
const opts = { ...options };
|
|
return collection.map((obj) => {
|
|
const vars = computeValue(obj, letExpr, null, options);
|
|
opts.variables = { ...options.variables, ...vars };
|
|
const [ok, res] = lookupEq(obj);
|
|
return {
|
|
...obj,
|
|
[expr.as]: ok ? agg.run(joinColl, opts) : res
|
|
};
|
|
});
|
|
};
|
|
export {
|
|
$lookup
|
|
};
|