n3 2.2.8 → 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/browser/n3.esm.min.js +11 -11
- package/browser/n3.min.js +1 -1
- package/lib/N3Reasoner.js +57 -41
- package/lib/N3Store.js +64 -19
- package/package.json +1 -1
- package/src/N3Reasoner.js +62 -33
- package/src/N3Store.js +61 -20
package/lib/N3Reasoner.js
CHANGED
|
@@ -33,7 +33,7 @@ class N3Reasoner {
|
|
|
33
33
|
// Caps a rule's premise count, as `_evaluatePremise` recurses once per premise
|
|
34
34
|
this._maxPremiseDepth = options.maxPremiseDepth === undefined ? Infinity : options.maxPremiseDepth;
|
|
35
35
|
}
|
|
36
|
-
_add(subject, predicate, object, graphItem, cb) {
|
|
36
|
+
_add(subject, predicate, object, graphItem, c, cb) {
|
|
37
37
|
// Only add to the remaining indexes if there is not already a value in the index
|
|
38
38
|
if (!this._store._addToIndex(graphItem.subjects, subject, predicate, object)) return;
|
|
39
39
|
this._store._addToIndex(graphItem.predicates, predicate, object, subject);
|
|
@@ -42,43 +42,63 @@ class N3Reasoner {
|
|
|
42
42
|
// after all three indexes are updated, so a caught error leaves the store
|
|
43
43
|
// in a consistent state (the reasoning result is merely incomplete).
|
|
44
44
|
if (++this._derivations > this._maxDerivations) throw new Error(`Reasoning exceeded the maximum of ${this._maxDerivations} derivations`);
|
|
45
|
-
cb();
|
|
45
|
+
cb(c);
|
|
46
46
|
}
|
|
47
|
+
|
|
48
|
+
// Emit conclusions without allocating per-match callbacks
|
|
49
|
+
_emit(rule, content, cb) {
|
|
50
|
+
const conclusion = rule.conclusion;
|
|
51
|
+
for (let k = 0; k < conclusion.length; k++) {
|
|
52
|
+
const c = conclusion[k];
|
|
53
|
+
this._add(c.subject.value, c.predicate.value, c.object.value, content, c, cb);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Bound values use direct lookups; unbound values scan the index
|
|
47
58
|
_evaluatePremise(rule, content, cb, i = 0) {
|
|
48
|
-
let
|
|
59
|
+
let value, index1;
|
|
49
60
|
const [val0, val1, val2] = rule.premise[i].value,
|
|
50
61
|
index = content[rule.premise[i].content];
|
|
62
|
+
const last = i === rule.premise.length - 1;
|
|
51
63
|
const v0 = !(value = val0.value);
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
64
|
+
if (v0) {
|
|
65
|
+
// Intermediate index entries are always non-empty
|
|
66
|
+
for (value in index) {
|
|
67
|
+
index1 = index[value];
|
|
68
|
+
val0.value = Number(value);
|
|
69
|
+
this._evaluateLevel1(rule, content, cb, i, last, val1, val2, index1);
|
|
70
|
+
}
|
|
71
|
+
val0.value = null;
|
|
72
|
+
} else if (index1 = index[value]) {
|
|
73
|
+
this._evaluateLevel1(rule, content, cb, i, last, val1, val2, index1);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
_evaluateLevel1(rule, content, cb, i, last, val1, val2, index1) {
|
|
77
|
+
let value, index2;
|
|
78
|
+
const v1 = !(value = val1.value);
|
|
79
|
+
if (v1) {
|
|
80
|
+
for (value in index1) {
|
|
81
|
+
index2 = index1[value];
|
|
82
|
+
val1.value = Number(value);
|
|
83
|
+
this._evaluateLevel2(rule, content, cb, i, last, val2, index2);
|
|
84
|
+
}
|
|
85
|
+
val1.value = null;
|
|
86
|
+
} else if (index2 = index1[value]) {
|
|
87
|
+
this._evaluateLevel2(rule, content, cb, i, last, val2, index2);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
_evaluateLevel2(rule, content, cb, i, last, val2, index2) {
|
|
91
|
+
let value;
|
|
92
|
+
const v2 = !(value = val2.value);
|
|
93
|
+
if (v2) {
|
|
94
|
+
for (value in index2) {
|
|
95
|
+
val2.value = Number(value);
|
|
96
|
+
if (last) this._emit(rule, content, cb);else this._evaluatePremise(rule, content, cb, i + 1);
|
|
79
97
|
}
|
|
98
|
+
val2.value = null;
|
|
80
99
|
}
|
|
81
|
-
|
|
100
|
+
// Bound leaves run once even when the key is absent
|
|
101
|
+
else if (last) this._emit(rule, content, cb);else this._evaluatePremise(rule, content, cb, i + 1);
|
|
82
102
|
}
|
|
83
103
|
_evaluateRules(rules, content, cb) {
|
|
84
104
|
for (let i = 0; i < rules.length; i++) {
|
|
@@ -96,14 +116,12 @@ class N3Reasoner {
|
|
|
96
116
|
});
|
|
97
117
|
}
|
|
98
118
|
|
|
99
|
-
//
|
|
119
|
+
// Reuse addRule instead of allocating a callback per conclusion
|
|
100
120
|
const addConclusions = conclusion => {
|
|
101
|
-
conclusion.
|
|
102
|
-
|
|
103
|
-
this._add(c.subject.value, c.predicate.value, c.object.value, content,
|
|
104
|
-
|
|
105
|
-
});
|
|
106
|
-
});
|
|
121
|
+
for (let k = 0; k < conclusion.length; k++) {
|
|
122
|
+
const c = conclusion[k];
|
|
123
|
+
this._add(c.subject.value, c.predicate.value, c.object.value, content, c, addRule);
|
|
124
|
+
}
|
|
107
125
|
};
|
|
108
126
|
this._evaluateRules(rules, content, addRule);
|
|
109
127
|
let r;
|
|
@@ -194,9 +212,7 @@ class N3Reasoner {
|
|
|
194
212
|
basePremise: p
|
|
195
213
|
});
|
|
196
214
|
}
|
|
197
|
-
r2.variables.
|
|
198
|
-
v.value = null;
|
|
199
|
-
});
|
|
215
|
+
for (let k = 0; k < r2.variables.length; k++) r2.variables[k].value = null;
|
|
200
216
|
}
|
|
201
217
|
}
|
|
202
218
|
}
|
package/lib/N3Store.js
CHANGED
|
@@ -14,14 +14,22 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
|
|
|
14
14
|
// **N3Store** objects store N3 quads by graph in memory.
|
|
15
15
|
|
|
16
16
|
const ITERATOR = Symbol('iter');
|
|
17
|
+
const SIZE = Symbol('size');
|
|
17
18
|
function hasInIndex(index0, key0, key1, key2) {
|
|
18
19
|
const index1 = index0 && index0[key0];
|
|
19
20
|
const index2 = index1 && index1[key1];
|
|
20
21
|
return !!index2 && key2 in index2;
|
|
21
22
|
}
|
|
22
23
|
function merge(target, source, depth = 4) {
|
|
23
|
-
|
|
24
|
-
for (const key in source)
|
|
24
|
+
let size = target[SIZE] || 0;
|
|
25
|
+
for (const key in source) {
|
|
26
|
+
if (!(key in target)) {
|
|
27
|
+
size++;
|
|
28
|
+
target[key] = depth === 0 ? null : merge(Object.create(null), source[key], depth - 1);
|
|
29
|
+
} else if (depth !== 0) target[key] = merge(target[key], source[key], depth - 1);
|
|
30
|
+
}
|
|
31
|
+
// Depth 2 is the level of the `subjects`, `predicates`, and `objects` indexes.
|
|
32
|
+
if (depth <= 2) target[SIZE] = size;
|
|
25
33
|
return target;
|
|
26
34
|
}
|
|
27
35
|
|
|
@@ -33,13 +41,16 @@ function merge(target, source, depth = 4) {
|
|
|
33
41
|
* *not* be set as the value for an index.
|
|
34
42
|
*/
|
|
35
43
|
function intersect(s1, s2, depth = 4) {
|
|
36
|
-
let target = false
|
|
44
|
+
let target = false,
|
|
45
|
+
size = 0;
|
|
46
|
+
if (depth <= 2 && s2[SIZE] < s1[SIZE]) [s1, s2] = [s2, s1];
|
|
37
47
|
for (const key in s1) {
|
|
38
48
|
if (key in s2) {
|
|
39
49
|
const intersection = depth === 0 ? null : intersect(s1[key], s2[key], depth - 1);
|
|
40
50
|
if (intersection !== false) {
|
|
41
51
|
target = target || Object.create(null);
|
|
42
52
|
target[key] = intersection;
|
|
53
|
+
size++;
|
|
43
54
|
}
|
|
44
55
|
// Depth 3 is the 'subjects', 'predicates' and 'objects' keys.
|
|
45
56
|
// If the 'subjects' index is empty, so will the 'predicates' and 'objects' index.
|
|
@@ -48,6 +59,9 @@ function intersect(s1, s2, depth = 4) {
|
|
|
48
59
|
}
|
|
49
60
|
}
|
|
50
61
|
}
|
|
62
|
+
|
|
63
|
+
// Depth 2 is the level of the `subjects`, `predicates`, and `objects` indexes.
|
|
64
|
+
if (depth <= 2 && target) target[SIZE] = size;
|
|
51
65
|
return target;
|
|
52
66
|
}
|
|
53
67
|
|
|
@@ -59,18 +73,21 @@ function intersect(s1, s2, depth = 4) {
|
|
|
59
73
|
* *not* be set as the value for an index.
|
|
60
74
|
*/
|
|
61
75
|
function difference(s1, s2, depth = 4) {
|
|
62
|
-
let target = false
|
|
76
|
+
let target = false,
|
|
77
|
+
size = 0;
|
|
63
78
|
for (const key in s1) {
|
|
64
79
|
// When the key is not in the index, then none of the triples defined by s1[key] are
|
|
65
80
|
// in s2 and so we want to copy them over to the resultant store.
|
|
66
81
|
if (!(key in s2)) {
|
|
67
82
|
target = target || Object.create(null);
|
|
68
83
|
target[key] = depth === 0 ? null : merge({}, s1[key], depth - 1);
|
|
84
|
+
size++;
|
|
69
85
|
} else if (depth !== 0) {
|
|
70
86
|
const diff = difference(s1[key], s2[key], depth - 1);
|
|
71
87
|
if (diff !== false) {
|
|
72
88
|
target = target || Object.create(null);
|
|
73
89
|
target[key] = diff;
|
|
90
|
+
size++;
|
|
74
91
|
}
|
|
75
92
|
// Depth 3 is the 'subjects', 'predicates' and 'objects' keys.
|
|
76
93
|
// If the 'subjects' index is empty, so will the 'predicates' and 'objects' index.
|
|
@@ -79,6 +96,9 @@ function difference(s1, s2, depth = 4) {
|
|
|
79
96
|
}
|
|
80
97
|
}
|
|
81
98
|
}
|
|
99
|
+
|
|
100
|
+
// Depth 2 is the level of the `subjects`, `predicates`, and `objects` indexes.
|
|
101
|
+
if (depth <= 2 && target) target[SIZE] = size;
|
|
82
102
|
return target;
|
|
83
103
|
}
|
|
84
104
|
|
|
@@ -178,7 +198,7 @@ class N3Store {
|
|
|
178
198
|
size = 0;
|
|
179
199
|
const graphs = this._graphs;
|
|
180
200
|
let subjects, subject;
|
|
181
|
-
for (const graphKey in graphs) for (const subjectKey in subjects = graphs[graphKey].subjects) for (const predicateKey in subject = subjects[subjectKey]) size +=
|
|
201
|
+
for (const graphKey in graphs) for (const subjectKey in subjects = graphs[graphKey].subjects) for (const predicateKey in subject = subjects[subjectKey]) size += subject[predicateKey][SIZE];
|
|
182
202
|
return this._size = size;
|
|
183
203
|
}
|
|
184
204
|
|
|
@@ -187,12 +207,27 @@ class N3Store {
|
|
|
187
207
|
// ### `_addToIndex` adds a quad to a three-layered index.
|
|
188
208
|
// Returns if the index has changed, if the entry did not already exist.
|
|
189
209
|
_addToIndex(index0, key0, key1, key2) {
|
|
190
|
-
// Create layers as necessary
|
|
191
|
-
|
|
192
|
-
|
|
210
|
+
// Create layers as necessary, maintaining their entry counters
|
|
211
|
+
let index1 = index0[key0];
|
|
212
|
+
if (!index1) {
|
|
213
|
+
index0[key0] = index1 = {
|
|
214
|
+
[SIZE]: 0
|
|
215
|
+
};
|
|
216
|
+
index0[SIZE]++;
|
|
217
|
+
}
|
|
218
|
+
let index2 = index1[key1];
|
|
219
|
+
if (!index2) {
|
|
220
|
+
index1[key1] = index2 = {
|
|
221
|
+
[SIZE]: 0
|
|
222
|
+
};
|
|
223
|
+
index1[SIZE]++;
|
|
224
|
+
}
|
|
193
225
|
// Setting the key to _any_ value signals the presence of the quad
|
|
194
226
|
const existed = key2 in index2;
|
|
195
|
-
if (!existed)
|
|
227
|
+
if (!existed) {
|
|
228
|
+
index2[key2] = null;
|
|
229
|
+
index2[SIZE]++;
|
|
230
|
+
}
|
|
196
231
|
return !existed;
|
|
197
232
|
}
|
|
198
233
|
|
|
@@ -203,11 +238,13 @@ class N3Store {
|
|
|
203
238
|
index2 = index1[key1];
|
|
204
239
|
delete index2[key2];
|
|
205
240
|
|
|
206
|
-
// Remove intermediary index layers if they are empty
|
|
207
|
-
|
|
241
|
+
// Remove intermediary index layers if they are empty,
|
|
242
|
+
// which the entry counters detect in constant time
|
|
243
|
+
if (--index2[SIZE] !== 0) return;
|
|
208
244
|
delete index1[key1];
|
|
209
|
-
|
|
245
|
+
if (--index1[SIZE] !== 0) return;
|
|
210
246
|
delete index0[key0];
|
|
247
|
+
index0[SIZE]--;
|
|
211
248
|
}
|
|
212
249
|
|
|
213
250
|
// ### `_findInIndex` finds a set of quads in a three-layered index.
|
|
@@ -312,7 +349,7 @@ class N3Store {
|
|
|
312
349
|
// If a key is specified, count the quad if it exists
|
|
313
350
|
if (key2) key2 in index2 && count++;
|
|
314
351
|
// Otherwise, count all quads
|
|
315
|
-
else count +=
|
|
352
|
+
else count += index2[SIZE];
|
|
316
353
|
}
|
|
317
354
|
}
|
|
318
355
|
}
|
|
@@ -365,9 +402,15 @@ class N3Store {
|
|
|
365
402
|
// Create the graph if it doesn't exist yet
|
|
366
403
|
if (!graphItem) {
|
|
367
404
|
graphItem = this._graphs[graph] = {
|
|
368
|
-
subjects: {
|
|
369
|
-
|
|
370
|
-
|
|
405
|
+
subjects: {
|
|
406
|
+
[SIZE]: 0
|
|
407
|
+
},
|
|
408
|
+
predicates: {
|
|
409
|
+
[SIZE]: 0
|
|
410
|
+
},
|
|
411
|
+
objects: {
|
|
412
|
+
[SIZE]: 0
|
|
413
|
+
}
|
|
371
414
|
};
|
|
372
415
|
// Freezing a graph helps subsequent `add` performance,
|
|
373
416
|
// and properties will never be modified anyway
|
|
@@ -454,8 +497,7 @@ class N3Store {
|
|
|
454
497
|
if (this._size !== null) this._size--;
|
|
455
498
|
|
|
456
499
|
// Remove the graph if it is empty
|
|
457
|
-
|
|
458
|
-
delete graphs[graph];
|
|
500
|
+
if (graphItem.subjects[SIZE] === 0) delete graphs[graph];
|
|
459
501
|
return true;
|
|
460
502
|
}
|
|
461
503
|
|
|
@@ -1062,7 +1104,8 @@ exports.default = N3Store;
|
|
|
1062
1104
|
function indexMatch(index, ids, depth = 0) {
|
|
1063
1105
|
const ind = ids[depth];
|
|
1064
1106
|
if (ind && !(ind in index)) return false;
|
|
1065
|
-
let target = false
|
|
1107
|
+
let target = false,
|
|
1108
|
+
size = 0;
|
|
1066
1109
|
for (const key in ind ? {
|
|
1067
1110
|
[ind]: index[ind]
|
|
1068
1111
|
} : index) {
|
|
@@ -1070,8 +1113,10 @@ function indexMatch(index, ids, depth = 0) {
|
|
|
1070
1113
|
if (result !== false) {
|
|
1071
1114
|
target = target || Object.create(null);
|
|
1072
1115
|
target[key] = result;
|
|
1116
|
+
size++;
|
|
1073
1117
|
}
|
|
1074
1118
|
}
|
|
1119
|
+
if (target) target[SIZE] = size;
|
|
1075
1120
|
return target;
|
|
1076
1121
|
}
|
|
1077
1122
|
|
package/package.json
CHANGED
package/src/N3Reasoner.js
CHANGED
|
@@ -22,7 +22,7 @@ export default class N3Reasoner {
|
|
|
22
22
|
this._maxPremiseDepth = options.maxPremiseDepth === undefined ? Infinity : options.maxPremiseDepth;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
_add(subject, predicate, object, graphItem, cb) {
|
|
25
|
+
_add(subject, predicate, object, graphItem, c, cb) {
|
|
26
26
|
// Only add to the remaining indexes if there is not already a value in the index
|
|
27
27
|
if (!this._store._addToIndex(graphItem.subjects, subject, predicate, object)) return;
|
|
28
28
|
this._store._addToIndex(graphItem.predicates, predicate, object, subject);
|
|
@@ -32,39 +32,68 @@ export default class N3Reasoner {
|
|
|
32
32
|
// in a consistent state (the reasoning result is merely incomplete).
|
|
33
33
|
if (++this._derivations > this._maxDerivations)
|
|
34
34
|
throw new Error(`Reasoning exceeded the maximum of ${this._maxDerivations} derivations`);
|
|
35
|
-
cb();
|
|
35
|
+
cb(c);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
// Emit conclusions without allocating per-match callbacks
|
|
39
|
+
_emit(rule, content, cb) {
|
|
40
|
+
const conclusion = rule.conclusion;
|
|
41
|
+
for (let k = 0; k < conclusion.length; k++) {
|
|
42
|
+
const c = conclusion[k];
|
|
43
|
+
this._add(c.subject.value, c.predicate.value, c.object.value, content, c, cb);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Bound values use direct lookups; unbound values scan the index
|
|
38
48
|
_evaluatePremise(rule, content, cb, i = 0) {
|
|
39
|
-
let
|
|
49
|
+
let value, index1;
|
|
40
50
|
const [val0, val1, val2] = rule.premise[i].value, index = content[rule.premise[i].content];
|
|
51
|
+
const last = i === rule.premise.length - 1;
|
|
41
52
|
const v0 = !(value = val0.value);
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if (v1) val1.value = null;
|
|
53
|
+
if (v0) {
|
|
54
|
+
// Intermediate index entries are always non-empty
|
|
55
|
+
for (value in index) {
|
|
56
|
+
index1 = index[value];
|
|
57
|
+
val0.value = Number(value);
|
|
58
|
+
this._evaluateLevel1(rule, content, cb, i, last, val1, val2, index1);
|
|
59
|
+
}
|
|
60
|
+
val0.value = null;
|
|
61
|
+
}
|
|
62
|
+
else if (index1 = index[value]) {
|
|
63
|
+
this._evaluateLevel1(rule, content, cb, i, last, val1, val2, index1);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
_evaluateLevel1(rule, content, cb, i, last, val1, val2, index1) {
|
|
68
|
+
let value, index2;
|
|
69
|
+
const v1 = !(value = val1.value);
|
|
70
|
+
if (v1) {
|
|
71
|
+
for (value in index1) {
|
|
72
|
+
index2 = index1[value];
|
|
73
|
+
val1.value = Number(value);
|
|
74
|
+
this._evaluateLevel2(rule, content, cb, i, last, val2, index2);
|
|
65
75
|
}
|
|
76
|
+
val1.value = null;
|
|
77
|
+
}
|
|
78
|
+
else if (index2 = index1[value]) {
|
|
79
|
+
this._evaluateLevel2(rule, content, cb, i, last, val2, index2);
|
|
66
80
|
}
|
|
67
|
-
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
_evaluateLevel2(rule, content, cb, i, last, val2, index2) {
|
|
84
|
+
let value;
|
|
85
|
+
const v2 = !(value = val2.value);
|
|
86
|
+
if (v2) {
|
|
87
|
+
for (value in index2) {
|
|
88
|
+
val2.value = Number(value);
|
|
89
|
+
if (last) this._emit(rule, content, cb);
|
|
90
|
+
else this._evaluatePremise(rule, content, cb, i + 1);
|
|
91
|
+
}
|
|
92
|
+
val2.value = null;
|
|
93
|
+
}
|
|
94
|
+
// Bound leaves run once even when the key is absent
|
|
95
|
+
else if (last) this._emit(rule, content, cb);
|
|
96
|
+
else this._evaluatePremise(rule, content, cb, i + 1);
|
|
68
97
|
}
|
|
69
98
|
|
|
70
99
|
_evaluateRules(rules, content, cb) {
|
|
@@ -85,12 +114,12 @@ export default class N3Reasoner {
|
|
|
85
114
|
});
|
|
86
115
|
}
|
|
87
116
|
|
|
88
|
-
//
|
|
117
|
+
// Reuse addRule instead of allocating a callback per conclusion
|
|
89
118
|
const addConclusions = conclusion => {
|
|
90
|
-
conclusion.
|
|
91
|
-
|
|
92
|
-
this._add(c.subject.value, c.predicate.value, c.object.value, content,
|
|
93
|
-
}
|
|
119
|
+
for (let k = 0; k < conclusion.length; k++) {
|
|
120
|
+
const c = conclusion[k];
|
|
121
|
+
this._add(c.subject.value, c.predicate.value, c.object.value, content, c, addRule);
|
|
122
|
+
}
|
|
94
123
|
};
|
|
95
124
|
|
|
96
125
|
this._evaluateRules(rules, content, addRule);
|
|
@@ -185,7 +214,7 @@ export default class N3Reasoner {
|
|
|
185
214
|
basePremise: p,
|
|
186
215
|
});
|
|
187
216
|
}
|
|
188
|
-
r2.variables.
|
|
217
|
+
for (let k = 0; k < r2.variables.length; k++) r2.variables[k].value = null;
|
|
189
218
|
}
|
|
190
219
|
}
|
|
191
220
|
}
|
package/src/N3Store.js
CHANGED
|
@@ -6,6 +6,7 @@ import { isDefaultGraph } from './N3Util';
|
|
|
6
6
|
import N3Writer from './N3Writer';
|
|
7
7
|
|
|
8
8
|
const ITERATOR = Symbol('iter');
|
|
9
|
+
const SIZE = Symbol('size');
|
|
9
10
|
|
|
10
11
|
function hasInIndex(index0, key0, key1, key2) {
|
|
11
12
|
const index1 = index0 && index0[key0];
|
|
@@ -14,11 +15,18 @@ function hasInIndex(index0, key0, key1, key2) {
|
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
function merge(target, source, depth = 4) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
let size = target[SIZE] || 0;
|
|
19
|
+
for (const key in source) {
|
|
20
|
+
if (!(key in target)) {
|
|
21
|
+
size++;
|
|
22
|
+
target[key] = depth === 0 ? null : merge(Object.create(null), source[key], depth - 1);
|
|
23
|
+
}
|
|
24
|
+
else if (depth !== 0)
|
|
25
|
+
target[key] = merge(target[key], source[key], depth - 1);
|
|
26
|
+
}
|
|
27
|
+
// Depth 2 is the level of the `subjects`, `predicates`, and `objects` indexes.
|
|
28
|
+
if (depth <= 2)
|
|
29
|
+
target[SIZE] = size;
|
|
22
30
|
|
|
23
31
|
return target;
|
|
24
32
|
}
|
|
@@ -31,7 +39,10 @@ function merge(target, source, depth = 4) {
|
|
|
31
39
|
* *not* be set as the value for an index.
|
|
32
40
|
*/
|
|
33
41
|
function intersect(s1, s2, depth = 4) {
|
|
34
|
-
let target = false;
|
|
42
|
+
let target = false, size = 0;
|
|
43
|
+
|
|
44
|
+
if (depth <= 2 && s2[SIZE] < s1[SIZE])
|
|
45
|
+
[s1, s2] = [s2, s1];
|
|
35
46
|
|
|
36
47
|
for (const key in s1) {
|
|
37
48
|
if (key in s2) {
|
|
@@ -39,6 +50,7 @@ function intersect(s1, s2, depth = 4) {
|
|
|
39
50
|
if (intersection !== false) {
|
|
40
51
|
target = target || Object.create(null);
|
|
41
52
|
target[key] = intersection;
|
|
53
|
+
size++;
|
|
42
54
|
}
|
|
43
55
|
// Depth 3 is the 'subjects', 'predicates' and 'objects' keys.
|
|
44
56
|
// If the 'subjects' index is empty, so will the 'predicates' and 'objects' index.
|
|
@@ -48,6 +60,10 @@ function intersect(s1, s2, depth = 4) {
|
|
|
48
60
|
}
|
|
49
61
|
}
|
|
50
62
|
|
|
63
|
+
// Depth 2 is the level of the `subjects`, `predicates`, and `objects` indexes.
|
|
64
|
+
if (depth <= 2 && target)
|
|
65
|
+
target[SIZE] = size;
|
|
66
|
+
|
|
51
67
|
return target;
|
|
52
68
|
}
|
|
53
69
|
|
|
@@ -59,7 +75,7 @@ function intersect(s1, s2, depth = 4) {
|
|
|
59
75
|
* *not* be set as the value for an index.
|
|
60
76
|
*/
|
|
61
77
|
function difference(s1, s2, depth = 4) {
|
|
62
|
-
let target = false;
|
|
78
|
+
let target = false, size = 0;
|
|
63
79
|
|
|
64
80
|
for (const key in s1) {
|
|
65
81
|
// When the key is not in the index, then none of the triples defined by s1[key] are
|
|
@@ -67,12 +83,14 @@ function difference(s1, s2, depth = 4) {
|
|
|
67
83
|
if (!(key in s2)) {
|
|
68
84
|
target = target || Object.create(null);
|
|
69
85
|
target[key] = depth === 0 ? null : merge({}, s1[key], depth - 1);
|
|
86
|
+
size++;
|
|
70
87
|
}
|
|
71
88
|
else if (depth !== 0) {
|
|
72
89
|
const diff = difference(s1[key], s2[key], depth - 1);
|
|
73
90
|
if (diff !== false) {
|
|
74
91
|
target = target || Object.create(null);
|
|
75
92
|
target[key] = diff;
|
|
93
|
+
size++;
|
|
76
94
|
}
|
|
77
95
|
// Depth 3 is the 'subjects', 'predicates' and 'objects' keys.
|
|
78
96
|
// If the 'subjects' index is empty, so will the 'predicates' and 'objects' index.
|
|
@@ -82,6 +100,10 @@ function difference(s1, s2, depth = 4) {
|
|
|
82
100
|
}
|
|
83
101
|
}
|
|
84
102
|
|
|
103
|
+
// Depth 2 is the level of the `subjects`, `predicates`, and `objects` indexes.
|
|
104
|
+
if (depth <= 2 && target)
|
|
105
|
+
target[SIZE] = size;
|
|
106
|
+
|
|
85
107
|
return target;
|
|
86
108
|
}
|
|
87
109
|
|
|
@@ -200,7 +222,7 @@ export default class N3Store {
|
|
|
200
222
|
for (const graphKey in graphs)
|
|
201
223
|
for (const subjectKey in (subjects = graphs[graphKey].subjects))
|
|
202
224
|
for (const predicateKey in (subject = subjects[subjectKey]))
|
|
203
|
-
size +=
|
|
225
|
+
size += subject[predicateKey][SIZE];
|
|
204
226
|
return this._size = size;
|
|
205
227
|
}
|
|
206
228
|
|
|
@@ -209,13 +231,23 @@ export default class N3Store {
|
|
|
209
231
|
// ### `_addToIndex` adds a quad to a three-layered index.
|
|
210
232
|
// Returns if the index has changed, if the entry did not already exist.
|
|
211
233
|
_addToIndex(index0, key0, key1, key2) {
|
|
212
|
-
// Create layers as necessary
|
|
213
|
-
|
|
214
|
-
|
|
234
|
+
// Create layers as necessary, maintaining their entry counters
|
|
235
|
+
let index1 = index0[key0];
|
|
236
|
+
if (!index1) {
|
|
237
|
+
index0[key0] = index1 = { [SIZE]: 0 };
|
|
238
|
+
index0[SIZE]++;
|
|
239
|
+
}
|
|
240
|
+
let index2 = index1[key1];
|
|
241
|
+
if (!index2) {
|
|
242
|
+
index1[key1] = index2 = { [SIZE]: 0 };
|
|
243
|
+
index1[SIZE]++;
|
|
244
|
+
}
|
|
215
245
|
// Setting the key to _any_ value signals the presence of the quad
|
|
216
246
|
const existed = key2 in index2;
|
|
217
|
-
if (!existed)
|
|
247
|
+
if (!existed) {
|
|
218
248
|
index2[key2] = null;
|
|
249
|
+
index2[SIZE]++;
|
|
250
|
+
}
|
|
219
251
|
return !existed;
|
|
220
252
|
}
|
|
221
253
|
|
|
@@ -225,11 +257,13 @@ export default class N3Store {
|
|
|
225
257
|
const index1 = index0[key0], index2 = index1[key1];
|
|
226
258
|
delete index2[key2];
|
|
227
259
|
|
|
228
|
-
// Remove intermediary index layers if they are empty
|
|
229
|
-
|
|
260
|
+
// Remove intermediary index layers if they are empty,
|
|
261
|
+
// which the entry counters detect in constant time
|
|
262
|
+
if (--index2[SIZE] !== 0) return;
|
|
230
263
|
delete index1[key1];
|
|
231
|
-
|
|
264
|
+
if (--index1[SIZE] !== 0) return;
|
|
232
265
|
delete index0[key0];
|
|
266
|
+
index0[SIZE]--;
|
|
233
267
|
}
|
|
234
268
|
|
|
235
269
|
// ### `_findInIndex` finds a set of quads in a three-layered index.
|
|
@@ -332,7 +366,7 @@ export default class N3Store {
|
|
|
332
366
|
// If a key is specified, count the quad if it exists
|
|
333
367
|
if (key2) (key2 in index2) && count++;
|
|
334
368
|
// Otherwise, count all quads
|
|
335
|
-
else count +=
|
|
369
|
+
else count += index2[SIZE];
|
|
336
370
|
}
|
|
337
371
|
}
|
|
338
372
|
}
|
|
@@ -384,7 +418,11 @@ export default class N3Store {
|
|
|
384
418
|
let graphItem = this._graphs[graph];
|
|
385
419
|
// Create the graph if it doesn't exist yet
|
|
386
420
|
if (!graphItem) {
|
|
387
|
-
graphItem = this._graphs[graph] = {
|
|
421
|
+
graphItem = this._graphs[graph] = {
|
|
422
|
+
subjects: { [SIZE]: 0 },
|
|
423
|
+
predicates: { [SIZE]: 0 },
|
|
424
|
+
objects: { [SIZE]: 0 },
|
|
425
|
+
};
|
|
388
426
|
// Freezing a graph helps subsequent `add` performance,
|
|
389
427
|
// and properties will never be modified anyway
|
|
390
428
|
Object.freeze(graphItem);
|
|
@@ -469,8 +507,8 @@ export default class N3Store {
|
|
|
469
507
|
if (this._size !== null) this._size--;
|
|
470
508
|
|
|
471
509
|
// Remove the graph if it is empty
|
|
472
|
-
|
|
473
|
-
|
|
510
|
+
if (graphItem.subjects[SIZE] === 0)
|
|
511
|
+
delete graphs[graph];
|
|
474
512
|
return true;
|
|
475
513
|
}
|
|
476
514
|
|
|
@@ -1130,15 +1168,18 @@ function indexMatch(index, ids, depth = 0) {
|
|
|
1130
1168
|
if (ind && !(ind in index))
|
|
1131
1169
|
return false;
|
|
1132
1170
|
|
|
1133
|
-
let target = false;
|
|
1171
|
+
let target = false, size = 0;
|
|
1134
1172
|
for (const key in (ind ? { [ind]: index[ind] } : index)) {
|
|
1135
1173
|
const result = depth === 2 ? null : indexMatch(index[key], ids, depth + 1);
|
|
1136
1174
|
|
|
1137
1175
|
if (result !== false) {
|
|
1138
1176
|
target = target || Object.create(null);
|
|
1139
1177
|
target[key] = result;
|
|
1178
|
+
size++;
|
|
1140
1179
|
}
|
|
1141
1180
|
}
|
|
1181
|
+
if (target)
|
|
1182
|
+
target[SIZE] = size;
|
|
1142
1183
|
return target;
|
|
1143
1184
|
}
|
|
1144
1185
|
|