@thi.ng/rstream-query 2.1.86 → 2.1.88
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/api.js +0 -1
- package/convert.js +18 -64
- package/logger.js +6 -2
- package/package.json +17 -14
- package/pattern.js +30 -44
- package/qvar.js +35 -42
- package/store.js +331 -294
- package/xforms.js +66 -50
package/CHANGELOG.md
CHANGED
package/api.js
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/convert.js
CHANGED
|
@@ -5,70 +5,24 @@ import { mapcat } from "@thi.ng/transducers/mapcat";
|
|
|
5
5
|
import { pairs } from "@thi.ng/transducers/pairs";
|
|
6
6
|
let NEXT_ID = 0;
|
|
7
7
|
const mapBNode = (s, p, o) => {
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
const id = `__b${NEXT_ID++}__`;
|
|
9
|
+
return concat([[s, p, id]], asTriples(o, id));
|
|
10
10
|
};
|
|
11
11
|
const mapSubject = (subject) => ([p, o]) => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
if (isArray(o)) {
|
|
13
|
+
return mapcat(
|
|
14
|
+
(o2) => isPlainObject(o2) ? mapBNode(subject, p, o2) : [[subject, p, o2]],
|
|
15
|
+
o
|
|
16
|
+
);
|
|
17
|
+
} else if (isPlainObject(o)) {
|
|
18
|
+
return mapBNode(subject, p, o);
|
|
19
|
+
}
|
|
20
|
+
return [[subject, p, o]];
|
|
21
|
+
};
|
|
22
|
+
const asTriples = (obj, subject) => mapcat(
|
|
23
|
+
subject === void 0 ? ([s, v]) => mapcat(mapSubject(s), pairs(v)) : mapSubject(subject),
|
|
24
|
+
pairs(obj)
|
|
25
|
+
);
|
|
26
|
+
export {
|
|
27
|
+
asTriples
|
|
21
28
|
};
|
|
22
|
-
/**
|
|
23
|
-
* Converts given object into an iterable of triples, with the following
|
|
24
|
-
* conversion rules:
|
|
25
|
-
*
|
|
26
|
-
* - Toplevel object keys are used as subjects and MUST each have a
|
|
27
|
-
* plain object as value, where its keys are used as predicates and
|
|
28
|
-
* values as objects (in the SPO sense).
|
|
29
|
-
* - Plain objects in SPO object position are translated into unique IDs
|
|
30
|
-
* in order to allow the nested map to become a subject itself. In RDF
|
|
31
|
-
* terms, this is equivalent to BNodes.
|
|
32
|
-
* - Arrays in SPO object position cause multiple triples with same
|
|
33
|
-
* subject & predicate to be emitted. If any of the items in the array
|
|
34
|
-
* is a plain object, it will be treated as BNode and transformed as
|
|
35
|
-
* described in the previous rule
|
|
36
|
-
*
|
|
37
|
-
* @example
|
|
38
|
-
* ```ts
|
|
39
|
-
* src = {
|
|
40
|
-
* "@thi.ng/rstream-query": {
|
|
41
|
-
* type: "project",
|
|
42
|
-
* author: "toxi",
|
|
43
|
-
* tag: ["ES6", "TypeScript", "graph"]
|
|
44
|
-
* },
|
|
45
|
-
* toxi: {
|
|
46
|
-
* type: "person",
|
|
47
|
-
* hasAccount: [
|
|
48
|
-
* {type: "twitter", id: "toxi"},
|
|
49
|
-
* {type: "github", id: "postspectacular"}
|
|
50
|
-
* ]
|
|
51
|
-
* }
|
|
52
|
-
* };
|
|
53
|
-
*
|
|
54
|
-
* [...asTriples(src)]
|
|
55
|
-
* // [ [ '@thi.ng/rstream-query', 'type', 'project' ],
|
|
56
|
-
* // [ '@thi.ng/rstream-query', 'author', 'toxi' ],
|
|
57
|
-
* // [ '@thi.ng/rstream-query', 'tag', 'ES6' ],
|
|
58
|
-
* // [ '@thi.ng/rstream-query', 'tag', 'TypeScript' ],
|
|
59
|
-
* // [ '@thi.ng/rstream-query', 'tag', 'graph' ],
|
|
60
|
-
* // [ 'toxi', 'type', 'person' ],
|
|
61
|
-
* // [ 'toxi', 'hasAccount', '__b0__' ],
|
|
62
|
-
* // [ '__b0__', 'type', 'twitter' ],
|
|
63
|
-
* // [ '__b0__', 'id', 'toxi' ],
|
|
64
|
-
* // [ 'toxi', 'hasAccount', '__b1__' ],
|
|
65
|
-
* // [ '__b1__', 'type', 'github' ],
|
|
66
|
-
* // [ '__b1__', 'id', 'postspectacular' ] ]
|
|
67
|
-
* ```
|
|
68
|
-
*
|
|
69
|
-
* @param obj -
|
|
70
|
-
* @param subject - internal use only, do not specify!
|
|
71
|
-
*/
|
|
72
|
-
export const asTriples = (obj, subject) => mapcat(subject === undefined
|
|
73
|
-
? ([s, v]) => mapcat(mapSubject(s), pairs(v))
|
|
74
|
-
: mapSubject(subject), pairs(obj));
|
package/logger.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/rstream-query",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.88",
|
|
4
4
|
"description": "@thi.ng/rstream based triple store & reactive query engine",
|
|
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,19 +35,20 @@
|
|
|
33
35
|
"test": "bun test"
|
|
34
36
|
},
|
|
35
37
|
"dependencies": {
|
|
36
|
-
"@thi.ng/api": "^8.9.
|
|
37
|
-
"@thi.ng/associative": "^6.3.
|
|
38
|
-
"@thi.ng/checks": "^3.4.
|
|
39
|
-
"@thi.ng/equiv": "^2.1.
|
|
40
|
-
"@thi.ng/errors": "^2.4.
|
|
41
|
-
"@thi.ng/logger": "^2.0
|
|
42
|
-
"@thi.ng/math": "^5.7.
|
|
43
|
-
"@thi.ng/rstream": "^8.2.
|
|
44
|
-
"@thi.ng/rstream-dot": "^3.0.
|
|
45
|
-
"@thi.ng/transducers": "^8.8.
|
|
38
|
+
"@thi.ng/api": "^8.9.13",
|
|
39
|
+
"@thi.ng/associative": "^6.3.25",
|
|
40
|
+
"@thi.ng/checks": "^3.4.13",
|
|
41
|
+
"@thi.ng/equiv": "^2.1.38",
|
|
42
|
+
"@thi.ng/errors": "^2.4.7",
|
|
43
|
+
"@thi.ng/logger": "^2.1.0",
|
|
44
|
+
"@thi.ng/math": "^5.7.8",
|
|
45
|
+
"@thi.ng/rstream": "^8.2.15",
|
|
46
|
+
"@thi.ng/rstream-dot": "^3.0.38",
|
|
47
|
+
"@thi.ng/transducers": "^8.8.16"
|
|
46
48
|
},
|
|
47
49
|
"devDependencies": {
|
|
48
50
|
"@microsoft/api-extractor": "^7.38.3",
|
|
51
|
+
"esbuild": "^0.19.8",
|
|
49
52
|
"rimraf": "^5.0.5",
|
|
50
53
|
"tools": "^0.0.1",
|
|
51
54
|
"typedoc": "^0.25.4",
|
|
@@ -70,7 +73,7 @@
|
|
|
70
73
|
"access": "public"
|
|
71
74
|
},
|
|
72
75
|
"engines": {
|
|
73
|
-
"node": ">=
|
|
76
|
+
"node": ">=18"
|
|
74
77
|
},
|
|
75
78
|
"files": [
|
|
76
79
|
"./*.js",
|
|
@@ -106,5 +109,5 @@
|
|
|
106
109
|
"parent": "@thi.ng/rstream",
|
|
107
110
|
"year": 2018
|
|
108
111
|
},
|
|
109
|
-
"gitHead": "
|
|
112
|
+
"gitHead": "25a42a81fac8603a1e440a7aa8bc343276211ff4\n"
|
|
110
113
|
}
|
package/pattern.js
CHANGED
|
@@ -1,49 +1,35 @@
|
|
|
1
1
|
import { repeatedly } from "@thi.ng/transducers/repeatedly";
|
|
2
2
|
import { autoQVar, isQVar, qvarName } from "./qvar.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
const patternVarCount = (p) => {
|
|
4
|
+
let n = 0;
|
|
5
|
+
if (isQVar(p[0]))
|
|
6
|
+
n++;
|
|
7
|
+
if (isQVar(p[1]))
|
|
8
|
+
n++;
|
|
9
|
+
if (isQVar(p[2]))
|
|
10
|
+
n++;
|
|
11
|
+
return n;
|
|
12
12
|
};
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
const patternVars = ([s, p, o]) => {
|
|
14
|
+
const vars = [];
|
|
15
|
+
isQVar(s) && vars.push(qvarName(s));
|
|
16
|
+
isQVar(p) && vars.push(qvarName(p));
|
|
17
|
+
isQVar(o) && vars.push(qvarName(o));
|
|
18
|
+
return vars;
|
|
19
19
|
};
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
* ```
|
|
36
|
-
*
|
|
37
|
-
* @param pattern -
|
|
38
|
-
* @param maxLen -
|
|
39
|
-
*/
|
|
40
|
-
export const resolvePathPattern = ([s, p, o], maxLen = p.length) => {
|
|
41
|
-
const res = [];
|
|
42
|
-
const avars = [...repeatedly(autoQVar, maxLen - 1)];
|
|
43
|
-
for (let i = 0; i < maxLen; i++) {
|
|
44
|
-
res.push([s, p[i % p.length], (s = avars[i])]);
|
|
45
|
-
}
|
|
46
|
-
res[res.length - 1][2] = o;
|
|
47
|
-
return [res, avars];
|
|
20
|
+
const resolvePathPattern = ([s, p, o], maxLen = p.length) => {
|
|
21
|
+
const res = [];
|
|
22
|
+
const avars = [...repeatedly(autoQVar, maxLen - 1)];
|
|
23
|
+
for (let i = 0; i < maxLen; i++) {
|
|
24
|
+
res.push([s, p[i % p.length], s = avars[i]]);
|
|
25
|
+
}
|
|
26
|
+
res[res.length - 1][2] = o;
|
|
27
|
+
return [res, avars];
|
|
28
|
+
};
|
|
29
|
+
const sortPatterns = (patterns) => patterns.sort((a, b) => patternVarCount(a) - patternVarCount(b));
|
|
30
|
+
export {
|
|
31
|
+
patternVarCount,
|
|
32
|
+
patternVars,
|
|
33
|
+
resolvePathPattern,
|
|
34
|
+
sortPatterns
|
|
48
35
|
};
|
|
49
|
-
export const sortPatterns = (patterns) => patterns.sort((a, b) => patternVarCount(a) - patternVarCount(b));
|
package/qvar.js
CHANGED
|
@@ -1,46 +1,39 @@
|
|
|
1
1
|
import { isString } from "@thi.ng/checks/is-string";
|
|
2
2
|
const AUTO_QVAR_PREFIX = "?__q";
|
|
3
3
|
let AUTO_QVAR_ID = 0;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
case 5:
|
|
40
|
-
return (f) => ({ [ss]: f[0], [oo]: f[2] });
|
|
41
|
-
case 6:
|
|
42
|
-
return (f) => ({ [ss]: f[0], [pp]: f[1] });
|
|
43
|
-
case 7:
|
|
44
|
-
return (f) => ({ [ss]: f[0], [pp]: f[1], [oo]: f[2] });
|
|
45
|
-
}
|
|
4
|
+
const isQVar = (x) => isString(x) && x.charAt(0) === "?";
|
|
5
|
+
const isAutoQVar = (x) => isString(x) && x.indexOf(AUTO_QVAR_PREFIX) == 0;
|
|
6
|
+
const autoQVar = () => AUTO_QVAR_PREFIX + (AUTO_QVAR_ID++).toString(36);
|
|
7
|
+
const qvarName = (x) => x.substring(1);
|
|
8
|
+
const qvarResolver = (vs, vp, vo, s, p, o) => {
|
|
9
|
+
const type = vs << 2 | vp << 1 | vo;
|
|
10
|
+
let ss = vs && qvarName(s);
|
|
11
|
+
let pp = vp && qvarName(p);
|
|
12
|
+
let oo = vo && qvarName(o);
|
|
13
|
+
switch (type) {
|
|
14
|
+
case 0:
|
|
15
|
+
default:
|
|
16
|
+
return;
|
|
17
|
+
case 1:
|
|
18
|
+
return (f) => ({ [oo]: f[2] });
|
|
19
|
+
case 2:
|
|
20
|
+
return (f) => ({ [pp]: f[1] });
|
|
21
|
+
case 3:
|
|
22
|
+
return (f) => ({ [pp]: f[1], [oo]: f[2] });
|
|
23
|
+
case 4:
|
|
24
|
+
return (f) => ({ [ss]: f[0] });
|
|
25
|
+
case 5:
|
|
26
|
+
return (f) => ({ [ss]: f[0], [oo]: f[2] });
|
|
27
|
+
case 6:
|
|
28
|
+
return (f) => ({ [ss]: f[0], [pp]: f[1] });
|
|
29
|
+
case 7:
|
|
30
|
+
return (f) => ({ [ss]: f[0], [pp]: f[1], [oo]: f[2] });
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
export {
|
|
34
|
+
autoQVar,
|
|
35
|
+
isAutoQVar,
|
|
36
|
+
isQVar,
|
|
37
|
+
qvarName,
|
|
38
|
+
qvarResolver
|
|
46
39
|
};
|
package/store.js
CHANGED
|
@@ -16,317 +16,354 @@ import { mapIndexed } from "@thi.ng/transducers/map-indexed";
|
|
|
16
16
|
import { transduce } from "@thi.ng/transducers/transduce";
|
|
17
17
|
import { patternVars, resolvePathPattern } from "./pattern.js";
|
|
18
18
|
import { isQVar, qvarResolver } from "./qvar.js";
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
has(t) {
|
|
64
|
-
return this.get(t) !== undefined;
|
|
65
|
-
}
|
|
66
|
-
get(t, notFound) {
|
|
67
|
-
const id = this.findTriple(this.indexS.get(t[0]), this.indexP.get(t[1]), this.indexO.get(t[2]), t);
|
|
68
|
-
return id !== -1 ? this.triples[id] : notFound;
|
|
69
|
-
}
|
|
70
|
-
add(t) {
|
|
71
|
-
let s = this.indexS.get(t[0]);
|
|
72
|
-
let p = this.indexP.get(t[1]);
|
|
73
|
-
let o = this.indexO.get(t[2]);
|
|
74
|
-
if (this.findTriple(s, p, o, t) !== -1)
|
|
75
|
-
return false;
|
|
76
|
-
const id = this.nextID();
|
|
77
|
-
const is = s || new Set();
|
|
78
|
-
const ip = p || new Set();
|
|
79
|
-
const io = o || new Set();
|
|
80
|
-
this.triples[id] = t;
|
|
81
|
-
is.add(id);
|
|
82
|
-
ip.add(id);
|
|
83
|
-
io.add(id);
|
|
84
|
-
this.allIDs.add(id);
|
|
85
|
-
!s && this.indexS.set(t[0], is);
|
|
86
|
-
!p && this.indexP.set(t[1], ip);
|
|
87
|
-
!o && this.indexO.set(t[2], io);
|
|
88
|
-
this.broadcastTriple(is, ip, io, t);
|
|
89
|
-
return true;
|
|
19
|
+
import {
|
|
20
|
+
bindVars,
|
|
21
|
+
filterSolutions,
|
|
22
|
+
indexSel,
|
|
23
|
+
intersect2,
|
|
24
|
+
intersect3,
|
|
25
|
+
joinSolutions,
|
|
26
|
+
limitSolutions,
|
|
27
|
+
resultTriples
|
|
28
|
+
} from "./xforms.js";
|
|
29
|
+
class TripleStore {
|
|
30
|
+
NEXT_ID;
|
|
31
|
+
freeIDs;
|
|
32
|
+
triples;
|
|
33
|
+
indexS;
|
|
34
|
+
indexP;
|
|
35
|
+
indexO;
|
|
36
|
+
indexSelections;
|
|
37
|
+
queries;
|
|
38
|
+
allIDs;
|
|
39
|
+
streamAll;
|
|
40
|
+
streamS;
|
|
41
|
+
streamP;
|
|
42
|
+
streamO;
|
|
43
|
+
constructor(triples) {
|
|
44
|
+
this.triples = [];
|
|
45
|
+
this.freeIDs = [];
|
|
46
|
+
this.queries = /* @__PURE__ */ new Map();
|
|
47
|
+
this.indexS = /* @__PURE__ */ new Map();
|
|
48
|
+
this.indexP = /* @__PURE__ */ new Map();
|
|
49
|
+
this.indexO = /* @__PURE__ */ new Map();
|
|
50
|
+
this.indexSelections = {
|
|
51
|
+
s: /* @__PURE__ */ new Map(),
|
|
52
|
+
p: /* @__PURE__ */ new Map(),
|
|
53
|
+
o: /* @__PURE__ */ new Map()
|
|
54
|
+
};
|
|
55
|
+
this.streamS = new Stream({ id: "S", closeOut: CloseMode.NEVER });
|
|
56
|
+
this.streamP = new Stream({ id: "P", closeOut: CloseMode.NEVER });
|
|
57
|
+
this.streamO = new Stream({ id: "O", closeOut: CloseMode.NEVER });
|
|
58
|
+
this.streamAll = new Stream({ id: "ALL", closeOut: CloseMode.NEVER });
|
|
59
|
+
this.allIDs = /* @__PURE__ */ new Set();
|
|
60
|
+
this.NEXT_ID = 0;
|
|
61
|
+
if (triples) {
|
|
62
|
+
this.into(triples);
|
|
90
63
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
delete(t) {
|
|
99
|
-
let s = this.indexS.get(t[0]);
|
|
100
|
-
let p = this.indexP.get(t[1]);
|
|
101
|
-
let o = this.indexO.get(t[2]);
|
|
102
|
-
const id = this.findTriple(s, p, o, t);
|
|
103
|
-
if (id === -1)
|
|
104
|
-
return false;
|
|
105
|
-
s.delete(id);
|
|
106
|
-
!s.size && this.indexS.delete(t[0]);
|
|
107
|
-
p.delete(id);
|
|
108
|
-
!p.size && this.indexP.delete(t[1]);
|
|
109
|
-
o.delete(id);
|
|
110
|
-
!o.size && this.indexO.delete(t[2]);
|
|
111
|
-
this.allIDs.delete(id);
|
|
112
|
-
delete this.triples[id];
|
|
113
|
-
this.freeIDs.push(id);
|
|
114
|
-
this.broadcastTriple(s, p, o, t);
|
|
115
|
-
return true;
|
|
64
|
+
}
|
|
65
|
+
*[Symbol.iterator]() {
|
|
66
|
+
for (let t of this.triples) {
|
|
67
|
+
if (t) {
|
|
68
|
+
yield t;
|
|
69
|
+
}
|
|
116
70
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
71
|
+
}
|
|
72
|
+
has(t) {
|
|
73
|
+
return this.get(t) !== void 0;
|
|
74
|
+
}
|
|
75
|
+
get(t, notFound) {
|
|
76
|
+
const id = this.findTriple(
|
|
77
|
+
this.indexS.get(t[0]),
|
|
78
|
+
this.indexP.get(t[1]),
|
|
79
|
+
this.indexO.get(t[2]),
|
|
80
|
+
t
|
|
81
|
+
);
|
|
82
|
+
return id !== -1 ? this.triples[id] : notFound;
|
|
83
|
+
}
|
|
84
|
+
add(t) {
|
|
85
|
+
let s = this.indexS.get(t[0]);
|
|
86
|
+
let p = this.indexP.get(t[1]);
|
|
87
|
+
let o = this.indexO.get(t[2]);
|
|
88
|
+
if (this.findTriple(s, p, o, t) !== -1)
|
|
89
|
+
return false;
|
|
90
|
+
const id = this.nextID();
|
|
91
|
+
const is = s || /* @__PURE__ */ new Set();
|
|
92
|
+
const ip = p || /* @__PURE__ */ new Set();
|
|
93
|
+
const io = o || /* @__PURE__ */ new Set();
|
|
94
|
+
this.triples[id] = t;
|
|
95
|
+
is.add(id);
|
|
96
|
+
ip.add(id);
|
|
97
|
+
io.add(id);
|
|
98
|
+
this.allIDs.add(id);
|
|
99
|
+
!s && this.indexS.set(t[0], is);
|
|
100
|
+
!p && this.indexP.set(t[1], ip);
|
|
101
|
+
!o && this.indexO.set(t[2], io);
|
|
102
|
+
this.broadcastTriple(is, ip, io, t);
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
into(triples) {
|
|
106
|
+
let ok = true;
|
|
107
|
+
for (let f of triples) {
|
|
108
|
+
ok = this.add(f) && ok;
|
|
129
109
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
xform,
|
|
162
|
-
reset: true,
|
|
163
|
-
});
|
|
164
|
-
this.queries.set(key, results);
|
|
165
|
-
submit(this.indexS, qs, s);
|
|
166
|
-
submit(this.indexP, qp, p);
|
|
167
|
-
submit(this.indexO, qo, o);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
return emitTriples ? results.transform(resultTriples(this)) : results;
|
|
110
|
+
return ok;
|
|
111
|
+
}
|
|
112
|
+
delete(t) {
|
|
113
|
+
let s = this.indexS.get(t[0]);
|
|
114
|
+
let p = this.indexP.get(t[1]);
|
|
115
|
+
let o = this.indexO.get(t[2]);
|
|
116
|
+
const id = this.findTriple(s, p, o, t);
|
|
117
|
+
if (id === -1)
|
|
118
|
+
return false;
|
|
119
|
+
s.delete(id);
|
|
120
|
+
!s.size && this.indexS.delete(t[0]);
|
|
121
|
+
p.delete(id);
|
|
122
|
+
!p.size && this.indexP.delete(t[1]);
|
|
123
|
+
o.delete(id);
|
|
124
|
+
!o.size && this.indexO.delete(t[2]);
|
|
125
|
+
this.allIDs.delete(id);
|
|
126
|
+
delete this.triples[id];
|
|
127
|
+
this.freeIDs.push(id);
|
|
128
|
+
this.broadcastTriple(s, p, o, t);
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Replaces triple `a` with `b`, *iff* `a` is actually in the store.
|
|
133
|
+
* Else does nothing.
|
|
134
|
+
*
|
|
135
|
+
* @param a -
|
|
136
|
+
* @param b -
|
|
137
|
+
*/
|
|
138
|
+
replace(a, b) {
|
|
139
|
+
if (this.delete(a)) {
|
|
140
|
+
return this.add(b);
|
|
171
141
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const vo = isQVar(o);
|
|
197
|
-
const resolve = qvarResolver(vs, vp, vo, s, p, o);
|
|
198
|
-
if (!resolve) {
|
|
199
|
-
illegalArgs("at least 1 query variable is required in pattern");
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
addPatternQuery(pattern, id, emitTriples = true) {
|
|
145
|
+
let results;
|
|
146
|
+
const [s, p, o] = pattern;
|
|
147
|
+
if (s == null && p == null && o == null) {
|
|
148
|
+
results = this.streamAll;
|
|
149
|
+
} else {
|
|
150
|
+
const key = JSON.stringify(pattern);
|
|
151
|
+
if (!(results = this.queries.get(key))) {
|
|
152
|
+
const qs = this.getIndexSelection(this.streamS, s, "s");
|
|
153
|
+
const qp = this.getIndexSelection(this.streamP, p, "p");
|
|
154
|
+
const qo = this.getIndexSelection(this.streamO, o, "o");
|
|
155
|
+
let src;
|
|
156
|
+
let xform = intersect2;
|
|
157
|
+
if (s == null && p == null) {
|
|
158
|
+
src = { a: qo, b: qs };
|
|
159
|
+
} else if (s == null && o == null) {
|
|
160
|
+
src = { a: qp, b: qs };
|
|
161
|
+
} else if (p == null && o == null) {
|
|
162
|
+
src = { a: qs, b: qp };
|
|
163
|
+
} else {
|
|
164
|
+
src = { s: qs, p: qp, o: qo };
|
|
165
|
+
xform = intersect3;
|
|
200
166
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
res.add(resolve(f));
|
|
207
|
-
}
|
|
208
|
-
return res;
|
|
209
|
-
}), dedupe(equiv), { id });
|
|
210
|
-
}
|
|
211
|
-
/**
|
|
212
|
-
* Converts the given path pattern into a number of sub-queries and
|
|
213
|
-
* return a rstream subscription of re-joined result solutions. If
|
|
214
|
-
* `maxLen` is given and greater than the number of actual path
|
|
215
|
-
* predicates, the predicates are repeated.
|
|
216
|
-
*
|
|
217
|
-
* @param path -
|
|
218
|
-
* @param maxDepth -
|
|
219
|
-
* @param id -
|
|
220
|
-
*/
|
|
221
|
-
addPathQuery(path, len = path[1].length, id) {
|
|
222
|
-
return this.addMultiJoin(this.addParamQueries(resolvePathPattern(path, len)[0]), patternVars(path), id);
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* Like {@link TripleStore.addMultiJoin}, but optimized for only two
|
|
226
|
-
* input queries. Returns a rstream subscription computing the
|
|
227
|
-
* natural join of the given input query results.
|
|
228
|
-
*
|
|
229
|
-
* @param id -
|
|
230
|
-
* @param a -
|
|
231
|
-
* @param b -
|
|
232
|
-
*/
|
|
233
|
-
addJoin(a, b, id) {
|
|
234
|
-
return sync({
|
|
235
|
-
id,
|
|
236
|
-
src: { a, b },
|
|
237
|
-
xform: comp(map(({ a, b }) => join(a, b)), dedupe(equiv)),
|
|
167
|
+
results = sync({
|
|
168
|
+
id,
|
|
169
|
+
src,
|
|
170
|
+
xform,
|
|
171
|
+
reset: true
|
|
238
172
|
});
|
|
173
|
+
this.queries.set(key, results);
|
|
174
|
+
submit(this.indexS, qs, s);
|
|
175
|
+
submit(this.indexP, qp, p);
|
|
176
|
+
submit(this.indexO, qo, o);
|
|
177
|
+
}
|
|
239
178
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
179
|
+
return emitTriples ? results.transform(resultTriples(this)) : results;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Creates a new parametric query using given pattern with at least
|
|
183
|
+
* 1 query variable. Query vars are strings with `?` prefix. The
|
|
184
|
+
* rest of the string is considered the variable name.
|
|
185
|
+
*
|
|
186
|
+
* ```
|
|
187
|
+
* g.addParamQuery(["?a", "friend", "?b"]);
|
|
188
|
+
* ```
|
|
189
|
+
*
|
|
190
|
+
* Internally, the query pattern is translated into a basic param
|
|
191
|
+
* query with an additional result transformation to resolve the
|
|
192
|
+
* stated query variable solutions. Returns a rstream subscription
|
|
193
|
+
* emitting arrays of solution objects like:
|
|
194
|
+
*
|
|
195
|
+
* ```
|
|
196
|
+
* [{a: "asterix", b: "obelix"}, {a: "romeo", b: "julia"}]
|
|
197
|
+
* ```
|
|
198
|
+
*
|
|
199
|
+
* @param id -
|
|
200
|
+
* @param param1 -
|
|
201
|
+
*/
|
|
202
|
+
addParamQuery([s, p, o], id) {
|
|
203
|
+
const vs = isQVar(s);
|
|
204
|
+
const vp = isQVar(p);
|
|
205
|
+
const vo = isQVar(o);
|
|
206
|
+
const resolve = qvarResolver(vs, vp, vo, s, p, o);
|
|
207
|
+
if (!resolve) {
|
|
208
|
+
illegalArgs("at least 1 query variable is required in pattern");
|
|
255
209
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
let
|
|
265
|
-
|
|
266
|
-
for (let q of spec.q) {
|
|
267
|
-
if (isWhereQuery(q)) {
|
|
268
|
-
curr = this.addMultiJoin(this.addParamQueries(q.where));
|
|
269
|
-
}
|
|
270
|
-
else if (isPathQuery(q)) {
|
|
271
|
-
curr = this.addPathQuery(q.path);
|
|
272
|
-
}
|
|
273
|
-
query && curr && (curr = this.addJoin(query, curr));
|
|
274
|
-
query = curr;
|
|
275
|
-
}
|
|
276
|
-
assert(!!query, "illegal query spec");
|
|
277
|
-
let xforms = [];
|
|
278
|
-
spec.limit && xforms.push(limitSolutions(spec.limit));
|
|
279
|
-
spec.bind && xforms.push(bindVars(spec.bind));
|
|
280
|
-
spec.select && xforms.push(filterSolutions(spec.select));
|
|
281
|
-
if (xforms.length) {
|
|
282
|
-
// @ts-ignore
|
|
283
|
-
query = query.transform(...xforms);
|
|
210
|
+
id || (id = `query-${__nextID()}`);
|
|
211
|
+
const query = this.addPatternQuery(
|
|
212
|
+
[vs ? null : s, vp ? null : p, vo ? null : o],
|
|
213
|
+
id + "-raw"
|
|
214
|
+
);
|
|
215
|
+
return query.transform(
|
|
216
|
+
map((triples) => {
|
|
217
|
+
const res = /* @__PURE__ */ new Set();
|
|
218
|
+
for (let f of triples) {
|
|
219
|
+
res.add(resolve(f));
|
|
284
220
|
}
|
|
285
|
-
return
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
221
|
+
return res;
|
|
222
|
+
}),
|
|
223
|
+
dedupe(equiv),
|
|
224
|
+
{ id }
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Converts the given path pattern into a number of sub-queries and
|
|
229
|
+
* return a rstream subscription of re-joined result solutions. If
|
|
230
|
+
* `maxLen` is given and greater than the number of actual path
|
|
231
|
+
* predicates, the predicates are repeated.
|
|
232
|
+
*
|
|
233
|
+
* @param path -
|
|
234
|
+
* @param maxDepth -
|
|
235
|
+
* @param id -
|
|
236
|
+
*/
|
|
237
|
+
addPathQuery(path, len = path[1].length, id) {
|
|
238
|
+
return this.addMultiJoin(
|
|
239
|
+
this.addParamQueries(resolvePathPattern(path, len)[0]),
|
|
240
|
+
patternVars(path),
|
|
241
|
+
id
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Like {@link TripleStore.addMultiJoin}, but optimized for only two
|
|
246
|
+
* input queries. Returns a rstream subscription computing the
|
|
247
|
+
* natural join of the given input query results.
|
|
248
|
+
*
|
|
249
|
+
* @param id -
|
|
250
|
+
* @param a -
|
|
251
|
+
* @param b -
|
|
252
|
+
*/
|
|
253
|
+
addJoin(a, b, id) {
|
|
254
|
+
return sync({
|
|
255
|
+
id,
|
|
256
|
+
src: { a, b },
|
|
257
|
+
xform: comp(
|
|
258
|
+
map(({ a: a2, b: b2 }) => join(a2, b2)),
|
|
259
|
+
dedupe(equiv)
|
|
260
|
+
)
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
addMultiJoin(queries, keepVars, id) {
|
|
264
|
+
const src = transduce(
|
|
265
|
+
mapIndexed((i, q) => [
|
|
266
|
+
String(i),
|
|
267
|
+
q
|
|
268
|
+
]),
|
|
269
|
+
assocObj(),
|
|
270
|
+
queries
|
|
271
|
+
);
|
|
272
|
+
let xforms = [
|
|
273
|
+
joinSolutions(Object.keys(src).length),
|
|
274
|
+
dedupe(equiv)
|
|
275
|
+
];
|
|
276
|
+
keepVars && xforms.push(filterSolutions(keepVars));
|
|
277
|
+
return sync({
|
|
278
|
+
id,
|
|
279
|
+
src,
|
|
280
|
+
xform: comp.apply(null, xforms)
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Compiles given query spec into a number of sub-queries and result
|
|
285
|
+
* transformations. Returns rstream subscription of final result
|
|
286
|
+
* sets. See {@link QuerySpec} docs for further details.
|
|
287
|
+
*
|
|
288
|
+
* @param spec -
|
|
289
|
+
*/
|
|
290
|
+
addQueryFromSpec(spec) {
|
|
291
|
+
let query;
|
|
292
|
+
let curr;
|
|
293
|
+
for (let q of spec.q) {
|
|
294
|
+
if (isWhereQuery(q)) {
|
|
295
|
+
curr = this.addMultiJoin(this.addParamQueries(q.where));
|
|
296
|
+
} else if (isPathQuery(q)) {
|
|
297
|
+
curr = this.addPathQuery(q.path);
|
|
298
|
+
}
|
|
299
|
+
query && curr && (curr = this.addJoin(query, curr));
|
|
300
|
+
query = curr;
|
|
289
301
|
}
|
|
290
|
-
|
|
291
|
-
|
|
302
|
+
assert(!!query, "illegal query spec");
|
|
303
|
+
let xforms = [];
|
|
304
|
+
spec.limit && xforms.push(limitSolutions(spec.limit));
|
|
305
|
+
spec.bind && xforms.push(bindVars(spec.bind));
|
|
306
|
+
spec.select && xforms.push(filterSolutions(spec.select));
|
|
307
|
+
if (xforms.length) {
|
|
308
|
+
query = query.transform(...xforms);
|
|
292
309
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
310
|
+
return query;
|
|
311
|
+
}
|
|
312
|
+
toDot(opts) {
|
|
313
|
+
return serialize(
|
|
314
|
+
[this.streamS, this.streamP, this.streamO, this.streamAll],
|
|
315
|
+
opts
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
nextID() {
|
|
319
|
+
return this.freeIDs.length ? this.freeIDs.pop() : this.NEXT_ID++;
|
|
320
|
+
}
|
|
321
|
+
broadcastTriple(s, p, o, t) {
|
|
322
|
+
this.streamAll.next(this.allIDs);
|
|
323
|
+
this.streamS.next({ index: s, key: t[0] });
|
|
324
|
+
this.streamP.next({ index: p, key: t[1] });
|
|
325
|
+
this.streamO.next({ index: o, key: t[2] });
|
|
326
|
+
}
|
|
327
|
+
findTriple(s, p, o, f) {
|
|
328
|
+
if (s && p && o) {
|
|
329
|
+
const triples = this.triples;
|
|
330
|
+
const index = [s, p, o][min3id(s.size, p.size, o.size)];
|
|
331
|
+
for (let id of index) {
|
|
332
|
+
if (equiv(triples[id], f)) {
|
|
333
|
+
return id;
|
|
308
334
|
}
|
|
309
|
-
|
|
335
|
+
}
|
|
310
336
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
if (!sel) {
|
|
317
|
-
this.indexSelections[id].set(key, (sel = stream.transform(indexSel(key), { id })));
|
|
318
|
-
}
|
|
319
|
-
return sel;
|
|
337
|
+
return -1;
|
|
338
|
+
}
|
|
339
|
+
getIndexSelection(stream, key, id) {
|
|
340
|
+
if (key == null) {
|
|
341
|
+
return this.streamAll;
|
|
320
342
|
}
|
|
321
|
-
|
|
322
|
-
|
|
343
|
+
let sel = this.indexSelections[id].get(key);
|
|
344
|
+
if (!sel) {
|
|
345
|
+
this.indexSelections[id].set(
|
|
346
|
+
key,
|
|
347
|
+
sel = stream.transform(indexSel(key), { id })
|
|
348
|
+
);
|
|
323
349
|
}
|
|
350
|
+
return sel;
|
|
351
|
+
}
|
|
352
|
+
addParamQueries(patterns) {
|
|
353
|
+
return map(
|
|
354
|
+
(q) => this.addParamQuery(q),
|
|
355
|
+
patterns
|
|
356
|
+
);
|
|
357
|
+
}
|
|
324
358
|
}
|
|
325
359
|
const submit = (index, stream, key) => {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
360
|
+
if (key != null) {
|
|
361
|
+
const ids = index.get(key);
|
|
362
|
+
ids && stream.next({ index: ids, key });
|
|
363
|
+
}
|
|
330
364
|
};
|
|
331
365
|
const isWhereQuery = (q) => !!q.where;
|
|
332
366
|
const isPathQuery = (q) => !!q.path;
|
|
367
|
+
export {
|
|
368
|
+
TripleStore
|
|
369
|
+
};
|
package/xforms.js
CHANGED
|
@@ -7,62 +7,78 @@ import { compR } from "@thi.ng/transducers/compr";
|
|
|
7
7
|
import { dedupe } from "@thi.ng/transducers/dedupe";
|
|
8
8
|
import { keySelector } from "@thi.ng/transducers/key-selector";
|
|
9
9
|
import { map } from "@thi.ng/transducers/map";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
10
|
+
const intersect2 = comp(
|
|
11
|
+
map(({ a, b }) => intersection(a, b)),
|
|
12
|
+
dedupe(equiv)
|
|
13
|
+
);
|
|
14
|
+
const intersect3 = comp(
|
|
15
|
+
map(({ s, p, o }) => intersection(intersection(s, p), o)),
|
|
16
|
+
dedupe(equiv)
|
|
17
|
+
);
|
|
18
|
+
const indexSel = (key) => (rfn) => {
|
|
19
|
+
const r = rfn[2];
|
|
20
|
+
return compR(rfn, (acc, e) => {
|
|
21
|
+
LOGGER.fine("index sel", e.key, key);
|
|
22
|
+
if (equiv(e.key, key)) {
|
|
23
|
+
return r(acc, e.index);
|
|
24
|
+
}
|
|
25
|
+
return acc;
|
|
26
|
+
});
|
|
21
27
|
};
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
const resultTriples = (graph) => map((ids) => {
|
|
29
|
+
const res = /* @__PURE__ */ new Set();
|
|
30
|
+
for (let id of ids)
|
|
31
|
+
res.add(graph.triples[id]);
|
|
32
|
+
return res;
|
|
27
33
|
});
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
const joinSolutions = (n) => map((src) => {
|
|
35
|
+
let res = src[0];
|
|
36
|
+
for (let i = 1; i < n && res.size; i++) {
|
|
37
|
+
res = join(res, src[i]);
|
|
38
|
+
}
|
|
39
|
+
return res;
|
|
34
40
|
});
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
for (let s of sol) {
|
|
40
|
-
res.add(filterVars(s));
|
|
41
|
-
}
|
|
42
|
-
return res;
|
|
43
|
-
});
|
|
44
|
-
};
|
|
45
|
-
export const limitSolutions = (n) => map((sol) => {
|
|
46
|
-
if (sol.size <= n) {
|
|
47
|
-
return sol;
|
|
48
|
-
}
|
|
49
|
-
const res = new Set();
|
|
50
|
-
let m = n;
|
|
41
|
+
const filterSolutions = (qvars) => {
|
|
42
|
+
const filterVars = keySelector([...qvars]);
|
|
43
|
+
return map((sol) => {
|
|
44
|
+
const res = /* @__PURE__ */ new Set();
|
|
51
45
|
for (let s of sol) {
|
|
52
|
-
|
|
53
|
-
if (--m <= 0)
|
|
54
|
-
break;
|
|
46
|
+
res.add(filterVars(s));
|
|
55
47
|
}
|
|
56
48
|
return res;
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
const limitSolutions = (n) => map((sol) => {
|
|
52
|
+
if (sol.size <= n) {
|
|
53
|
+
return sol;
|
|
54
|
+
}
|
|
55
|
+
const res = /* @__PURE__ */ new Set();
|
|
56
|
+
let m = n;
|
|
57
|
+
for (let s of sol) {
|
|
58
|
+
res.add(s);
|
|
59
|
+
if (--m <= 0)
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
return res;
|
|
57
63
|
});
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
64
|
+
const bindVars = (bindings) => map((sol) => {
|
|
65
|
+
const res = /* @__PURE__ */ new Set();
|
|
66
|
+
for (let s of sol) {
|
|
67
|
+
s = { ...s };
|
|
68
|
+
res.add(s);
|
|
69
|
+
for (let b in bindings) {
|
|
70
|
+
s[b] = bindings[b](s);
|
|
66
71
|
}
|
|
67
|
-
|
|
72
|
+
}
|
|
73
|
+
return res;
|
|
68
74
|
});
|
|
75
|
+
export {
|
|
76
|
+
bindVars,
|
|
77
|
+
filterSolutions,
|
|
78
|
+
indexSel,
|
|
79
|
+
intersect2,
|
|
80
|
+
intersect3,
|
|
81
|
+
joinSolutions,
|
|
82
|
+
limitSolutions,
|
|
83
|
+
resultTriples
|
|
84
|
+
};
|