@thi.ng/oquery
Version:
Datalog-inspired, optimized pattern/predicate query engine for JS objects & arrays of objects
77 lines (76 loc) • 1.97 kB
JavaScript
import { isNumber } from "@thi.ng/checks/is-number";
import { isString } from "@thi.ng/checks/is-string";
import { numericOp, stringOp } from "@thi.ng/compare";
const matchStrings = (key, matches, opts) => {
const [includes, excludes] = matches.reduce(
(acc, x) => {
x[0] === "!" ? acc[1].push(x.substring(1)) : acc[0].push(x);
return acc;
},
[[], []]
);
return matchMultiple(key, includes, excludes, opts);
};
const matchMultiple = (key, includes, excludes, opts = {}) => {
const { union = false, value: valueFn } = opts;
return excludes.length ? {
q: [
key,
(values) => {
const $values = valueFn ? valueFn(values) : values;
for (let x of excludes) {
if ($values.includes(x)) return false;
}
let match = false;
for (let x of includes) {
if ($values.includes(x)) {
match = true;
if (union) break;
} else if (!union) {
match = false;
break;
}
}
return match;
}
],
opts: { cwise: false }
} : { q: [key, includes], opts: { intersect: !union } };
};
const matchPattern = (key, expr, opts) => {
let re;
if (expr instanceof RegExp) {
re = expr;
} else {
if (expr === "*") return { q: [key, (x) => x != null], opts };
if (/^[<>=!]/.test(expr)) {
const op = /^[<>=!]+/.exec(expr)[0];
const arg = expr.substring(op.length).trim();
const argN = parseFloat(arg);
return matchCompare(
key,
op,
isNaN(argN) ? arg : argN,
opts
);
}
re = new RegExp(expr, "i");
}
return {
q: [
key,
(x) => (isString(x) || isNumber(x)) && re.test(String(x))
],
opts
};
};
const matchCompare = (key, op, arg, opts) => ({
q: [key, isNumber(arg) ? numericOp(op, arg) : stringOp(op, arg)],
opts
});
export {
matchCompare,
matchMultiple,
matchPattern,
matchStrings
};