@agentshouse/mdruntime 0.1.0-cefbc8bb8a1c0 → 0.1.0-cf1e7fdb9c600
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/README.md +1 -1
- package/dist/adapter.d.ts +69 -49
- package/dist/adapter.d.ts.map +1 -1
- package/dist/bootstrap.d.ts +5 -8
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +20 -21
- package/dist/chain.d.ts +2 -1
- package/dist/chain.d.ts.map +1 -1
- package/dist/client.d.ts +6 -5
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +0 -1
- package/dist/collection.d.ts +2 -4
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +1 -17
- package/dist/compile.d.ts +1 -6
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +97 -281
- package/dist/connection.d.ts +5 -7
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +816 -846
- package/dist/context.d.ts +2 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +7 -0
- package/dist/conversation.d.ts +15 -13
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +133 -142
- package/dist/durability.d.ts +0 -1
- package/dist/durability.d.ts.map +1 -1
- package/dist/entry.d.ts +2 -2
- package/dist/entry.d.ts.map +1 -1
- package/dist/entry.js +14 -5
- package/dist/host.d.ts +3 -7
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +12 -26
- package/dist/index.d.ts +9 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/job.d.ts +14 -0
- package/dist/job.d.ts.map +1 -0
- package/dist/job.js +28 -0
- package/dist/live.d.ts +2 -1
- package/dist/live.d.ts.map +1 -1
- package/dist/live.js +10 -7
- package/dist/memory.d.ts +9 -4
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +132 -81
- package/dist/messages.d.ts +2 -2
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +15 -16
- package/dist/model.d.ts +1 -3
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +0 -12
- package/dist/observation.d.ts +2 -2
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +7 -2
- package/dist/outcome.d.ts +0 -3
- package/dist/outcome.d.ts.map +1 -1
- package/dist/query.d.ts +5 -209
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +3 -505
- package/dist/reach.d.ts +7 -7
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +31 -44
- package/dist/react.d.ts +3 -1
- package/dist/react.d.ts.map +1 -1
- package/dist/readiness.d.ts +2 -15
- package/dist/readiness.d.ts.map +1 -1
- package/dist/readiness.js +4 -10
- package/dist/refresh.d.ts +7 -5
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +46 -6
- package/dist/replica.d.ts +2 -2
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +5 -5
- package/dist/room-draft.d.ts +2 -1
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +65 -52
- package/dist/rows.d.ts +3 -19
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +36 -164
- package/dist/search.d.ts +12 -63
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +9 -52
- package/dist/shared.d.ts +0 -1
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +0 -10
- package/dist/status.d.ts +0 -4
- package/dist/status.d.ts.map +1 -1
- package/dist/store.d.ts +15 -19
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +40 -39
- package/dist/stored.d.ts +33 -6
- package/dist/stored.d.ts.map +1 -1
- package/dist/stored.js +87 -124
- package/dist/tables.d.ts +34 -33
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js +21 -54
- package/dist/transfer.d.ts +0 -5
- package/dist/transfer.d.ts.map +1 -1
- package/dist/validation.d.ts +3 -4
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +39 -97
- package/dist/windows.d.ts +1 -12
- package/dist/windows.d.ts.map +1 -1
- package/dist/windows.js +2 -75
- package/dist/wire.d.ts +1 -4
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +1 -1
- package/package.json +2 -2
- package/dist/fulltext.d.ts +0 -27
- package/dist/fulltext.d.ts.map +0 -1
- package/dist/fulltext.js +0 -370
- package/dist/reads.d.ts +0 -11
- package/dist/reads.d.ts.map +0 -1
- package/dist/reads.js +0 -91
- package/dist/words.d.ts +0 -19
- package/dist/words.d.ts.map +0 -1
- package/dist/words.js +0 -156
package/dist/collection.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { parse, VERSION } from '@agentshouse/mdmodel';
|
|
2
2
|
import { beneath, normalize } from './paths.js';
|
|
3
3
|
import { hashed } from './observation.js';
|
|
4
|
-
import {
|
|
5
|
-
import { ROW_WEIGHT } from './turn.js';
|
|
4
|
+
import { sortedKeys } from './shared.js';
|
|
6
5
|
export const MD_MODEL = { identity: '@agentshouse/mdmodel', version: VERSION };
|
|
7
6
|
const typesPrinted = new WeakMap();
|
|
8
7
|
function typesPrint(model) {
|
|
@@ -61,21 +60,6 @@ export function* documentsOf(collection) {
|
|
|
61
60
|
}
|
|
62
61
|
})();
|
|
63
62
|
}
|
|
64
|
-
export function* unreadable(text, paths = null) {
|
|
65
|
-
const named = paths ?? (yield* keysIn(text));
|
|
66
|
-
let least = null;
|
|
67
|
-
for (const path of named) {
|
|
68
|
-
const content = Object.hasOwn(text, path) ? text[path] : undefined;
|
|
69
|
-
if (content === undefined)
|
|
70
|
-
continue;
|
|
71
|
-
const read = parse(content, path);
|
|
72
|
-
if (!read.ok && (least === null || path < least.path)) {
|
|
73
|
-
least = { path, refused: { kind: 'refused', refusal: read.refusal } };
|
|
74
|
-
}
|
|
75
|
-
yield ROW_WEIGHT + content.length;
|
|
76
|
-
}
|
|
77
|
-
return least?.refused ?? null;
|
|
78
|
-
}
|
|
79
63
|
export function validRoot(root) {
|
|
80
64
|
return typeof root === 'string' && normalize(root) === root;
|
|
81
65
|
}
|
package/dist/compile.d.ts
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import type { Plan, QueryRows } from './query.js';
|
|
2
|
-
import type { Connection
|
|
2
|
+
import type { Connection } from './tables.js';
|
|
3
3
|
export type QueryRoom = {
|
|
4
4
|
readonly locator: string;
|
|
5
5
|
readonly key: number;
|
|
6
6
|
};
|
|
7
|
-
export type Fragment = {
|
|
8
|
-
readonly sql: string;
|
|
9
|
-
readonly bind: readonly Value[];
|
|
10
|
-
};
|
|
11
7
|
export type QueryAnswer = QueryRows & {
|
|
12
8
|
readonly more?: string;
|
|
13
9
|
};
|
|
14
|
-
export declare function selected(connection: Connection, plan: Plan, rooms: readonly QueryRoom[]): Fragment;
|
|
15
10
|
export declare function answerQuery(connection: Connection, plan: Plan, rooms: readonly QueryRoom[], budget?: number | null): QueryAnswer;
|
|
16
11
|
//# sourceMappingURL=compile.d.ts.map
|
package/dist/compile.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../src/compile.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../src/compile.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,IAAI,EAAuC,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvF,OAAO,KAAK,EAAE,UAAU,EAAc,MAAM,aAAa,CAAC;AAG1D,MAAM,MAAM,SAAS,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAkb3E,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG;IAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAkUjE,wBAAgB,WAAW,CACzB,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,MAAM,GAAE,MAAM,GAAG,IAAW,GAC3B,WAAW,CAUb"}
|
package/dist/compile.js
CHANGED
|
@@ -1,125 +1,36 @@
|
|
|
1
|
-
import { ARRAY_INDEX, parse, pointerSegments } from '@agentshouse/mdmodel';
|
|
1
|
+
import { ARRAY_INDEX, bodied, byCodePoint, compared, cursorOf, heldKind, parse, pointerSegments, sql, } from '@agentshouse/mdmodel';
|
|
2
2
|
import { binaryRecord } from './binary.js';
|
|
3
|
-
import { documentKey,
|
|
4
|
-
import {
|
|
3
|
+
import { documentKey, pathOfKey, roomOfKey } from './rows.js';
|
|
4
|
+
import { FROM_START, MAXIMUM_WINDOW } from './windows.js';
|
|
5
|
+
const { all, any, exists, fragment, grouped, joined, listed } = sql;
|
|
5
6
|
const VISIBLE = (alias) => `${alias}.visible = 1 AND ${alias}.present = 1 AND ${alias}.queried = 1`;
|
|
6
|
-
function
|
|
7
|
-
return
|
|
7
|
+
function located(alias) {
|
|
8
|
+
return fragment(`(SELECT k.locator FROM replica_keys k WHERE k.key = ${alias}.key)`);
|
|
8
9
|
}
|
|
9
|
-
function
|
|
10
|
-
return {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
function balanced(parts, separator) {
|
|
16
|
-
if (parts.length === 1)
|
|
17
|
-
return grouped(parts[0]);
|
|
18
|
-
const half = Math.ceil(parts.length / 2);
|
|
19
|
-
return grouped(joined([balanced(parts.slice(0, half), separator), balanced(parts.slice(half), separator)], separator));
|
|
20
|
-
}
|
|
21
|
-
function all(parts) {
|
|
22
|
-
return parts.length === 0 ? fragment('1') : balanced(parts, ' AND ');
|
|
23
|
-
}
|
|
24
|
-
function any(parts) {
|
|
25
|
-
return parts.length === 0 ? fragment('0') : balanced(parts, ' OR ');
|
|
26
|
-
}
|
|
27
|
-
function grouped(part) {
|
|
28
|
-
return { sql: `(${part.sql})`, bind: part.bind };
|
|
29
|
-
}
|
|
30
|
-
function list(values) {
|
|
31
|
-
return fragment(`(${values.map(() => '?').join(', ')})`, values);
|
|
32
|
-
}
|
|
33
|
-
function sqlValue(value) {
|
|
34
|
-
if (value === null)
|
|
35
|
-
return { kind: 'null', value: null };
|
|
36
|
-
if (typeof value === 'boolean')
|
|
37
|
-
return { kind: 'boolean', value: value ? 1 : 0 };
|
|
38
|
-
if (typeof value === 'number')
|
|
39
|
-
return { kind: 'number', value };
|
|
40
|
-
return { kind: 'string', value };
|
|
41
|
-
}
|
|
42
|
-
function same(alias, value) {
|
|
43
|
-
const held = sqlValue(value);
|
|
44
|
-
return held.kind === 'null'
|
|
45
|
-
? fragment(`${alias}.kind = 'null'`)
|
|
46
|
-
: fragment(`${alias}.kind = ? AND ${alias}.value = ?`, [held.kind, held.value]);
|
|
47
|
-
}
|
|
48
|
-
const BOUNDS = { gt: '>', gte: '>=', lt: '<', lte: '<=' };
|
|
49
|
-
function pointed(alias, field, reach) {
|
|
50
|
-
const { keys, places, glob } = placedOf(field, reach);
|
|
51
|
-
return fragment(`${alias}.keys = ? AND ${alias}.places ${glob ? 'GLOB' : '='} ?`, [keys, places]);
|
|
52
|
-
}
|
|
53
|
-
function heldValues(doc, field, alias, where) {
|
|
54
|
-
return joined([
|
|
55
|
-
fragment(`SELECT 1 FROM replica_values ${alias} WHERE ${alias}.doc = ${doc}.id AND `),
|
|
56
|
-
grouped(pointed(alias, field, 'value')),
|
|
57
|
-
fragment(' AND '),
|
|
58
|
-
grouped(where),
|
|
59
|
-
], '');
|
|
60
|
-
}
|
|
61
|
-
function exists(part) {
|
|
62
|
-
return { sql: `EXISTS (${part.sql})`, bind: part.bind };
|
|
63
|
-
}
|
|
64
|
-
function itemOf(held, where) {
|
|
65
|
-
return exists(joined([
|
|
66
|
-
fragment(`SELECT 1 FROM replica_values i WHERE i.doc = ${held}.doc AND i.keys = ${held}.keys AND i.places = ${held}.places AND i.item >= 0 AND `),
|
|
67
|
-
grouped(where),
|
|
68
|
-
], ''));
|
|
69
|
-
}
|
|
70
|
-
function matching(held, values) {
|
|
71
|
-
return any(values.map((value) => any([
|
|
72
|
-
same(held, value),
|
|
73
|
-
all([fragment(`${held}.kind = 'array'`), itemOf(held, same('i', value))]),
|
|
74
|
-
])));
|
|
75
|
-
}
|
|
76
|
-
function tested(doc, field, test) {
|
|
77
|
-
const reached = exists(heldValues(doc, field, 'r', fragment('r.item = -1')));
|
|
78
|
-
if (test.op === 'exists')
|
|
79
|
-
return test.exists ? reached : fragment(`NOT ${reached.sql}`, reached.bind);
|
|
80
|
-
if (test.op === 'eq' || test.op === 'in') {
|
|
81
|
-
const values = test.op === 'eq' ? [test.value] : test.values;
|
|
82
|
-
return exists(heldValues(doc, field, 'v', any(values.map((value) => same('v', value)))));
|
|
83
|
-
}
|
|
84
|
-
if (test.op === 'ne' || test.op === 'nin') {
|
|
85
|
-
const values = test.op === 'ne' ? [test.value] : test.values;
|
|
86
|
-
const unmatched = matching('h', values);
|
|
87
|
-
return any([
|
|
88
|
-
fragment(`NOT ${reached.sql}`, reached.bind),
|
|
89
|
-
exists(heldValues(doc, field, 'h', all([fragment('h.item = -1'), fragment(`NOT (${unmatched.sql})`, unmatched.bind)]))),
|
|
90
|
-
]);
|
|
91
|
-
}
|
|
92
|
-
if (test.op === 'range') {
|
|
93
|
-
return exists(heldValues(doc, field, 'v', all([
|
|
94
|
-
fragment('v.item = -1'),
|
|
95
|
-
...test.bounds.map(([operator, bound]) => {
|
|
96
|
-
const held = sqlValue(bound);
|
|
97
|
-
return fragment(`v.kind = ? AND v.value ${BOUNDS[operator]} ?`, [
|
|
98
|
-
held.kind,
|
|
99
|
-
held.value,
|
|
100
|
-
]);
|
|
101
|
-
}),
|
|
102
|
-
])));
|
|
103
|
-
}
|
|
104
|
-
if (test.op === 'all') {
|
|
105
|
-
return exists(heldValues(doc, field, 'h', all([
|
|
106
|
-
fragment("h.item = -1 AND h.kind = 'array'"),
|
|
107
|
-
...test.values.map((value) => itemOf('h', same('i', value))),
|
|
108
|
-
])));
|
|
109
|
-
}
|
|
110
|
-
return exists(heldValues(doc, field, 'v', fragment("v.item = -1 AND v.kind = 'array' AND v.size = ?", [test.size])));
|
|
10
|
+
function within(alias, path) {
|
|
11
|
+
return fragment(`(${alias}.path = ? OR (${alias}.path >= ? AND ${alias}.path < ?))`, [
|
|
12
|
+
path,
|
|
13
|
+
`${path}/`,
|
|
14
|
+
`${path}0`,
|
|
15
|
+
]);
|
|
111
16
|
}
|
|
112
|
-
function
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return
|
|
117
|
-
|
|
17
|
+
function ancestors(area) {
|
|
18
|
+
const depth = area.path.split('/').length;
|
|
19
|
+
return area.open.flatMap((open) => {
|
|
20
|
+
const segments = open.split('/');
|
|
21
|
+
return segments.slice(depth - 1, -1).map((_, at) => segments.slice(0, depth + at).join('/'));
|
|
22
|
+
});
|
|
118
23
|
}
|
|
119
|
-
function
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
24
|
+
function fenced(alias, key, area, folders) {
|
|
25
|
+
const opened = any([
|
|
26
|
+
...area.open.map((open) => within(alias, open)),
|
|
27
|
+
...(folders ? ancestors(area).map((path) => fragment(`${alias}.path = ?`, [path])) : []),
|
|
28
|
+
]);
|
|
29
|
+
return all([
|
|
30
|
+
fragment(`${alias}.key = ?`, [key]),
|
|
31
|
+
within(alias, area.path),
|
|
32
|
+
fragment(`NOT ${grouped(opened).sql}`, opened.bind),
|
|
33
|
+
]);
|
|
123
34
|
}
|
|
124
35
|
function reached(bound, alias) {
|
|
125
36
|
const reach = bound.reach;
|
|
@@ -133,44 +44,20 @@ function reached(bound, alias) {
|
|
|
133
44
|
const key = bound.keys.get(roomOfKey(held));
|
|
134
45
|
return key === undefined ? [] : [[key, pathOfKey(held)]];
|
|
135
46
|
});
|
|
136
|
-
const
|
|
47
|
+
const outside = any(reach.areas.flatMap((area) => {
|
|
137
48
|
const key = bound.keys.get(area.room);
|
|
138
|
-
return key === undefined ? [] : [
|
|
139
|
-
});
|
|
140
|
-
const beneath = (area) => fragment(`${alias}.key = ? AND (${alias}.path = ? OR (${alias}.path >= ? AND ${alias}.path < ?))`, [area.key, area.path, `${area.path}/`, `${area.path}0`]);
|
|
141
|
-
const typed = (types) => fragment(`${alias}.type IN ${list(types).sql}`, types);
|
|
142
|
-
const outside = any(areas.map((area) => area.types.length === 0
|
|
143
|
-
? beneath(area)
|
|
144
|
-
: all([beneath(area), fragment(`NOT ${grouped(typed(area.types)).sql}`, area.types)])));
|
|
49
|
+
return key === undefined ? [] : [fenced(alias, key, area, false)];
|
|
50
|
+
}));
|
|
145
51
|
return all([
|
|
146
52
|
any([
|
|
147
|
-
...(rooms.length === 0 ? [] : [fragment(`${alias}.key IN ${
|
|
53
|
+
...(rooms.length === 0 ? [] : [fragment(`${alias}.key IN ${listed(rooms).sql}`, rooms)]),
|
|
148
54
|
...(documents.length === 0
|
|
149
55
|
? []
|
|
150
56
|
: [
|
|
151
57
|
fragment(`EXISTS (SELECT 1 FROM json_each(?) j WHERE j.value ->> 0 = ${alias}.key AND j.value ->> 1 = ${alias}.path)`, [JSON.stringify(documents)]),
|
|
152
58
|
]),
|
|
153
|
-
...areas.flatMap((area) => area.types.length === 0 ? [] : [all([beneath(area), typed(area.types)])]),
|
|
154
59
|
]),
|
|
155
|
-
...(areas.length === 0 ? [] : [fragment(`NOT ${grouped(outside).sql}`, outside.bind)]),
|
|
156
|
-
]);
|
|
157
|
-
}
|
|
158
|
-
function unindexed(column) {
|
|
159
|
-
return `+${column}`;
|
|
160
|
-
}
|
|
161
|
-
function scoped(alias, scope, byPaths = false) {
|
|
162
|
-
const type = byPaths ? unindexed(`${alias}.type`) : `${alias}.type`;
|
|
163
|
-
return all([
|
|
164
|
-
...(scope.types === null ? [] : [fragment(`${type} IN ${list(scope.types).sql}`, scope.types)]),
|
|
165
|
-
...(scope.pathPrefix === null || scope.pathPrefix === ''
|
|
166
|
-
? []
|
|
167
|
-
: [
|
|
168
|
-
fragment(`${alias}.path >= ? AND ${alias}.path < ?`, [
|
|
169
|
-
`${scope.pathPrefix}/`,
|
|
170
|
-
`${scope.pathPrefix}0`,
|
|
171
|
-
]),
|
|
172
|
-
]),
|
|
173
|
-
...(scope.where === null ? [] : [conditioned(alias, scope.where)]),
|
|
60
|
+
...(reach.areas.length === 0 ? [] : [fragment(`NOT ${grouped(outside).sql}`, outside.bind)]),
|
|
174
61
|
]);
|
|
175
62
|
}
|
|
176
63
|
function related(bound, plan) {
|
|
@@ -178,17 +65,17 @@ function related(bound, plan) {
|
|
|
178
65
|
if (plan.hop !== null) {
|
|
179
66
|
parts.push(exists(joined([
|
|
180
67
|
fragment(`SELECT 1 FROM replica_links l JOIN replica_entries m ON m.key = d.key AND m.path = l.target AND ${VISIBLE('m')} WHERE l.doc = d.id AND l.kind = 'field' AND`),
|
|
181
|
-
grouped(
|
|
68
|
+
grouped(sql.placed('l', plan.hop.field, 'link')),
|
|
182
69
|
fragment('AND'),
|
|
183
|
-
grouped(all([scoped('m', plan.hop), reached(bound, 'm')])),
|
|
70
|
+
grouped(all([sql.scoped('m', plan.hop), reached(bound, 'm')])),
|
|
184
71
|
], ' ')));
|
|
185
72
|
}
|
|
186
73
|
if (plan.backlinks !== null) {
|
|
187
74
|
const field = plan.backlinks.field;
|
|
188
75
|
parts.push(exists(joined([
|
|
189
76
|
fragment(`SELECT 1 FROM replica_links l JOIN replica_entries s ON s.id = l.doc AND ${VISIBLE('s')} WHERE l.key = d.key AND l.target = d.path AND l.kind = 'field' AND`),
|
|
190
|
-
...(field === null ? [] : [grouped(
|
|
191
|
-
grouped(all([scoped('s', plan.backlinks), reached(bound, 's')])),
|
|
77
|
+
...(field === null ? [] : [grouped(sql.placed('l', field, 'link')), fragment('AND')]),
|
|
78
|
+
grouped(all([sql.scoped('s', plan.backlinks), reached(bound, 's')])),
|
|
192
79
|
], ' ')));
|
|
193
80
|
}
|
|
194
81
|
return parts;
|
|
@@ -196,11 +83,9 @@ function related(bound, plan) {
|
|
|
196
83
|
function filtered(bound, plan) {
|
|
197
84
|
const keys = bound.rooms.map(({ key }) => key);
|
|
198
85
|
return all([
|
|
199
|
-
fragment(`d.key IN ${
|
|
200
|
-
...(plan.paths === null ? [] : [fragment(`d.path IN ${list(plan.paths).sql}`, plan.paths)]),
|
|
201
|
-
...(plan.folder === null ? [] : [fragment('d.folder = ?', [plan.folder])]),
|
|
86
|
+
fragment(`d.key IN ${listed(keys).sql} AND ${VISIBLE('d')}`, keys),
|
|
202
87
|
reached(bound, 'd'),
|
|
203
|
-
|
|
88
|
+
sql.tests('d', plan),
|
|
204
89
|
...related(bound, plan),
|
|
205
90
|
]);
|
|
206
91
|
}
|
|
@@ -231,103 +116,25 @@ const GROUP_TIES = {
|
|
|
231
116
|
locale: undefined,
|
|
232
117
|
localeOptions: undefined,
|
|
233
118
|
};
|
|
234
|
-
function
|
|
235
|
-
if (value === null || value === undefined) {
|
|
236
|
-
return { placement: ordering.nulls === 'first' ? 0 : 2, klass: 0, scalar: null };
|
|
237
|
-
}
|
|
238
|
-
if (typeof value === 'boolean')
|
|
239
|
-
return { placement: 1, klass: 1, scalar: value ? 1 : 0 };
|
|
240
|
-
if (typeof value === 'number')
|
|
241
|
-
return { placement: 1, klass: 1, scalar: value };
|
|
242
|
-
if (typeof value === 'string')
|
|
243
|
-
return { placement: 1, klass: 2, scalar: value };
|
|
244
|
-
return { placement: 1, klass: 3, scalar: JSON.stringify(value) };
|
|
245
|
-
}
|
|
246
|
-
function placement(term) {
|
|
247
|
-
const nulls = term.ordering.nulls === 'first' ? 0 : 2;
|
|
248
|
-
return fragment(`CASE WHEN ${term.kind.sql} IS NULL OR ${term.kind.sql} = 'null' THEN ${nulls} ELSE 1 END`, [...term.kind.bind, ...term.kind.bind]);
|
|
249
|
-
}
|
|
250
|
-
function klass(term) {
|
|
251
|
-
return fragment(`CASE ${term.kind.sql} WHEN 'boolean' THEN 1 WHEN 'number' THEN 1 WHEN 'string' THEN 2 WHEN 'array' THEN 3 ELSE 4 END`, term.kind.bind);
|
|
252
|
-
}
|
|
253
|
-
function classed(term, which) {
|
|
254
|
-
const kinds = which === 1 ? "('boolean', 'number')" : which === 2 ? "('string')" : "('array')";
|
|
255
|
-
return fragment(`(CASE WHEN ${term.kind.sql} IN ${kinds} THEN ${term.value.sql} END)`, [
|
|
256
|
-
...term.kind.bind,
|
|
257
|
-
...term.value.bind,
|
|
258
|
-
]);
|
|
259
|
-
}
|
|
260
|
-
function collated(bound, term, which) {
|
|
261
|
-
if (which === 1)
|
|
262
|
-
return '';
|
|
263
|
-
const name = bound.collate(term.ordering, which === 3, term.total === true);
|
|
264
|
-
return name === null ? '' : ` COLLATE ${name}`;
|
|
265
|
-
}
|
|
266
|
-
function sorted(bound, term) {
|
|
267
|
-
const direction = term.ordering.direction === 'desc' ? 'DESC' : 'ASC';
|
|
268
|
-
return joined([
|
|
269
|
-
placement(term),
|
|
270
|
-
fragment(`${klass(term).sql} ${direction}`, klass(term).bind),
|
|
271
|
-
...[1, 2, 3].map((which) => {
|
|
272
|
-
const value = classed(term, which);
|
|
273
|
-
return fragment(`${value.sql}${collated(bound, term, which)} ${direction}`, value.bind);
|
|
274
|
-
}),
|
|
275
|
-
], ', ');
|
|
276
|
-
}
|
|
277
|
-
function beyond(bound, term, value) {
|
|
278
|
-
const held = classOf(value, term.ordering);
|
|
279
|
-
const place = placement(term);
|
|
280
|
-
if (held.placement !== 1) {
|
|
281
|
-
return {
|
|
282
|
-
beyond: fragment(`${place.sql} > ${held.placement}`, place.bind),
|
|
283
|
-
equal: fragment(`${place.sql} = ${held.placement}`, place.bind),
|
|
284
|
-
};
|
|
285
|
-
}
|
|
286
|
-
const after = term.ordering.direction === 'desc' ? '<' : '>';
|
|
287
|
-
const kind = klass(term);
|
|
288
|
-
const scalar = classed(term, held.klass);
|
|
289
|
-
const collation = collated(bound, term, held.klass);
|
|
290
|
-
return {
|
|
291
|
-
beyond: any([
|
|
292
|
-
fragment(`${place.sql} > 1`, place.bind),
|
|
293
|
-
all([
|
|
294
|
-
fragment(`${place.sql} = 1`, place.bind),
|
|
295
|
-
any([
|
|
296
|
-
fragment(`${kind.sql} ${after} ${held.klass}`, kind.bind),
|
|
297
|
-
all([
|
|
298
|
-
fragment(`${kind.sql} = ${held.klass}`, kind.bind),
|
|
299
|
-
fragment(`${scalar.sql} ${after} ?${collation}`, [...scalar.bind, held.scalar]),
|
|
300
|
-
]),
|
|
301
|
-
]),
|
|
302
|
-
]),
|
|
303
|
-
]),
|
|
304
|
-
equal: all([
|
|
305
|
-
fragment(`${place.sql} = 1 AND ${kind.sql} = ${held.klass}`, [...place.bind, ...kind.bind]),
|
|
306
|
-
fragment(`${scalar.sql} = ?${collation}`, [...scalar.bind, held.scalar]),
|
|
307
|
-
]),
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
function following(parts, tie) {
|
|
311
|
-
let condition = tie;
|
|
312
|
-
for (const part of [...parts].reverse()) {
|
|
313
|
-
condition = any([part.beyond, all([part.equal, condition])]);
|
|
314
|
-
}
|
|
315
|
-
return condition;
|
|
316
|
-
}
|
|
317
|
-
function keyTerm(bound, key, ordering, index) {
|
|
119
|
+
function keyTerm(key, ordering, index) {
|
|
318
120
|
if (key.of === 'field') {
|
|
319
121
|
const alias = `o${index}`;
|
|
320
122
|
return {
|
|
321
|
-
term: {
|
|
123
|
+
term: {
|
|
124
|
+
ordering,
|
|
125
|
+
kind: fragment(`${alias}.kind`),
|
|
126
|
+
value: fragment(`${alias}.value`),
|
|
127
|
+
instant: fragment(`${alias}.instant`),
|
|
128
|
+
},
|
|
322
129
|
join: joined([
|
|
323
130
|
fragment(`LEFT JOIN replica_values ${alias} ON ${alias}.doc = d.id AND `),
|
|
324
|
-
grouped(
|
|
131
|
+
grouped(sql.placed(alias, key.field, 'exact')),
|
|
325
132
|
fragment(` AND ${alias}.item = -1 AND ${alias}.kind NOT IN ('object', 'undefined')`),
|
|
326
133
|
], ''),
|
|
327
134
|
};
|
|
328
135
|
}
|
|
329
136
|
const expression = key.of === 'room'
|
|
330
|
-
? located(
|
|
137
|
+
? located('d')
|
|
331
138
|
: key.of === 'path'
|
|
332
139
|
? fragment('d.path')
|
|
333
140
|
: key.of === 'type'
|
|
@@ -353,13 +160,20 @@ function decoded(kind, value) {
|
|
|
353
160
|
return JSON.parse(value);
|
|
354
161
|
return null;
|
|
355
162
|
}
|
|
163
|
+
function sortedValue(row, index) {
|
|
164
|
+
const kind = row[`k${index}`];
|
|
165
|
+
return sql.sortValue(kind, decoded(kind, row[`v${index}`]), row[`i${index}`]);
|
|
166
|
+
}
|
|
356
167
|
function decodedKey(key, row, index) {
|
|
357
168
|
if (key.of === 'field') {
|
|
358
169
|
const kind = row[`k${index}`];
|
|
359
|
-
return kind === null || kind === undefined ? undefined :
|
|
170
|
+
return kind === null || kind === undefined ? undefined : sortedValue(row, index);
|
|
360
171
|
}
|
|
361
172
|
return row[`v${index}`] ?? null;
|
|
362
173
|
}
|
|
174
|
+
function instantOf(term) {
|
|
175
|
+
return term.instant ?? fragment('NULL');
|
|
176
|
+
}
|
|
363
177
|
const registered = new WeakMap();
|
|
364
178
|
function hashed(text) {
|
|
365
179
|
let hash = 0x811c9dc5;
|
|
@@ -409,9 +223,6 @@ function collating(connection) {
|
|
|
409
223
|
return name;
|
|
410
224
|
};
|
|
411
225
|
}
|
|
412
|
-
function bodied(shape) {
|
|
413
|
-
return shape.sections !== null || shape.preamble || shape.markdown;
|
|
414
|
-
}
|
|
415
226
|
function masked(pointers) {
|
|
416
227
|
const root = { whole: false, members: new Map() };
|
|
417
228
|
for (const pointer of pointers) {
|
|
@@ -468,9 +279,10 @@ function documentOf(shape, room, row, linked) {
|
|
|
468
279
|
const type = row.type;
|
|
469
280
|
const fileId = row.file_id ?? null;
|
|
470
281
|
const revision = row.revision ?? null;
|
|
282
|
+
const content = row.content ?? null;
|
|
283
|
+
const unheld = Number(row.unheld) === 1 ? { textHeld: false } : {};
|
|
471
284
|
if (type === null) {
|
|
472
285
|
const binary = row.binary === null ? null : JSON.parse(row.binary);
|
|
473
|
-
const content = row.content ?? null;
|
|
474
286
|
return {
|
|
475
287
|
room,
|
|
476
288
|
path,
|
|
@@ -480,11 +292,12 @@ function documentOf(shape, room, row, linked) {
|
|
|
480
292
|
binaryFile: binaryRecord(path, binary, revision ?? undefined),
|
|
481
293
|
}),
|
|
482
294
|
...(shape.markdown && content !== null ? { markdown: content } : {}),
|
|
295
|
+
...unheld,
|
|
483
296
|
...(shape.fileId ? { fileId } : {}),
|
|
484
297
|
...(shape.revision ? { revision } : {}),
|
|
485
298
|
};
|
|
486
299
|
}
|
|
487
|
-
const read = bodied(shape) ? parse(
|
|
300
|
+
const read = bodied(shape) && content !== null ? parse(content, path) : null;
|
|
488
301
|
const body = read?.ok === true ? read.document : null;
|
|
489
302
|
const fields = JSON.parse(row.fields ?? '{}');
|
|
490
303
|
return {
|
|
@@ -502,7 +315,8 @@ function documentOf(shape, room, row, linked) {
|
|
|
502
315
|
? {}
|
|
503
316
|
: { sections: sectionsOf(body.sections, shape.sections) }),
|
|
504
317
|
...(shape.preamble && body !== null ? { preamble: body.preamble } : {}),
|
|
505
|
-
...(shape.markdown &&
|
|
318
|
+
...(shape.markdown && content !== null ? { markdown: content } : {}),
|
|
319
|
+
...unheld,
|
|
506
320
|
...(shape.backlinks ? { backlinks: linked.get(documentKey(room, path)) ?? [] } : {}),
|
|
507
321
|
...(shape.change
|
|
508
322
|
? {
|
|
@@ -536,10 +350,10 @@ function backlinksOf(connection, bound, rows) {
|
|
|
536
350
|
return linked;
|
|
537
351
|
}
|
|
538
352
|
function documents(connection, bound, plan, shape, budget) {
|
|
539
|
-
const terms = shape.orderBy.map(({ key, options }, index) => keyTerm(
|
|
353
|
+
const terms = shape.orderBy.map(({ key, options }, index) => keyTerm(key, orderingOf(options), index));
|
|
540
354
|
const filter = filtered(bound, plan);
|
|
541
355
|
const cursor = plan.after?.values ?? null;
|
|
542
|
-
const room = located(
|
|
356
|
+
const room = located('d');
|
|
543
357
|
const tie = cursor === null
|
|
544
358
|
? null
|
|
545
359
|
: (() => {
|
|
@@ -555,19 +369,20 @@ function documents(connection, bound, plan, shape, budget) {
|
|
|
555
369
|
})();
|
|
556
370
|
const after = cursor === null || tie === null
|
|
557
371
|
? null
|
|
558
|
-
: following(terms.map(({ term }, index) => beyond(bound, term, cursor[index])), tie);
|
|
372
|
+
: sql.following(terms.map(({ term }, index) => sql.beyond(bound.collate, term, cursor[index])), tie);
|
|
559
373
|
const selected = joined([
|
|
560
|
-
fragment('SELECT d.id, d.key, d.path, d.type, d.binary, d.revision, d.fields, d.change, d.file_id'),
|
|
374
|
+
fragment('SELECT d.id, d.key, d.path, d.type, d.binary, d.revision, d.fields, d.change, d.file_id, d.content IS NULL AND d.outline IS NOT NULL AS unheld'),
|
|
561
375
|
...terms.map(({ term }, index) => joined([
|
|
562
376
|
fragment(`, ${term.kind.sql} AS k${index}`, term.kind.bind),
|
|
563
377
|
fragment(`, ${term.value.sql} AS v${index}`, term.value.bind),
|
|
378
|
+
fragment(`, ${instantOf(term).sql} AS i${index}`, instantOf(term).bind),
|
|
564
379
|
], '')),
|
|
565
380
|
fragment(' FROM replica_entries d'),
|
|
566
381
|
...terms.flatMap(({ join }) => (join === null ? [] : [fragment(' '), join])),
|
|
567
382
|
fragment(' WHERE '),
|
|
568
383
|
after === null ? filter : all([filter, after]),
|
|
569
384
|
fragment(' ORDER BY '),
|
|
570
|
-
joined([...terms.map(({ term }) => sorted(bound, term)), room, fragment('d.path')], ', '),
|
|
385
|
+
joined([...terms.map(({ term }) => sql.sorted(bound.collate, term)), room, fragment('d.path')], ', '),
|
|
571
386
|
...(plan.limit === null ? [] : [fragment(' LIMIT ?', [plan.limit + 1])]),
|
|
572
387
|
], '');
|
|
573
388
|
const rows = connection.rows(selected.sql, selected.bind);
|
|
@@ -606,7 +421,7 @@ function withContent(connection, rows, budget) {
|
|
|
606
421
|
return rows;
|
|
607
422
|
const ids = rows.map((row) => row.id);
|
|
608
423
|
const sizes = new Map(connection
|
|
609
|
-
.rows(`SELECT id, octet_length(content) AS size FROM replica_entries WHERE id IN ${
|
|
424
|
+
.rows(`SELECT id, octet_length(content) AS size FROM replica_entries WHERE id IN ${listed(ids).sql}`, ids)
|
|
610
425
|
.map((row) => [row.id, Number(row.size ?? 0)]));
|
|
611
426
|
let cut = rows.length;
|
|
612
427
|
let spent = 0;
|
|
@@ -621,34 +436,30 @@ function withContent(connection, rows, budget) {
|
|
|
621
436
|
const kept = rows.slice(0, cut);
|
|
622
437
|
const read = kept.map((row) => row.id);
|
|
623
438
|
const held = new Map(connection
|
|
624
|
-
.rows(`SELECT id, content FROM replica_entries WHERE id IN ${
|
|
439
|
+
.rows(`SELECT id, content FROM replica_entries WHERE id IN ${listed(read).sql}`, read)
|
|
625
440
|
.map((row) => [row.id, row.content]));
|
|
626
441
|
return kept.map((row) => ({ ...row, content: held.get(row.id) ?? null }));
|
|
627
442
|
}
|
|
628
443
|
function storedFolders(bound, plan) {
|
|
629
|
-
if (plan.paths !== null || plan.
|
|
444
|
+
if (plan.paths !== null || plan.fileIds !== null)
|
|
445
|
+
return null;
|
|
446
|
+
if (plan.hop !== null || plan.backlinks !== null)
|
|
630
447
|
return null;
|
|
631
448
|
const whole = bound.rooms.filter(({ locator }) => bound.reach === null || bound.reach.rooms.includes(locator));
|
|
632
449
|
if (whole.length === 0)
|
|
633
450
|
return null;
|
|
634
451
|
const keys = whole.map(({ key }) => key);
|
|
635
452
|
const prefix = plan.root.pathPrefix;
|
|
636
|
-
const
|
|
453
|
+
const outside = (bound.reach?.areas ?? []).flatMap((area) => {
|
|
637
454
|
const key = bound.keys.get(area.room);
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
key,
|
|
643
|
-
area.path,
|
|
644
|
-
`${area.path}/`,
|
|
645
|
-
`${area.path}0`,
|
|
646
|
-
]),
|
|
647
|
-
];
|
|
455
|
+
if (key === undefined)
|
|
456
|
+
return [];
|
|
457
|
+
const fence = fenced('f', key, area, true);
|
|
458
|
+
return [fragment(`NOT ${grouped(fence).sql}`, fence.bind)];
|
|
648
459
|
});
|
|
649
460
|
return all([
|
|
650
|
-
fragment(`f.key IN ${
|
|
651
|
-
...
|
|
461
|
+
fragment(`f.key IN ${listed(keys).sql} AND f.layer = 0 AND f.present = 1`, keys),
|
|
462
|
+
...outside,
|
|
652
463
|
...(prefix === null || prefix === ''
|
|
653
464
|
? []
|
|
654
465
|
: [
|
|
@@ -663,7 +474,7 @@ function storedFolders(bound, plan) {
|
|
|
663
474
|
}
|
|
664
475
|
function groups(connection, bound, plan, shape) {
|
|
665
476
|
const keyed = shape.groupBy.map((key, index) => {
|
|
666
|
-
const { term, join } = keyTerm(
|
|
477
|
+
const { term, join } = keyTerm(key, GROUP_ORDER, index);
|
|
667
478
|
const kind = fragment(`COALESCE(${term.kind.sql}, 'null')`, term.kind.bind);
|
|
668
479
|
return { term: { ...term, kind }, join };
|
|
669
480
|
});
|
|
@@ -673,6 +484,7 @@ function groups(connection, bound, plan, shape) {
|
|
|
673
484
|
joined(keyed.map(({ term }, index) => joined([
|
|
674
485
|
fragment(`${term.kind.sql} AS k${index}`, term.kind.bind),
|
|
675
486
|
fragment(`, ${term.value.sql} AS v${index}`, term.value.bind),
|
|
487
|
+
fragment(`, ${instantOf(term).sql} AS i${index}`, instantOf(term).bind),
|
|
676
488
|
], '')), ', '),
|
|
677
489
|
fragment(', 1 AS n FROM replica_entries d'),
|
|
678
490
|
...keyed.flatMap(({ join }) => (join === null ? [] : [fragment(' '), join])),
|
|
@@ -686,10 +498,15 @@ function groups(connection, bound, plan, shape) {
|
|
|
686
498
|
? inner
|
|
687
499
|
: joined([
|
|
688
500
|
inner,
|
|
689
|
-
fragment(" UNION ALL SELECT 'string' AS k0, f.path AS v0, 0 AS n FROM replica_folders f WHERE "),
|
|
501
|
+
fragment(" UNION ALL SELECT 'string' AS k0, f.path AS v0, NULL AS i0, 0 AS n FROM replica_folders f WHERE "),
|
|
690
502
|
folders,
|
|
691
503
|
], '');
|
|
692
|
-
const columns = keyed
|
|
504
|
+
const columns = keyed
|
|
505
|
+
.map((_, index) => `k${index}, min(v${index}) AS v${index}, i${index}`)
|
|
506
|
+
.join(', ');
|
|
507
|
+
const held = keyed
|
|
508
|
+
.map((_, index) => `k${index}, i${index}, CASE WHEN i${index} IS NULL THEN v${index} END`)
|
|
509
|
+
.join(', ');
|
|
693
510
|
const counted = {
|
|
694
511
|
ordering: { ...GROUP_ORDER },
|
|
695
512
|
kind: fragment("'number'"),
|
|
@@ -702,7 +519,7 @@ function groups(connection, bound, plan, shape) {
|
|
|
702
519
|
});
|
|
703
520
|
const ordered = shape.orderBy.map(({ by, direction }) => by === 'count'
|
|
704
521
|
? { ...counted, ordering: { ...GROUP_ORDER, direction } }
|
|
705
|
-
: outer(by, { ...GROUP_ORDER, direction }));
|
|
522
|
+
: { ...outer(by, { ...GROUP_ORDER, direction }), instant: fragment(`i${by}`) });
|
|
706
523
|
const ties = keyed.flatMap((_, index) => [
|
|
707
524
|
{ ...outer(index, GROUP_TIES), total: true },
|
|
708
525
|
{ ordering: GROUP_TIES, kind: fragment("'string'"), value: fragment(`k${index}`) },
|
|
@@ -712,19 +529,21 @@ function groups(connection, bound, plan, shape) {
|
|
|
712
529
|
? null
|
|
713
530
|
: cursor[shape.orderBy.length].flatMap((value) => [
|
|
714
531
|
value,
|
|
715
|
-
|
|
532
|
+
heldKind(value ?? null),
|
|
716
533
|
]);
|
|
717
534
|
const after = cursor === null || tieValues === null
|
|
718
535
|
? null
|
|
719
|
-
: following(ordered.map((term, index) => beyond(bound, term, cursor[index])), following(ties
|
|
720
|
-
|
|
536
|
+
: sql.following(ordered.map((term, index) => sql.beyond(bound.collate, term, cursor[index])), sql.following(ties
|
|
537
|
+
.slice(0, -1)
|
|
538
|
+
.map((term, index) => sql.beyond(bound.collate, term, tieValues[index])), sql.beyond(bound.collate, ties.at(-1), tieValues.at(-1)).beyond));
|
|
539
|
+
const grouping = joined([fragment(`SELECT ${columns}, sum(n) AS n FROM (`), source, fragment(`) GROUP BY ${held}`)], '');
|
|
721
540
|
const windowed = joined([
|
|
722
541
|
fragment('SELECT * FROM ('),
|
|
723
542
|
grouping,
|
|
724
543
|
fragment(')'),
|
|
725
544
|
...(after === null ? [] : [fragment(' WHERE '), after]),
|
|
726
545
|
fragment(' ORDER BY '),
|
|
727
|
-
joined([...ordered, ...ties].map((term) => sorted(bound, term)), ', '),
|
|
546
|
+
joined([...ordered, ...ties].map((term) => sql.sorted(bound.collate, term)), ', '),
|
|
728
547
|
...(plan.limit === null ? [] : [fragment(' LIMIT ?', [plan.limit + 1])]),
|
|
729
548
|
], '');
|
|
730
549
|
const rows = connection.rows(windowed.sql, windowed.bind);
|
|
@@ -737,7 +556,7 @@ function groups(connection, bound, plan, shape) {
|
|
|
737
556
|
: last === undefined
|
|
738
557
|
? (plan.after?.text ?? FROM_START)
|
|
739
558
|
: cursorOf([
|
|
740
|
-
...shape.orderBy.map(({ by }) => by === 'count' ? Number(last.n) :
|
|
559
|
+
...shape.orderBy.map(({ by }) => by === 'count' ? Number(last.n) : sortedValue(last, by)),
|
|
741
560
|
keyOf(last),
|
|
742
561
|
]);
|
|
743
562
|
const total = plan.total
|
|
@@ -757,9 +576,6 @@ function boundOf(connection, plan, rooms) {
|
|
|
757
576
|
collate: collating(connection),
|
|
758
577
|
};
|
|
759
578
|
}
|
|
760
|
-
export function selected(connection, plan, rooms) {
|
|
761
|
-
return filtered(boundOf(connection, plan, rooms), plan);
|
|
762
|
-
}
|
|
763
579
|
export function answerQuery(connection, plan, rooms, budget = null) {
|
|
764
580
|
const bound = boundOf(connection, plan, rooms);
|
|
765
581
|
if (rooms.length === 0) {
|