@thi.ng/oquery 2.2.9 → 2.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -1
- package/README.md +1 -1
- package/api.js +0 -1
- package/match.js +70 -160
- package/package.json +11 -8
- package/query.js +273 -365
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
package/api.js
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/match.js
CHANGED
|
@@ -1,169 +1,79 @@
|
|
|
1
1
|
import { isNumber } from "@thi.ng/checks/is-number";
|
|
2
2
|
import { isString } from "@thi.ng/checks/is-string";
|
|
3
3
|
import { numericOp, stringOp } from "@thi.ng/compare";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
*
|
|
14
|
-
* See {@link matchMultiple} for more details & code example.
|
|
15
|
-
*
|
|
16
|
-
* @param key
|
|
17
|
-
* @param matches
|
|
18
|
-
* @param opts
|
|
19
|
-
* @returns
|
|
20
|
-
*/
|
|
21
|
-
export const matchStrings = (key, matches, opts) => {
|
|
22
|
-
const [includes, excludes] = matches.reduce((acc, x) => {
|
|
23
|
-
x[0] === "!" ? acc[1].push(x.substring(1)) : acc[0].push(x);
|
|
24
|
-
return acc;
|
|
25
|
-
}, [[], []]);
|
|
26
|
-
return matchMultiple(key, includes, excludes, opts);
|
|
4
|
+
const matchStrings = (key, matches, opts) => {
|
|
5
|
+
const [includes, excludes] = matches.reduce(
|
|
6
|
+
(acc, x) => {
|
|
7
|
+
x[0] === "!" ? acc[1].push(x.substring(1)) : acc[0].push(x);
|
|
8
|
+
return acc;
|
|
9
|
+
},
|
|
10
|
+
[[], []]
|
|
11
|
+
);
|
|
12
|
+
return matchMultiple(key, includes, excludes, opts);
|
|
27
13
|
};
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
*
|
|
39
|
-
* Note: See {@link matchStrings} for a syntax sugar of this function, aimed at
|
|
40
|
-
* matching `string[]` options.
|
|
41
|
-
*
|
|
42
|
-
* @example
|
|
43
|
-
* ```ts
|
|
44
|
-
* const DB = [
|
|
45
|
-
* { id: 1, tags: ["a", "b"] },
|
|
46
|
-
* { id: 2, tags: ["c", "b"] },
|
|
47
|
-
* { id: 3, tags: ["c", "a"] },
|
|
48
|
-
* ];
|
|
49
|
-
*
|
|
50
|
-
* // tag intersection
|
|
51
|
-
* query(DB, [matchStrings("tags", ["a", "b"])])
|
|
52
|
-
* // [ { id: 1, tags: ["a", "b"] } ]
|
|
53
|
-
*
|
|
54
|
-
* // tag union
|
|
55
|
-
* query(DB, [matchStrings("tags", ["a", "b"])])
|
|
56
|
-
* // here returns full DB...
|
|
57
|
-
* // since each item either has `a` and/or `b` tags
|
|
58
|
-
*
|
|
59
|
-
* // tag exclusion (require `a`, disallow `b`)
|
|
60
|
-
* query(DB, [matchStrings("tags", ["a", "!b"])])
|
|
61
|
-
* // [ { id: 3, tags: ["c", "a"] } ]
|
|
62
|
-
* ```
|
|
63
|
-
*
|
|
64
|
-
* @param key
|
|
65
|
-
* @param matches
|
|
66
|
-
* @param opts
|
|
67
|
-
*/
|
|
68
|
-
export const matchMultiple = (key, includes, excludes, opts) => {
|
|
69
|
-
const { union, value: valueFn } = { union: false, ...opts };
|
|
70
|
-
return excludes.length
|
|
71
|
-
? {
|
|
72
|
-
q: [
|
|
73
|
-
key,
|
|
74
|
-
(values) => {
|
|
75
|
-
const $values = valueFn ? valueFn(values) : values;
|
|
76
|
-
for (let x of excludes) {
|
|
77
|
-
if ($values.includes(x))
|
|
78
|
-
return false;
|
|
79
|
-
}
|
|
80
|
-
let match = false;
|
|
81
|
-
for (let x of includes) {
|
|
82
|
-
if ($values.includes(x)) {
|
|
83
|
-
match = true;
|
|
84
|
-
if (union)
|
|
85
|
-
break;
|
|
86
|
-
}
|
|
87
|
-
else if (!union) {
|
|
88
|
-
match = false;
|
|
89
|
-
break;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return match;
|
|
93
|
-
},
|
|
94
|
-
],
|
|
95
|
-
opts: { cwise: false },
|
|
14
|
+
const matchMultiple = (key, includes, excludes, opts) => {
|
|
15
|
+
const { union, value: valueFn } = { union: false, ...opts };
|
|
16
|
+
return excludes.length ? {
|
|
17
|
+
q: [
|
|
18
|
+
key,
|
|
19
|
+
(values) => {
|
|
20
|
+
const $values = valueFn ? valueFn(values) : values;
|
|
21
|
+
for (let x of excludes) {
|
|
22
|
+
if ($values.includes(x))
|
|
23
|
+
return false;
|
|
96
24
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
* - if `expr` is the sole `*` it will match any non-null value
|
|
108
|
-
* - if `expr` starts with `=`, `!=`, `<`, `<=`, `>=` or `>`, values will be
|
|
109
|
-
* matched using comparators. If the following chars in `expr` are numeric,
|
|
110
|
-
* the comparisons will be done as numbers otherwise as strings. Whitespace
|
|
111
|
-
* between operator and value are ignored.
|
|
112
|
-
*
|
|
113
|
-
* @example
|
|
114
|
-
* ```ts
|
|
115
|
-
* const DB = [
|
|
116
|
-
* { id: "aaa", score: 32 },
|
|
117
|
-
* { id: "bbbb", score: 60 },
|
|
118
|
-
* { id: "c", score: 15 },
|
|
119
|
-
* ];
|
|
120
|
-
*
|
|
121
|
-
* query(DB, [matchPattern("id", /[a-z]{4,}/)]);
|
|
122
|
-
* // [{ id: "bbbb", score: 60 }]
|
|
123
|
-
* query(DB, [matchPattern("id", ">= c")]);
|
|
124
|
-
* // [{ id: "c", score: 15 }]
|
|
125
|
-
*
|
|
126
|
-
* query(DB, [matchPattern("score", "<50")]);
|
|
127
|
-
* // [{ id: "a", score: 32 }, { id: "c", score: 15 }]
|
|
128
|
-
* ```
|
|
129
|
-
*
|
|
130
|
-
* @param key
|
|
131
|
-
* @param expr
|
|
132
|
-
* @param opts
|
|
133
|
-
*/
|
|
134
|
-
export const matchPattern = (key, expr, opts) => {
|
|
135
|
-
let re;
|
|
136
|
-
if (expr instanceof RegExp) {
|
|
137
|
-
re = expr;
|
|
138
|
-
}
|
|
139
|
-
else {
|
|
140
|
-
if (expr === "*")
|
|
141
|
-
return { q: [key, (x) => x != null], opts };
|
|
142
|
-
if (/^[<>=!]/.test(expr)) {
|
|
143
|
-
const op = /^[<>=!]+/.exec(expr)[0];
|
|
144
|
-
const arg = expr.substring(op.length).trim();
|
|
145
|
-
const argN = parseFloat(arg);
|
|
146
|
-
return matchCompare(key, op, isNaN(argN) ? arg : argN, opts);
|
|
25
|
+
let match = false;
|
|
26
|
+
for (let x of includes) {
|
|
27
|
+
if ($values.includes(x)) {
|
|
28
|
+
match = true;
|
|
29
|
+
if (union)
|
|
30
|
+
break;
|
|
31
|
+
} else if (!union) {
|
|
32
|
+
match = false;
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
147
35
|
}
|
|
148
|
-
|
|
36
|
+
return match;
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
opts: { cwise: false }
|
|
40
|
+
} : { q: [key, includes], opts: { intersect: !union } };
|
|
41
|
+
};
|
|
42
|
+
const matchPattern = (key, expr, opts) => {
|
|
43
|
+
let re;
|
|
44
|
+
if (expr instanceof RegExp) {
|
|
45
|
+
re = expr;
|
|
46
|
+
} else {
|
|
47
|
+
if (expr === "*")
|
|
48
|
+
return { q: [key, (x) => x != null], opts };
|
|
49
|
+
if (/^[<>=!]/.test(expr)) {
|
|
50
|
+
const op = /^[<>=!]+/.exec(expr)[0];
|
|
51
|
+
const arg = expr.substring(op.length).trim();
|
|
52
|
+
const argN = parseFloat(arg);
|
|
53
|
+
return matchCompare(
|
|
54
|
+
key,
|
|
55
|
+
op,
|
|
56
|
+
isNaN(argN) ? arg : argN,
|
|
57
|
+
opts
|
|
58
|
+
);
|
|
149
59
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
60
|
+
re = new RegExp(expr, "i");
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
q: [
|
|
64
|
+
key,
|
|
65
|
+
(x) => (isString(x) || isNumber(x)) && re.test(String(x))
|
|
66
|
+
],
|
|
67
|
+
opts
|
|
68
|
+
};
|
|
157
69
|
};
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
*
|
|
162
|
-
* @param key
|
|
163
|
-
* @param match
|
|
164
|
-
* @param opts
|
|
165
|
-
*/
|
|
166
|
-
export const matchCompare = (key, op, arg, opts) => ({
|
|
167
|
-
q: [key, isNumber(arg) ? numericOp(op, arg) : stringOp(op, arg)],
|
|
168
|
-
opts,
|
|
70
|
+
const matchCompare = (key, op, arg, opts) => ({
|
|
71
|
+
q: [key, isNumber(arg) ? numericOp(op, arg) : stringOp(op, arg)],
|
|
72
|
+
opts
|
|
169
73
|
});
|
|
74
|
+
export {
|
|
75
|
+
matchCompare,
|
|
76
|
+
matchMultiple,
|
|
77
|
+
matchPattern,
|
|
78
|
+
matchStrings
|
|
79
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/oquery",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.10",
|
|
4
4
|
"description": "Datalog-inspired, optimized pattern/predicate query engine for JS objects & arrays of objects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
"author": "Karsten Schmidt (https://thi.ng)",
|
|
25
25
|
"license": "Apache-2.0",
|
|
26
26
|
"scripts": {
|
|
27
|
-
"build": "yarn
|
|
27
|
+
"build": "yarn build:esbuild && yarn build:decl",
|
|
28
|
+
"build:decl": "tsc --declaration --emitDeclarationOnly",
|
|
29
|
+
"build:esbuild": "esbuild --format=esm --platform=neutral --target=es2022 --tsconfig=tsconfig.json --outdir=. src/**/*.ts",
|
|
28
30
|
"clean": "rimraf --glob '*.js' '*.d.ts' '*.map' doc",
|
|
29
31
|
"doc": "typedoc --excludePrivate --excludeInternal --out doc src/index.ts",
|
|
30
32
|
"doc:ae": "mkdir -p .ae/doc .ae/temp && api-extractor run --local --verbose",
|
|
@@ -33,14 +35,15 @@
|
|
|
33
35
|
"test": "bun test"
|
|
34
36
|
},
|
|
35
37
|
"dependencies": {
|
|
36
|
-
"@thi.ng/api": "^8.9.
|
|
37
|
-
"@thi.ng/checks": "^3.4.
|
|
38
|
-
"@thi.ng/compare": "^2.2.
|
|
39
|
-
"@thi.ng/defmulti": "^3.0.
|
|
40
|
-
"@thi.ng/equiv": "^2.1.
|
|
38
|
+
"@thi.ng/api": "^8.9.12",
|
|
39
|
+
"@thi.ng/checks": "^3.4.12",
|
|
40
|
+
"@thi.ng/compare": "^2.2.8",
|
|
41
|
+
"@thi.ng/defmulti": "^3.0.10",
|
|
42
|
+
"@thi.ng/equiv": "^2.1.37"
|
|
41
43
|
},
|
|
42
44
|
"devDependencies": {
|
|
43
45
|
"@microsoft/api-extractor": "^7.38.3",
|
|
46
|
+
"esbuild": "^0.19.8",
|
|
44
47
|
"rimraf": "^5.0.5",
|
|
45
48
|
"tools": "^0.0.1",
|
|
46
49
|
"typedoc": "^0.25.4",
|
|
@@ -93,5 +96,5 @@
|
|
|
93
96
|
],
|
|
94
97
|
"year": 2020
|
|
95
98
|
},
|
|
96
|
-
"gitHead": "
|
|
99
|
+
"gitHead": "5e7bafedfc3d53bc131469a28de31dd8e5b4a3ff\n"
|
|
97
100
|
}
|
package/query.js
CHANGED
|
@@ -6,428 +6,336 @@ import { compareByKey } from "@thi.ng/compare/keys";
|
|
|
6
6
|
import { reverse } from "@thi.ng/compare/reverse";
|
|
7
7
|
import { defmulti } from "@thi.ng/defmulti/defmulti";
|
|
8
8
|
import { equiv } from "@thi.ng/equiv";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
*
|
|
12
|
-
* @param x -
|
|
13
|
-
*
|
|
14
|
-
* @internal
|
|
15
|
-
*/
|
|
16
|
-
const classify = (x) => (x != null ? (isFunction(x) ? "f" : "l") : "n");
|
|
17
|
-
/** @internal */
|
|
18
|
-
const ensureArray = (src) => (isArray(src) ? src : [...src]);
|
|
19
|
-
/** @internal */
|
|
9
|
+
const classify = (x) => x != null ? isFunction(x) ? "f" : "l" : "n";
|
|
10
|
+
const ensureArray = (src) => isArray(src) ? src : [...src];
|
|
20
11
|
const ensureSet = (src) => isArray(src) ? new Set(src) : src;
|
|
21
|
-
/**
|
|
22
|
-
* HOF predicate function for intersection queries (i.e. iff `opts.intersect =
|
|
23
|
-
* true`). Takes an array or set query term and returns predicate to check if
|
|
24
|
-
* ALL of the query term elements are present in a given object (assumed to be
|
|
25
|
-
* an array or set too).
|
|
26
|
-
*
|
|
27
|
-
* @param src -
|
|
28
|
-
*
|
|
29
|
-
* @internal
|
|
30
|
-
*/
|
|
31
12
|
const intersect = (src) => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
13
|
+
const a = ensureArray(src);
|
|
14
|
+
const num = a.length;
|
|
15
|
+
return (b) => {
|
|
16
|
+
const $b = ensureSet(b);
|
|
17
|
+
for (let i = num; i-- > 0; ) {
|
|
18
|
+
if (!$b.has(a[i]))
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
return true;
|
|
22
|
+
};
|
|
42
23
|
};
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
* predicate function to check if an element is included. Otherwise, returns
|
|
46
|
-
* input. If `isec` is true, the returned predicate will be the result of
|
|
47
|
-
* {@link intersect}.
|
|
48
|
-
*
|
|
49
|
-
* @param x -
|
|
50
|
-
* @param isec -
|
|
51
|
-
*
|
|
52
|
-
* @internal
|
|
53
|
-
*/
|
|
54
|
-
const coerce = (x, isec = false) => isArray(x)
|
|
55
|
-
? isec
|
|
56
|
-
? intersect(x)
|
|
57
|
-
: (y) => x.includes(y)
|
|
58
|
-
: isSet(x)
|
|
59
|
-
? isec
|
|
60
|
-
? intersect(x)
|
|
61
|
-
: (y) => x.has(y)
|
|
62
|
-
: x;
|
|
63
|
-
/**
|
|
64
|
-
* Similar to {@link coerce}, but intended for S,P terms. Unless `x` is a
|
|
65
|
-
* function or null, coerces `x` (or its elements) to strings first.
|
|
66
|
-
*
|
|
67
|
-
* @param x -
|
|
68
|
-
*
|
|
69
|
-
* @internal
|
|
70
|
-
*/
|
|
71
|
-
const coerceStr = (x) => isArray(x)
|
|
72
|
-
? coerce(x.map((y) => String(y)))
|
|
73
|
-
: isSet(x)
|
|
74
|
-
? coerce(new Set([...x].map((y) => String(y))))
|
|
75
|
-
: x == null || isFunction(x)
|
|
76
|
-
? x
|
|
77
|
-
: String(x);
|
|
24
|
+
const coerce = (x, isec = false) => isArray(x) ? isec ? intersect(x) : (y) => x.includes(y) : isSet(x) ? isec ? intersect(x) : (y) => x.has(y) : x;
|
|
25
|
+
const coerceStr = (x) => isArray(x) ? coerce(x.map((y) => String(y))) : isSet(x) ? coerce(new Set([...x].map((y) => String(y)))) : x == null || isFunction(x) ? x : String(x);
|
|
78
26
|
const addTriple = (acc, s, p, o) => {
|
|
79
|
-
|
|
80
|
-
|
|
27
|
+
const sval = acc[s];
|
|
28
|
+
sval ? sval[p] = o : acc[s] = { [p]: o };
|
|
81
29
|
};
|
|
82
30
|
const match = (o, val, opts) => {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
31
|
+
if (val != null) {
|
|
32
|
+
const pred = isFunction(o) ? o : ($) => opts.equiv(o, $);
|
|
33
|
+
return opts.cwise && isArray(val) ? val.some(pred) : pred(val);
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
88
36
|
};
|
|
89
37
|
const collect = (acc, s, p, o, val, opts) => {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
addTriple(acc, s, p, val);
|
|
98
|
-
}
|
|
38
|
+
if (val != null) {
|
|
39
|
+
const pred = isFunction(o) ? o : ($) => opts.equiv(o, $);
|
|
40
|
+
if (opts.cwise && isArray(val)) {
|
|
41
|
+
val = val.filter(pred);
|
|
42
|
+
val.length && addTriple(acc, s, p, val);
|
|
43
|
+
} else if (pred(val)) {
|
|
44
|
+
addTriple(acc, s, p, val);
|
|
99
45
|
}
|
|
46
|
+
}
|
|
100
47
|
};
|
|
101
|
-
const collectFull = (res, s, val) =>
|
|
48
|
+
const collectFull = (res, s, val) => res[s] = val;
|
|
102
49
|
const collectSP = (res, sval, s, p, o, opts) => {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
50
|
+
if (opts.partial) {
|
|
51
|
+
for (let $p in sval) {
|
|
52
|
+
p($p) && collect(res, s, $p, o, sval[$p], opts);
|
|
107
53
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
54
|
+
} else {
|
|
55
|
+
for (let $p in sval) {
|
|
56
|
+
if (p($p) && match(o, sval[$p], opts)) {
|
|
57
|
+
collectFull(res, s, sval);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
115
60
|
}
|
|
61
|
+
}
|
|
116
62
|
};
|
|
117
63
|
const collectSO = (res, sval, s, o, opts) => {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
64
|
+
if (opts.partial) {
|
|
65
|
+
for (let p in sval) {
|
|
66
|
+
collect(res, s, p, o, sval[p], opts);
|
|
122
67
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
68
|
+
} else {
|
|
69
|
+
for (let p in sval) {
|
|
70
|
+
if (match(o, sval[p], opts)) {
|
|
71
|
+
collectFull(res, s, sval);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
130
74
|
}
|
|
75
|
+
}
|
|
131
76
|
};
|
|
132
77
|
const queryLL = (res, db, s, p, o, opts) => {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
78
|
+
const sval = db[s];
|
|
79
|
+
const val = sval?.[p];
|
|
80
|
+
if (opts.partial) {
|
|
81
|
+
collect(res, s, p, o, val, opts);
|
|
82
|
+
} else {
|
|
83
|
+
match(o, val, opts) && collectFull(res, s, sval);
|
|
84
|
+
}
|
|
141
85
|
};
|
|
142
86
|
const queryLF = (res, db, s, p, o, opts) => {
|
|
143
|
-
|
|
144
|
-
|
|
87
|
+
const sval = db[s];
|
|
88
|
+
sval != null && collectSP(res, sval, s, p, o, opts);
|
|
145
89
|
};
|
|
146
90
|
const queryLN = (res, db, s, _, o, opts) => {
|
|
147
|
-
|
|
148
|
-
|
|
91
|
+
const sval = db[s];
|
|
92
|
+
sval != null && collectSO(res, sval, s, o, opts);
|
|
149
93
|
};
|
|
150
94
|
const queryFL = (res, db, s, p, o, opts) => {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
95
|
+
if (opts.partial) {
|
|
96
|
+
for (let $s in db) {
|
|
97
|
+
s($s) && collect(res, $s, p, o, db[$s]?.[p], opts);
|
|
155
98
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
match(o, sval?.[p], opts) &&
|
|
161
|
-
collectFull(res, $s, sval);
|
|
162
|
-
}
|
|
99
|
+
} else {
|
|
100
|
+
for (let $s in db) {
|
|
101
|
+
const sval = db[$s];
|
|
102
|
+
s($s) && match(o, sval?.[p], opts) && collectFull(res, $s, sval);
|
|
163
103
|
}
|
|
104
|
+
}
|
|
164
105
|
};
|
|
165
106
|
const queryFF = (res, db, s, p, o, opts) => {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
107
|
+
for (let $s in db) {
|
|
108
|
+
s($s) && collectSP(res, db[$s], $s, p, o, opts);
|
|
109
|
+
}
|
|
169
110
|
};
|
|
170
111
|
const queryFN = (res, db, s, _, o, opts) => {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
112
|
+
for (let $s in db) {
|
|
113
|
+
s($s) && collectSO(res, db[$s], $s, o, opts);
|
|
114
|
+
}
|
|
174
115
|
};
|
|
175
116
|
const queryNL = (res, db, _, p, o, opts) => {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
117
|
+
if (opts.partial) {
|
|
118
|
+
for (let s in db) {
|
|
119
|
+
collect(res, s, p, o, db[s][p], opts);
|
|
180
120
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
121
|
+
} else {
|
|
122
|
+
for (let s in db) {
|
|
123
|
+
const sval = db[s];
|
|
124
|
+
match(o, sval[p], opts) && collectFull(res, s, sval);
|
|
186
125
|
}
|
|
126
|
+
}
|
|
187
127
|
};
|
|
188
128
|
const queryNF = (res, db, _, p, o, opts) => {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
129
|
+
for (let s in db) {
|
|
130
|
+
collectSP(res, db[s], s, p, o, opts);
|
|
131
|
+
}
|
|
192
132
|
};
|
|
193
133
|
const queryNN = (res, db, _, __, o, opts) => {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
134
|
+
for (let s in db) {
|
|
135
|
+
collectSO(res, db[s], s, o, opts);
|
|
136
|
+
}
|
|
197
137
|
};
|
|
198
138
|
const querySP = (res, sval, s, p, _, opts) => {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
139
|
+
if (opts.partial) {
|
|
140
|
+
for (let q in sval) {
|
|
141
|
+
if (p(q)) {
|
|
142
|
+
const val = sval[q];
|
|
143
|
+
val != null && addTriple(res, s, q, val);
|
|
144
|
+
}
|
|
206
145
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
}
|
|
146
|
+
} else {
|
|
147
|
+
for (let q in sval) {
|
|
148
|
+
if (p(q)) {
|
|
149
|
+
collectFull(res, s, sval);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
214
152
|
}
|
|
153
|
+
}
|
|
215
154
|
};
|
|
216
155
|
const queryO = (res, db, s, p, _, opts) => {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
(opts.partial ? addTriple(res, s, p, val) : collectFull(res, s, sval));
|
|
156
|
+
const sval = db[s];
|
|
157
|
+
const val = sval?.[p];
|
|
158
|
+
val != null && (opts.partial ? addTriple(res, s, p, val) : collectFull(res, s, sval));
|
|
221
159
|
};
|
|
222
160
|
const IMPLS = {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
else {
|
|
259
|
-
for (let $s in db) {
|
|
260
|
-
if (s($s)) {
|
|
261
|
-
const sval = db[$s];
|
|
262
|
-
for (let $p in sval) {
|
|
263
|
-
if (p($p)) {
|
|
264
|
-
collectFull(res, $s, sval);
|
|
265
|
-
break;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
},
|
|
272
|
-
fnl: queryFN,
|
|
273
|
-
fnf: queryFN,
|
|
274
|
-
fnn: (res, db, s) => {
|
|
275
|
-
for (let $s in db) {
|
|
276
|
-
if (s($s)) {
|
|
277
|
-
const sval = db[$s];
|
|
278
|
-
sval != null && collectFull(res, $s, sval);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
},
|
|
282
|
-
nll: queryNL,
|
|
283
|
-
nlf: queryNL,
|
|
284
|
-
nln: (res, db, _, p, __, opts) => {
|
|
285
|
-
if (opts.partial) {
|
|
286
|
-
for (let s in db) {
|
|
287
|
-
const val = db[s][p];
|
|
288
|
-
val != null && addTriple(res, s, p, val);
|
|
289
|
-
}
|
|
161
|
+
lll: queryLL,
|
|
162
|
+
llf: queryLL,
|
|
163
|
+
lln: queryO,
|
|
164
|
+
lfl: queryLF,
|
|
165
|
+
lff: queryLF,
|
|
166
|
+
lfn: (res, db, s, p, _, opts) => {
|
|
167
|
+
const sval = db[s];
|
|
168
|
+
sval != null && querySP(res, sval, s, p, null, opts);
|
|
169
|
+
},
|
|
170
|
+
lnl: queryLN,
|
|
171
|
+
lnf: queryLN,
|
|
172
|
+
lnn: (res, db, s) => {
|
|
173
|
+
const sval = db[s];
|
|
174
|
+
sval != null && collectFull(res, s, sval);
|
|
175
|
+
},
|
|
176
|
+
fll: queryFL,
|
|
177
|
+
flf: queryFL,
|
|
178
|
+
fln: (res, db, s, p, _, opts) => {
|
|
179
|
+
for (let $s in db) {
|
|
180
|
+
s($s) && queryO(res, db, $s, p, null, opts);
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
ffl: queryFF,
|
|
184
|
+
fff: queryFF,
|
|
185
|
+
ffn: (res, db, s, p, _, opts) => {
|
|
186
|
+
if (opts.partial) {
|
|
187
|
+
for (let $s in db) {
|
|
188
|
+
if (s($s)) {
|
|
189
|
+
const sval = db[$s];
|
|
190
|
+
for (let $p in sval) {
|
|
191
|
+
p($p) && addTriple(res, $s, $p, sval[$p]);
|
|
192
|
+
}
|
|
290
193
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
194
|
+
}
|
|
195
|
+
} else {
|
|
196
|
+
for (let $s in db) {
|
|
197
|
+
if (s($s)) {
|
|
198
|
+
const sval = db[$s];
|
|
199
|
+
for (let $p in sval) {
|
|
200
|
+
if (p($p)) {
|
|
201
|
+
collectFull(res, $s, sval);
|
|
202
|
+
break;
|
|
296
203
|
}
|
|
204
|
+
}
|
|
297
205
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
fnl: queryFN,
|
|
210
|
+
fnf: queryFN,
|
|
211
|
+
fnn: (res, db, s) => {
|
|
212
|
+
for (let $s in db) {
|
|
213
|
+
if (s($s)) {
|
|
214
|
+
const sval = db[$s];
|
|
215
|
+
sval != null && collectFull(res, $s, sval);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
nll: queryNL,
|
|
220
|
+
nlf: queryNL,
|
|
221
|
+
nln: (res, db, _, p, __, opts) => {
|
|
222
|
+
if (opts.partial) {
|
|
223
|
+
for (let s in db) {
|
|
224
|
+
const val = db[s][p];
|
|
225
|
+
val != null && addTriple(res, s, p, val);
|
|
226
|
+
}
|
|
227
|
+
} else {
|
|
228
|
+
for (let s in db) {
|
|
229
|
+
const sval = db[s];
|
|
230
|
+
const val = sval[p];
|
|
231
|
+
val != null && collectFull(res, s, sval);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
nfl: queryNF,
|
|
236
|
+
nff: queryNF,
|
|
237
|
+
nfn: (res, db, _, p, __, opts) => {
|
|
238
|
+
for (let s in db) {
|
|
239
|
+
querySP(res, db[s], s, p, null, opts);
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
nnl: queryNN,
|
|
243
|
+
nnf: queryNN,
|
|
244
|
+
nnn: (res, db) => Object.assign(res, db)
|
|
309
245
|
};
|
|
310
|
-
/**
|
|
311
|
-
* Query function implementation, dispatches to one of the 27 optimized
|
|
312
|
-
* functions based on given query pattern.
|
|
313
|
-
*
|
|
314
|
-
* @internal
|
|
315
|
-
*/
|
|
316
246
|
const impl = defmulti((_, __, s, p, o) => classify(s) + classify(p) + classify(o), {}, IMPLS);
|
|
317
247
|
const objQuery = (src, opts, args) => {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
248
|
+
const isIsec = opts.cwise && opts.intersect;
|
|
249
|
+
isIsec && (opts.cwise = false);
|
|
250
|
+
let [s, p, o, out] = args;
|
|
251
|
+
out = out || {};
|
|
252
|
+
impl(
|
|
253
|
+
out,
|
|
254
|
+
src,
|
|
255
|
+
coerceStr(s),
|
|
256
|
+
coerceStr(p),
|
|
257
|
+
coerce(o, isIsec),
|
|
258
|
+
opts
|
|
259
|
+
);
|
|
260
|
+
return out;
|
|
324
261
|
};
|
|
325
|
-
const arrayQuery = (src, opts, p, o,
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
}
|
|
262
|
+
const arrayQuery = (src, opts, p, o, collect2) => {
|
|
263
|
+
const isIsec = opts.cwise && opts.intersect;
|
|
264
|
+
isIsec && (opts.cwise = false);
|
|
265
|
+
const $p = coerceStr(p);
|
|
266
|
+
const $o = coerce(o, isIsec);
|
|
267
|
+
const impl2 = IMPLS["n" + classify($p) + classify($o)];
|
|
268
|
+
for (let i = 0, n = src.length; i < n; i++) {
|
|
269
|
+
const res = {};
|
|
270
|
+
impl2(res, { _: src[i] }, "_", $p, $o, opts);
|
|
271
|
+
res._ && collect2(res._, i);
|
|
272
|
+
}
|
|
337
273
|
};
|
|
338
274
|
const DEFAULT_OPTS = {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
275
|
+
partial: false,
|
|
276
|
+
cwise: true,
|
|
277
|
+
intersect: false,
|
|
278
|
+
equiv
|
|
343
279
|
};
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
*
|
|
354
|
-
* @param opts -
|
|
355
|
-
*/
|
|
356
|
-
export const defQuery = (opts) => {
|
|
357
|
-
const $opts = { ...DEFAULT_OPTS, ...opts };
|
|
358
|
-
return ((src, ...args) => {
|
|
359
|
-
if (isArray(src)) {
|
|
360
|
-
const out = args[2] || [];
|
|
361
|
-
arrayQuery(src, $opts, args[0], args[1], (x) => out.push(x));
|
|
362
|
-
return out;
|
|
363
|
-
}
|
|
364
|
-
else {
|
|
365
|
-
return objQuery(src, $opts, args);
|
|
366
|
-
}
|
|
367
|
-
});
|
|
368
|
-
};
|
|
369
|
-
/**
|
|
370
|
-
* Generic Higher-order function to return an actual query function based on
|
|
371
|
-
* given behavior options. Unlike {@link defQuery}, key query functions only
|
|
372
|
-
* return sets of keys (or indices) of matching objects.
|
|
373
|
-
*
|
|
374
|
-
* @remarks
|
|
375
|
-
* @see {@link KeyQueryOpts}
|
|
376
|
-
* @see {@link ObjKeyQueryFn}
|
|
377
|
-
* @see {@link ArrayKeyQueryFn}
|
|
378
|
-
*
|
|
379
|
-
* @param opts -
|
|
380
|
-
*/
|
|
381
|
-
export const defKeyQuery = (opts) => {
|
|
382
|
-
const $opts = { ...DEFAULT_OPTS, ...opts };
|
|
383
|
-
return ((src, ...args) => {
|
|
384
|
-
if (isArray(src)) {
|
|
385
|
-
const out = args[2] || new Set();
|
|
386
|
-
arrayQuery(src, $opts, args[0], args[1], (_, i) => out.add(i));
|
|
387
|
-
return out;
|
|
388
|
-
}
|
|
389
|
-
else {
|
|
390
|
-
const res = objQuery(src, $opts, args.slice(0, 3));
|
|
391
|
-
const out = args[3];
|
|
392
|
-
if (!out)
|
|
393
|
-
return new Set(Object.keys(res));
|
|
394
|
-
for (let k in res)
|
|
395
|
-
out.add(k);
|
|
396
|
-
return out;
|
|
397
|
-
}
|
|
398
|
-
});
|
|
399
|
-
};
|
|
400
|
-
/**
|
|
401
|
-
* Multi-term query function for collections (arrays) of {@link QueryObj}ects.
|
|
402
|
-
* Takes a number of {@link QueryTerm}s and matches each term in succession
|
|
403
|
-
* against the array of results of the previous terms (i.e. each sub-query is
|
|
404
|
-
* potentially further narrowing the result set). Returns final results,
|
|
405
|
-
* possibly post-processed, depending on given options.
|
|
406
|
-
*
|
|
407
|
-
* @remarks
|
|
408
|
-
* Each {@link QueryTerm} can provide its own options and post-processing
|
|
409
|
-
* function. Furthermore, global post-processing (e.g. limiting number of final
|
|
410
|
-
* results, sorting by key) can be configured via `opts`.
|
|
411
|
-
*
|
|
412
|
-
* @param db
|
|
413
|
-
* @param terms
|
|
414
|
-
* @param opts
|
|
415
|
-
*/
|
|
416
|
-
export const query = (db, terms, opts = {}) => {
|
|
417
|
-
for (let term of terms) {
|
|
418
|
-
if (!term)
|
|
419
|
-
continue;
|
|
420
|
-
db = (defQuery(term.opts)(db, term.q[0], term.q[1]));
|
|
421
|
-
term.post && (db = term.post(db));
|
|
422
|
-
if (!db.length)
|
|
423
|
-
return db;
|
|
280
|
+
const defQuery = (opts) => {
|
|
281
|
+
const $opts = { ...DEFAULT_OPTS, ...opts };
|
|
282
|
+
return (src, ...args) => {
|
|
283
|
+
if (isArray(src)) {
|
|
284
|
+
const out = args[2] || [];
|
|
285
|
+
arrayQuery(src, $opts, args[0], args[1], (x) => out.push(x));
|
|
286
|
+
return out;
|
|
287
|
+
} else {
|
|
288
|
+
return objQuery(src, $opts, args);
|
|
424
289
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
290
|
+
};
|
|
291
|
+
};
|
|
292
|
+
const defKeyQuery = (opts) => {
|
|
293
|
+
const $opts = { ...DEFAULT_OPTS, ...opts };
|
|
294
|
+
return (src, ...args) => {
|
|
295
|
+
if (isArray(src)) {
|
|
296
|
+
const out = args[2] || /* @__PURE__ */ new Set();
|
|
297
|
+
arrayQuery(src, $opts, args[0], args[1], (_, i) => out.add(i));
|
|
298
|
+
return out;
|
|
299
|
+
} else {
|
|
300
|
+
const res = objQuery(src, $opts, args.slice(0, 3));
|
|
301
|
+
const out = args[3];
|
|
302
|
+
if (!out)
|
|
303
|
+
return new Set(Object.keys(res));
|
|
304
|
+
for (let k in res)
|
|
305
|
+
out.add(k);
|
|
306
|
+
return out;
|
|
431
307
|
}
|
|
432
|
-
|
|
308
|
+
};
|
|
309
|
+
};
|
|
310
|
+
const query = (db, terms, opts = {}) => {
|
|
311
|
+
for (let term of terms) {
|
|
312
|
+
if (!term)
|
|
313
|
+
continue;
|
|
314
|
+
db = defQuery(term.opts)(
|
|
315
|
+
db,
|
|
316
|
+
term.q[0],
|
|
317
|
+
term.q[1]
|
|
318
|
+
);
|
|
319
|
+
term.post && (db = term.post(db));
|
|
320
|
+
if (!db.length)
|
|
321
|
+
return db;
|
|
322
|
+
}
|
|
323
|
+
const limit = opts.limit || 0;
|
|
324
|
+
if (limit > 0 && limit < db.length) {
|
|
325
|
+
db.length = limit;
|
|
326
|
+
}
|
|
327
|
+
if (opts.sort) {
|
|
328
|
+
db.sort(
|
|
329
|
+
compareByKey(
|
|
330
|
+
opts.sort,
|
|
331
|
+
opts.reverse ? reverse(compare) : compare
|
|
332
|
+
)
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
return db;
|
|
336
|
+
};
|
|
337
|
+
export {
|
|
338
|
+
defKeyQuery,
|
|
339
|
+
defQuery,
|
|
340
|
+
query
|
|
433
341
|
};
|