@suss/datalog 0.37.0 → 0.38.0
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/dist/index.cjs +132 -48
- package/dist/index.d.cts +18 -10
- package/dist/index.d.ts +18 -10
- package/dist/index.js +132 -48
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -50,45 +50,93 @@ __export(index_exports, {
|
|
|
50
50
|
module.exports = __toCommonJS(index_exports);
|
|
51
51
|
|
|
52
52
|
// src/factIndex.ts
|
|
53
|
-
var
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
var FactKey = class {
|
|
54
|
+
constructor(tuple) {
|
|
55
|
+
this.tuple = tuple;
|
|
56
|
+
}
|
|
57
|
+
tuple;
|
|
58
|
+
present = false;
|
|
59
|
+
tag = void 0;
|
|
60
|
+
};
|
|
61
|
+
var Branch = class {
|
|
62
|
+
/** The key of a tuple that ends at this column, as a shorter one in a relation of mixed arity does. */
|
|
63
|
+
here = void 0;
|
|
64
|
+
next = /* @__PURE__ */ new Map();
|
|
65
|
+
};
|
|
66
|
+
function sameFrom(a, b, from) {
|
|
67
|
+
if (a.length !== b.length) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
for (let at = from; at < a.length; at++) {
|
|
71
|
+
if (a[at] !== b[at]) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
function place(branch, key, depth) {
|
|
78
|
+
if (depth === key.tuple.length) {
|
|
79
|
+
branch.here = key;
|
|
80
|
+
} else {
|
|
81
|
+
branch.next.set(key.tuple[depth], key);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function soleKey(branch) {
|
|
85
|
+
if (branch.here !== void 0) {
|
|
86
|
+
return branch.next.size === 0 ? branch.here : void 0;
|
|
87
|
+
}
|
|
88
|
+
if (branch.next.size !== 1) {
|
|
89
|
+
return void 0;
|
|
90
|
+
}
|
|
91
|
+
const only = branch.next.values().next().value;
|
|
92
|
+
return only instanceof FactKey ? only : void 0;
|
|
93
|
+
}
|
|
58
94
|
var FactIndex = class {
|
|
59
95
|
/** How many facts are present. */
|
|
60
96
|
size = 0;
|
|
61
|
-
root =
|
|
97
|
+
root = new Branch();
|
|
62
98
|
/** This tuple's key here, whether or not the fact is present. */
|
|
63
99
|
key(tuple) {
|
|
64
|
-
let
|
|
65
|
-
for (let
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
node.next = next;
|
|
100
|
+
let branch = this.root;
|
|
101
|
+
for (let depth = 0; ; depth++) {
|
|
102
|
+
if (depth === tuple.length) {
|
|
103
|
+
branch.here ??= new FactKey(tuple);
|
|
104
|
+
return branch.here;
|
|
70
105
|
}
|
|
71
|
-
const atom = tuple[
|
|
72
|
-
|
|
106
|
+
const atom = tuple[depth];
|
|
107
|
+
const child = branch.next.get(atom);
|
|
73
108
|
if (child === void 0) {
|
|
74
|
-
|
|
75
|
-
next.set(atom,
|
|
109
|
+
const key = new FactKey(tuple);
|
|
110
|
+
branch.next.set(atom, key);
|
|
111
|
+
return key;
|
|
112
|
+
}
|
|
113
|
+
if (child instanceof Branch) {
|
|
114
|
+
branch = child;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (sameFrom(child.tuple, tuple, depth + 1)) {
|
|
118
|
+
return child;
|
|
76
119
|
}
|
|
77
|
-
|
|
120
|
+
const split = new Branch();
|
|
121
|
+
place(split, child, depth + 1);
|
|
122
|
+
branch.next.set(atom, split);
|
|
123
|
+
branch = split;
|
|
78
124
|
}
|
|
79
|
-
return node;
|
|
80
125
|
}
|
|
81
126
|
/** The key of a fact this relation has, or undefined when it has none. */
|
|
82
127
|
find(tuple) {
|
|
83
|
-
let
|
|
84
|
-
for (let
|
|
85
|
-
const child =
|
|
128
|
+
let branch = this.root;
|
|
129
|
+
for (let depth = 0; ; depth++) {
|
|
130
|
+
const child = depth === tuple.length ? branch.here : branch.next.get(tuple[depth]);
|
|
86
131
|
if (child === void 0) {
|
|
87
132
|
return void 0;
|
|
88
133
|
}
|
|
89
|
-
|
|
134
|
+
if (child instanceof Branch) {
|
|
135
|
+
branch = child;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
return child.present && sameFrom(child.tuple, tuple, depth + 1) ? child : void 0;
|
|
90
139
|
}
|
|
91
|
-
return node.present ? node : void 0;
|
|
92
140
|
}
|
|
93
141
|
has(key) {
|
|
94
142
|
return key.present;
|
|
@@ -112,32 +160,45 @@ var FactIndex = class {
|
|
|
112
160
|
this.size -= 1;
|
|
113
161
|
}
|
|
114
162
|
/**
|
|
115
|
-
* Drop
|
|
116
|
-
*
|
|
117
|
-
*
|
|
163
|
+
* Drop a removed tuple's key, and any branch left with one key to
|
|
164
|
+
* tell apart. Without this a relation that is retracted from over and
|
|
165
|
+
* over keeps a key per tuple it ever had.
|
|
118
166
|
*/
|
|
119
167
|
prune(tuple) {
|
|
120
|
-
const path = [
|
|
121
|
-
let
|
|
122
|
-
|
|
123
|
-
|
|
168
|
+
const path = [];
|
|
169
|
+
let branch = this.root;
|
|
170
|
+
let depth = 0;
|
|
171
|
+
for (; ; depth++) {
|
|
172
|
+
path.push(branch);
|
|
173
|
+
const child = depth === tuple.length ? branch.here : branch.next.get(tuple[depth]);
|
|
124
174
|
if (child === void 0) {
|
|
125
175
|
return;
|
|
126
176
|
}
|
|
127
|
-
|
|
128
|
-
|
|
177
|
+
if (child instanceof Branch) {
|
|
178
|
+
branch = child;
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (child.present || !sameFrom(child.tuple, tuple, depth + 1)) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
if (depth === tuple.length) {
|
|
187
|
+
branch.here = void 0;
|
|
188
|
+
} else {
|
|
189
|
+
branch.next.delete(tuple[depth]);
|
|
129
190
|
}
|
|
130
|
-
for (let
|
|
131
|
-
const
|
|
132
|
-
if (
|
|
191
|
+
for (let at = path.length - 1; at > 0; at--) {
|
|
192
|
+
const sole = soleKey(path[at]);
|
|
193
|
+
if (sole === void 0) {
|
|
133
194
|
return;
|
|
134
195
|
}
|
|
135
|
-
path[
|
|
196
|
+
path[at - 1].next.set(tuple[at - 1], sole);
|
|
136
197
|
}
|
|
137
198
|
}
|
|
138
199
|
/** Drop every fact, and whatever was kept in order to find them. */
|
|
139
200
|
clear() {
|
|
140
|
-
this.root =
|
|
201
|
+
this.root = new Branch();
|
|
141
202
|
this.size = 0;
|
|
142
203
|
}
|
|
143
204
|
};
|
|
@@ -574,6 +635,24 @@ function formatProfile(profile) {
|
|
|
574
635
|
}
|
|
575
636
|
|
|
576
637
|
// src/relation.ts
|
|
638
|
+
var isLone = (bucket) => typeof bucket[0] !== "object";
|
|
639
|
+
var NO_TUPLES = [];
|
|
640
|
+
function tuplesIn(bucket) {
|
|
641
|
+
if (bucket === void 0) {
|
|
642
|
+
return NO_TUPLES;
|
|
643
|
+
}
|
|
644
|
+
return isLone(bucket) ? [bucket] : bucket;
|
|
645
|
+
}
|
|
646
|
+
function fileUnder(buckets, key, tuple) {
|
|
647
|
+
const bucket = buckets.get(key);
|
|
648
|
+
if (bucket === void 0) {
|
|
649
|
+
buckets.set(key, tuple);
|
|
650
|
+
} else if (isLone(bucket)) {
|
|
651
|
+
buckets.set(key, [bucket, tuple]);
|
|
652
|
+
} else {
|
|
653
|
+
bucket.push(tuple);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
577
656
|
var sameTuple = (a, b) => a.length === b.length && a.every((atom, at) => atom === b[at]);
|
|
578
657
|
function storedTuplesOf(relation, facts) {
|
|
579
658
|
const column = relation.columns.findIndex((buckets2) => buckets2 !== void 0);
|
|
@@ -582,19 +661,24 @@ function storedTuplesOf(relation, facts) {
|
|
|
582
661
|
return null;
|
|
583
662
|
}
|
|
584
663
|
const stored = facts.map(
|
|
585
|
-
(fact) => buckets.get(fact[column])
|
|
664
|
+
(fact) => tuplesIn(buckets.get(fact[column])).find(
|
|
665
|
+
(tuple) => sameTuple(tuple, fact)
|
|
666
|
+
)
|
|
586
667
|
);
|
|
587
668
|
return stored.every((tuple) => tuple !== void 0) ? stored : null;
|
|
588
669
|
}
|
|
589
670
|
function removeFrom(buckets, key, tuple) {
|
|
590
671
|
const bucket = buckets.get(key);
|
|
591
|
-
|
|
592
|
-
if (bucket === void 0 || at === -1) {
|
|
672
|
+
if (bucket === void 0) {
|
|
593
673
|
return;
|
|
594
674
|
}
|
|
595
|
-
bucket
|
|
596
|
-
if (bucket.length === 0) {
|
|
675
|
+
if (isLone(bucket)) {
|
|
597
676
|
buckets.delete(key);
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
bucket.splice(bucket.indexOf(tuple), 1);
|
|
680
|
+
if (bucket.length === 1) {
|
|
681
|
+
buckets.set(key, bucket[0]);
|
|
598
682
|
}
|
|
599
683
|
}
|
|
600
684
|
function removeTuples(relation, stored) {
|
|
@@ -646,7 +730,7 @@ function addToCombined(index, tuple) {
|
|
|
646
730
|
}
|
|
647
731
|
level = next;
|
|
648
732
|
}
|
|
649
|
-
|
|
733
|
+
fileUnder(
|
|
650
734
|
level,
|
|
651
735
|
tuple[index.columns[last]],
|
|
652
736
|
tuple
|
|
@@ -665,10 +749,10 @@ function combinedBucketIn(relation, mask, values) {
|
|
|
665
749
|
for (const value of values) {
|
|
666
750
|
level = level.get(value);
|
|
667
751
|
if (level === void 0) {
|
|
668
|
-
return
|
|
752
|
+
return NO_TUPLES;
|
|
669
753
|
}
|
|
670
754
|
}
|
|
671
|
-
return level;
|
|
755
|
+
return tuplesIn(level);
|
|
672
756
|
}
|
|
673
757
|
function addToBucket(buckets, key, tuple) {
|
|
674
758
|
const bucket = buckets.get(key);
|
|
@@ -685,12 +769,12 @@ function bucketIn(relation, column, value) {
|
|
|
685
769
|
for (const tuple of relation.tuples) {
|
|
686
770
|
const at = tuple[column];
|
|
687
771
|
if (at !== void 0) {
|
|
688
|
-
|
|
772
|
+
fileUnder(buckets, at, tuple);
|
|
689
773
|
}
|
|
690
774
|
}
|
|
691
775
|
relation.columns[column] = buckets;
|
|
692
776
|
}
|
|
693
|
-
return buckets.get(value)
|
|
777
|
+
return tuplesIn(buckets.get(value));
|
|
694
778
|
}
|
|
695
779
|
|
|
696
780
|
// src/stratum.ts
|
|
@@ -956,7 +1040,7 @@ var Database = class {
|
|
|
956
1040
|
const buckets = relation.columns[column];
|
|
957
1041
|
const value = tuple[column];
|
|
958
1042
|
if (buckets !== void 0 && value !== void 0) {
|
|
959
|
-
|
|
1043
|
+
fileUnder(buckets, value, tuple);
|
|
960
1044
|
}
|
|
961
1045
|
}
|
|
962
1046
|
for (const combined of relation.combined.values()) {
|
package/dist/index.d.cts
CHANGED
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
* The join asks for that key again on every candidate the rules
|
|
8
8
|
* consider, so what the key costs to work out sets the cost of
|
|
9
9
|
* evaluation. A relation finds a fact by walking a trie of its tuple's
|
|
10
|
-
* atoms, one map hop per column,
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* why the walk is over the atoms
|
|
10
|
+
* atoms, one map hop per column, to the first column where no other
|
|
11
|
+
* tuple shares the atoms so far. The key is stored there, and the walk
|
|
12
|
+
* compares the rest of the tuple instead of taking a map per column.
|
|
13
|
+
* DESIGN.md says why the walk is over the atoms, not interned integers.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -18,10 +18,11 @@
|
|
|
18
18
|
* nothing to the relation that did not hand it out, and nothing outside
|
|
19
19
|
* this module reads its fields.
|
|
20
20
|
*/
|
|
21
|
-
|
|
21
|
+
declare class FactKey {
|
|
22
|
+
readonly tuple: Tuple;
|
|
22
23
|
present: boolean;
|
|
23
24
|
tag: unknown;
|
|
24
|
-
|
|
25
|
+
constructor(tuple: Tuple);
|
|
25
26
|
}
|
|
26
27
|
/** The facts one relation has, and the tag stored against each. */
|
|
27
28
|
declare class FactIndex {
|
|
@@ -40,9 +41,9 @@ declare class FactIndex {
|
|
|
40
41
|
/** Take the fact away, and the tag with it. */
|
|
41
42
|
remove(key: FactKey): void;
|
|
42
43
|
/**
|
|
43
|
-
* Drop
|
|
44
|
-
*
|
|
45
|
-
*
|
|
44
|
+
* Drop a removed tuple's key, and any branch left with one key to
|
|
45
|
+
* tell apart. Without this a relation that is retracted from over and
|
|
46
|
+
* over keeps a key per tuple it ever had.
|
|
46
47
|
*/
|
|
47
48
|
prune(tuple: Tuple): void;
|
|
48
49
|
/** Drop every fact, and whatever was kept in order to find them. */
|
|
@@ -70,7 +71,7 @@ interface Relation {
|
|
|
70
71
|
* encoding it first would build a string out of every node id on every
|
|
71
72
|
* lookup and buy nothing.
|
|
72
73
|
*/
|
|
73
|
-
columns: (Map<Atom,
|
|
74
|
+
columns: (Map<Atom, Bucket> | undefined)[];
|
|
74
75
|
/**
|
|
75
76
|
* Per set of columns, the tuples under each combination of values at
|
|
76
77
|
* those columns, keyed by a bitmask of the column positions. Built and
|
|
@@ -86,6 +87,13 @@ interface CombinedIndex {
|
|
|
86
87
|
columns: readonly number[];
|
|
87
88
|
root: Map<Atom, unknown>;
|
|
88
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* The tuples an index has under one value, in the order they arrived.
|
|
92
|
+
* Most values in a column of node ids belong to a single tuple, and an
|
|
93
|
+
* array for each of those cost more than the tuple itself, so a lone
|
|
94
|
+
* tuple is kept on its own.
|
|
95
|
+
*/
|
|
96
|
+
type Bucket = Tuple | Tuple[];
|
|
89
97
|
|
|
90
98
|
/**
|
|
91
99
|
* Confidence levels as a tag algebra: how sure the run is of each
|
package/dist/index.d.ts
CHANGED
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
* The join asks for that key again on every candidate the rules
|
|
8
8
|
* consider, so what the key costs to work out sets the cost of
|
|
9
9
|
* evaluation. A relation finds a fact by walking a trie of its tuple's
|
|
10
|
-
* atoms, one map hop per column,
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* why the walk is over the atoms
|
|
10
|
+
* atoms, one map hop per column, to the first column where no other
|
|
11
|
+
* tuple shares the atoms so far. The key is stored there, and the walk
|
|
12
|
+
* compares the rest of the tuple instead of taking a map per column.
|
|
13
|
+
* DESIGN.md says why the walk is over the atoms, not interned integers.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -18,10 +18,11 @@
|
|
|
18
18
|
* nothing to the relation that did not hand it out, and nothing outside
|
|
19
19
|
* this module reads its fields.
|
|
20
20
|
*/
|
|
21
|
-
|
|
21
|
+
declare class FactKey {
|
|
22
|
+
readonly tuple: Tuple;
|
|
22
23
|
present: boolean;
|
|
23
24
|
tag: unknown;
|
|
24
|
-
|
|
25
|
+
constructor(tuple: Tuple);
|
|
25
26
|
}
|
|
26
27
|
/** The facts one relation has, and the tag stored against each. */
|
|
27
28
|
declare class FactIndex {
|
|
@@ -40,9 +41,9 @@ declare class FactIndex {
|
|
|
40
41
|
/** Take the fact away, and the tag with it. */
|
|
41
42
|
remove(key: FactKey): void;
|
|
42
43
|
/**
|
|
43
|
-
* Drop
|
|
44
|
-
*
|
|
45
|
-
*
|
|
44
|
+
* Drop a removed tuple's key, and any branch left with one key to
|
|
45
|
+
* tell apart. Without this a relation that is retracted from over and
|
|
46
|
+
* over keeps a key per tuple it ever had.
|
|
46
47
|
*/
|
|
47
48
|
prune(tuple: Tuple): void;
|
|
48
49
|
/** Drop every fact, and whatever was kept in order to find them. */
|
|
@@ -70,7 +71,7 @@ interface Relation {
|
|
|
70
71
|
* encoding it first would build a string out of every node id on every
|
|
71
72
|
* lookup and buy nothing.
|
|
72
73
|
*/
|
|
73
|
-
columns: (Map<Atom,
|
|
74
|
+
columns: (Map<Atom, Bucket> | undefined)[];
|
|
74
75
|
/**
|
|
75
76
|
* Per set of columns, the tuples under each combination of values at
|
|
76
77
|
* those columns, keyed by a bitmask of the column positions. Built and
|
|
@@ -86,6 +87,13 @@ interface CombinedIndex {
|
|
|
86
87
|
columns: readonly number[];
|
|
87
88
|
root: Map<Atom, unknown>;
|
|
88
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* The tuples an index has under one value, in the order they arrived.
|
|
92
|
+
* Most values in a column of node ids belong to a single tuple, and an
|
|
93
|
+
* array for each of those cost more than the tuple itself, so a lone
|
|
94
|
+
* tuple is kept on its own.
|
|
95
|
+
*/
|
|
96
|
+
type Bucket = Tuple | Tuple[];
|
|
89
97
|
|
|
90
98
|
/**
|
|
91
99
|
* Confidence levels as a tag algebra: how sure the run is of each
|
package/dist/index.js
CHANGED
|
@@ -1,43 +1,91 @@
|
|
|
1
1
|
// src/factIndex.ts
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
var FactKey = class {
|
|
3
|
+
constructor(tuple) {
|
|
4
|
+
this.tuple = tuple;
|
|
5
|
+
}
|
|
6
|
+
tuple;
|
|
7
|
+
present = false;
|
|
8
|
+
tag = void 0;
|
|
9
|
+
};
|
|
10
|
+
var Branch = class {
|
|
11
|
+
/** The key of a tuple that ends at this column, as a shorter one in a relation of mixed arity does. */
|
|
12
|
+
here = void 0;
|
|
13
|
+
next = /* @__PURE__ */ new Map();
|
|
14
|
+
};
|
|
15
|
+
function sameFrom(a, b, from) {
|
|
16
|
+
if (a.length !== b.length) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
for (let at = from; at < a.length; at++) {
|
|
20
|
+
if (a[at] !== b[at]) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
function place(branch, key, depth) {
|
|
27
|
+
if (depth === key.tuple.length) {
|
|
28
|
+
branch.here = key;
|
|
29
|
+
} else {
|
|
30
|
+
branch.next.set(key.tuple[depth], key);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function soleKey(branch) {
|
|
34
|
+
if (branch.here !== void 0) {
|
|
35
|
+
return branch.next.size === 0 ? branch.here : void 0;
|
|
36
|
+
}
|
|
37
|
+
if (branch.next.size !== 1) {
|
|
38
|
+
return void 0;
|
|
39
|
+
}
|
|
40
|
+
const only = branch.next.values().next().value;
|
|
41
|
+
return only instanceof FactKey ? only : void 0;
|
|
42
|
+
}
|
|
7
43
|
var FactIndex = class {
|
|
8
44
|
/** How many facts are present. */
|
|
9
45
|
size = 0;
|
|
10
|
-
root =
|
|
46
|
+
root = new Branch();
|
|
11
47
|
/** This tuple's key here, whether or not the fact is present. */
|
|
12
48
|
key(tuple) {
|
|
13
|
-
let
|
|
14
|
-
for (let
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
node.next = next;
|
|
49
|
+
let branch = this.root;
|
|
50
|
+
for (let depth = 0; ; depth++) {
|
|
51
|
+
if (depth === tuple.length) {
|
|
52
|
+
branch.here ??= new FactKey(tuple);
|
|
53
|
+
return branch.here;
|
|
19
54
|
}
|
|
20
|
-
const atom = tuple[
|
|
21
|
-
|
|
55
|
+
const atom = tuple[depth];
|
|
56
|
+
const child = branch.next.get(atom);
|
|
22
57
|
if (child === void 0) {
|
|
23
|
-
|
|
24
|
-
next.set(atom,
|
|
58
|
+
const key = new FactKey(tuple);
|
|
59
|
+
branch.next.set(atom, key);
|
|
60
|
+
return key;
|
|
61
|
+
}
|
|
62
|
+
if (child instanceof Branch) {
|
|
63
|
+
branch = child;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (sameFrom(child.tuple, tuple, depth + 1)) {
|
|
67
|
+
return child;
|
|
25
68
|
}
|
|
26
|
-
|
|
69
|
+
const split = new Branch();
|
|
70
|
+
place(split, child, depth + 1);
|
|
71
|
+
branch.next.set(atom, split);
|
|
72
|
+
branch = split;
|
|
27
73
|
}
|
|
28
|
-
return node;
|
|
29
74
|
}
|
|
30
75
|
/** The key of a fact this relation has, or undefined when it has none. */
|
|
31
76
|
find(tuple) {
|
|
32
|
-
let
|
|
33
|
-
for (let
|
|
34
|
-
const child =
|
|
77
|
+
let branch = this.root;
|
|
78
|
+
for (let depth = 0; ; depth++) {
|
|
79
|
+
const child = depth === tuple.length ? branch.here : branch.next.get(tuple[depth]);
|
|
35
80
|
if (child === void 0) {
|
|
36
81
|
return void 0;
|
|
37
82
|
}
|
|
38
|
-
|
|
83
|
+
if (child instanceof Branch) {
|
|
84
|
+
branch = child;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
return child.present && sameFrom(child.tuple, tuple, depth + 1) ? child : void 0;
|
|
39
88
|
}
|
|
40
|
-
return node.present ? node : void 0;
|
|
41
89
|
}
|
|
42
90
|
has(key) {
|
|
43
91
|
return key.present;
|
|
@@ -61,32 +109,45 @@ var FactIndex = class {
|
|
|
61
109
|
this.size -= 1;
|
|
62
110
|
}
|
|
63
111
|
/**
|
|
64
|
-
* Drop
|
|
65
|
-
*
|
|
66
|
-
*
|
|
112
|
+
* Drop a removed tuple's key, and any branch left with one key to
|
|
113
|
+
* tell apart. Without this a relation that is retracted from over and
|
|
114
|
+
* over keeps a key per tuple it ever had.
|
|
67
115
|
*/
|
|
68
116
|
prune(tuple) {
|
|
69
|
-
const path = [
|
|
70
|
-
let
|
|
71
|
-
|
|
72
|
-
|
|
117
|
+
const path = [];
|
|
118
|
+
let branch = this.root;
|
|
119
|
+
let depth = 0;
|
|
120
|
+
for (; ; depth++) {
|
|
121
|
+
path.push(branch);
|
|
122
|
+
const child = depth === tuple.length ? branch.here : branch.next.get(tuple[depth]);
|
|
73
123
|
if (child === void 0) {
|
|
74
124
|
return;
|
|
75
125
|
}
|
|
76
|
-
|
|
77
|
-
|
|
126
|
+
if (child instanceof Branch) {
|
|
127
|
+
branch = child;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (child.present || !sameFrom(child.tuple, tuple, depth + 1)) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
if (depth === tuple.length) {
|
|
136
|
+
branch.here = void 0;
|
|
137
|
+
} else {
|
|
138
|
+
branch.next.delete(tuple[depth]);
|
|
78
139
|
}
|
|
79
|
-
for (let
|
|
80
|
-
const
|
|
81
|
-
if (
|
|
140
|
+
for (let at = path.length - 1; at > 0; at--) {
|
|
141
|
+
const sole = soleKey(path[at]);
|
|
142
|
+
if (sole === void 0) {
|
|
82
143
|
return;
|
|
83
144
|
}
|
|
84
|
-
path[
|
|
145
|
+
path[at - 1].next.set(tuple[at - 1], sole);
|
|
85
146
|
}
|
|
86
147
|
}
|
|
87
148
|
/** Drop every fact, and whatever was kept in order to find them. */
|
|
88
149
|
clear() {
|
|
89
|
-
this.root =
|
|
150
|
+
this.root = new Branch();
|
|
90
151
|
this.size = 0;
|
|
91
152
|
}
|
|
92
153
|
};
|
|
@@ -523,6 +584,24 @@ function formatProfile(profile) {
|
|
|
523
584
|
}
|
|
524
585
|
|
|
525
586
|
// src/relation.ts
|
|
587
|
+
var isLone = (bucket) => typeof bucket[0] !== "object";
|
|
588
|
+
var NO_TUPLES = [];
|
|
589
|
+
function tuplesIn(bucket) {
|
|
590
|
+
if (bucket === void 0) {
|
|
591
|
+
return NO_TUPLES;
|
|
592
|
+
}
|
|
593
|
+
return isLone(bucket) ? [bucket] : bucket;
|
|
594
|
+
}
|
|
595
|
+
function fileUnder(buckets, key, tuple) {
|
|
596
|
+
const bucket = buckets.get(key);
|
|
597
|
+
if (bucket === void 0) {
|
|
598
|
+
buckets.set(key, tuple);
|
|
599
|
+
} else if (isLone(bucket)) {
|
|
600
|
+
buckets.set(key, [bucket, tuple]);
|
|
601
|
+
} else {
|
|
602
|
+
bucket.push(tuple);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
526
605
|
var sameTuple = (a, b) => a.length === b.length && a.every((atom, at) => atom === b[at]);
|
|
527
606
|
function storedTuplesOf(relation, facts) {
|
|
528
607
|
const column = relation.columns.findIndex((buckets2) => buckets2 !== void 0);
|
|
@@ -531,19 +610,24 @@ function storedTuplesOf(relation, facts) {
|
|
|
531
610
|
return null;
|
|
532
611
|
}
|
|
533
612
|
const stored = facts.map(
|
|
534
|
-
(fact) => buckets.get(fact[column])
|
|
613
|
+
(fact) => tuplesIn(buckets.get(fact[column])).find(
|
|
614
|
+
(tuple) => sameTuple(tuple, fact)
|
|
615
|
+
)
|
|
535
616
|
);
|
|
536
617
|
return stored.every((tuple) => tuple !== void 0) ? stored : null;
|
|
537
618
|
}
|
|
538
619
|
function removeFrom(buckets, key, tuple) {
|
|
539
620
|
const bucket = buckets.get(key);
|
|
540
|
-
|
|
541
|
-
if (bucket === void 0 || at === -1) {
|
|
621
|
+
if (bucket === void 0) {
|
|
542
622
|
return;
|
|
543
623
|
}
|
|
544
|
-
bucket
|
|
545
|
-
if (bucket.length === 0) {
|
|
624
|
+
if (isLone(bucket)) {
|
|
546
625
|
buckets.delete(key);
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
bucket.splice(bucket.indexOf(tuple), 1);
|
|
629
|
+
if (bucket.length === 1) {
|
|
630
|
+
buckets.set(key, bucket[0]);
|
|
547
631
|
}
|
|
548
632
|
}
|
|
549
633
|
function removeTuples(relation, stored) {
|
|
@@ -595,7 +679,7 @@ function addToCombined(index, tuple) {
|
|
|
595
679
|
}
|
|
596
680
|
level = next;
|
|
597
681
|
}
|
|
598
|
-
|
|
682
|
+
fileUnder(
|
|
599
683
|
level,
|
|
600
684
|
tuple[index.columns[last]],
|
|
601
685
|
tuple
|
|
@@ -614,10 +698,10 @@ function combinedBucketIn(relation, mask, values) {
|
|
|
614
698
|
for (const value of values) {
|
|
615
699
|
level = level.get(value);
|
|
616
700
|
if (level === void 0) {
|
|
617
|
-
return
|
|
701
|
+
return NO_TUPLES;
|
|
618
702
|
}
|
|
619
703
|
}
|
|
620
|
-
return level;
|
|
704
|
+
return tuplesIn(level);
|
|
621
705
|
}
|
|
622
706
|
function addToBucket(buckets, key, tuple) {
|
|
623
707
|
const bucket = buckets.get(key);
|
|
@@ -634,12 +718,12 @@ function bucketIn(relation, column, value) {
|
|
|
634
718
|
for (const tuple of relation.tuples) {
|
|
635
719
|
const at = tuple[column];
|
|
636
720
|
if (at !== void 0) {
|
|
637
|
-
|
|
721
|
+
fileUnder(buckets, at, tuple);
|
|
638
722
|
}
|
|
639
723
|
}
|
|
640
724
|
relation.columns[column] = buckets;
|
|
641
725
|
}
|
|
642
|
-
return buckets.get(value)
|
|
726
|
+
return tuplesIn(buckets.get(value));
|
|
643
727
|
}
|
|
644
728
|
|
|
645
729
|
// src/stratum.ts
|
|
@@ -905,7 +989,7 @@ var Database = class {
|
|
|
905
989
|
const buckets = relation.columns[column];
|
|
906
990
|
const value = tuple[column];
|
|
907
991
|
if (buckets !== void 0 && value !== void 0) {
|
|
908
|
-
|
|
992
|
+
fileUnder(buckets, value, tuple);
|
|
909
993
|
}
|
|
910
994
|
}
|
|
911
995
|
for (const combined of relation.combined.values()) {
|
package/package.json
CHANGED