@agentshouse/mdruntime 0.1.0-cae97ad2e9142 → 0.1.0-caf080a9a9c60
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 +14 -14
- package/dist/acceptance.d.ts +3 -0
- package/dist/acceptance.d.ts.map +1 -0
- package/dist/acceptance.js +318 -0
- package/dist/act-lanes.d.ts +29 -0
- package/dist/act-lanes.d.ts.map +1 -0
- package/dist/act-lanes.js +177 -0
- package/dist/actions.d.ts +25 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +261 -0
- package/dist/adapter.d.ts +126 -107
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attached-model.d.ts +3 -10
- package/dist/attached-model.d.ts.map +1 -1
- package/dist/attached-model.js +29 -47
- package/dist/bootstrap.d.ts +17 -19
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +266 -135
- package/dist/cell.d.ts +6 -0
- package/dist/cell.d.ts.map +1 -0
- package/dist/cell.js +9 -0
- package/dist/chain.d.ts +2 -1
- package/dist/chain.d.ts.map +1 -1
- package/dist/change-set.d.ts +4 -0
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +6 -0
- package/dist/change.d.ts +1 -0
- package/dist/change.d.ts.map +1 -1
- package/dist/change.js +4 -2
- package/dist/chat-types.d.ts +5 -0
- package/dist/chat-types.d.ts.map +1 -0
- package/dist/chat-types.js +13 -0
- package/dist/client.d.ts +28 -18
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +84 -20
- package/dist/collection.d.ts +28 -7
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +35 -19
- package/dist/compile.d.ts +8 -6
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +289 -332
- package/dist/connection.d.ts +6 -141
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +111 -5012
- package/dist/context.d.ts +2 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +7 -0
- package/dist/conversation-live.d.ts +138 -0
- package/dist/conversation-live.d.ts.map +1 -0
- package/dist/conversation-live.js +249 -0
- package/dist/conversation-row.d.ts +33 -0
- package/dist/conversation-row.d.ts.map +1 -0
- package/dist/conversation-row.js +53 -0
- package/dist/conversation.d.ts +59 -50
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +938 -832
- package/dist/declaration.d.ts +10 -0
- package/dist/declaration.d.ts.map +1 -0
- package/dist/declaration.js +24 -0
- package/dist/discovery.d.ts +10 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +153 -0
- package/dist/durability.d.ts +18 -1
- package/dist/durability.d.ts.map +1 -1
- package/dist/entry.d.ts +3 -2
- package/dist/entry.d.ts.map +1 -1
- package/dist/entry.js +19 -9
- package/dist/expectation.d.ts +26 -13
- package/dist/expectation.d.ts.map +1 -1
- package/dist/expectation.js +72 -19
- package/dist/failure.d.ts +3 -0
- package/dist/failure.d.ts.map +1 -1
- package/dist/failure.js +6 -0
- package/dist/figures.d.ts +49 -0
- package/dist/figures.d.ts.map +1 -0
- package/dist/figures.js +197 -0
- package/dist/file.d.ts +2 -11
- package/dist/file.d.ts.map +1 -1
- package/dist/file.js +6 -4
- package/dist/holdings.d.ts +29 -0
- package/dist/holdings.d.ts.map +1 -0
- package/dist/holdings.js +95 -0
- package/dist/host.d.ts +20 -8
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +70 -40
- package/dist/identity.d.ts +0 -1
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +0 -3
- package/dist/index.d.ts +33 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -3
- package/dist/install.d.ts +54 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +539 -0
- package/dist/lifecycle.d.ts +7 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +279 -0
- package/dist/listing.d.ts +48 -0
- package/dist/listing.d.ts.map +1 -0
- package/dist/listing.js +139 -0
- package/dist/live.d.ts +5 -4
- package/dist/live.d.ts.map +1 -1
- package/dist/live.js +64 -23
- package/dist/locks.d.ts +7 -0
- package/dist/locks.d.ts.map +1 -0
- package/dist/locks.js +33 -0
- package/dist/log.d.ts +1 -0
- package/dist/log.d.ts.map +1 -1
- package/dist/lost.d.ts +5 -0
- package/dist/lost.d.ts.map +1 -0
- package/dist/lost.js +50 -0
- package/dist/media.d.ts +8 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +218 -0
- package/dist/memory.d.ts +15 -6
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +251 -98
- package/dist/messages.d.ts +24 -3
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +92 -32
- package/dist/model.d.ts +8 -11
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +5 -22
- package/dist/models.d.ts +10 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +156 -0
- package/dist/observation.d.ts +3 -2
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +15 -15
- package/dist/opened.d.ts +1 -1
- package/dist/opened.d.ts.map +1 -1
- package/dist/opened.js +5 -5
- package/dist/opening.d.ts +4 -0
- package/dist/opening.d.ts.map +1 -0
- package/dist/opening.js +59 -0
- package/dist/outcome.d.ts +20 -6
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +34 -2
- package/dist/overlay.d.ts +6 -1
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +47 -21
- package/dist/page-metadata.d.ts +8 -0
- package/dist/page-metadata.d.ts.map +1 -0
- package/dist/page-metadata.js +19 -0
- package/dist/parts.d.ts +3 -0
- package/dist/parts.d.ts.map +1 -0
- package/dist/parts.js +10 -0
- package/dist/paths.d.ts +2 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +26 -0
- package/dist/preparation.d.ts +20 -0
- package/dist/preparation.d.ts.map +1 -0
- package/dist/preparation.js +233 -0
- package/dist/presence.d.ts +52 -0
- package/dist/presence.d.ts.map +1 -0
- package/dist/presence.js +220 -0
- package/dist/queries.d.ts +16 -0
- package/dist/queries.d.ts.map +1 -0
- package/dist/queries.js +285 -0
- package/dist/query.d.ts +14 -210
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +3 -505
- package/dist/questions.d.ts +29 -0
- package/dist/questions.d.ts.map +1 -0
- package/dist/questions.js +115 -0
- package/dist/queue.d.ts +40 -0
- package/dist/queue.d.ts.map +1 -0
- package/dist/queue.js +465 -0
- package/dist/reach.d.ts +15 -12
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +228 -145
- package/dist/react.d.ts +15 -2
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +33 -1
- package/dist/read.d.ts +7 -0
- package/dist/read.d.ts.map +1 -0
- package/dist/read.js +17 -0
- package/dist/readiness.d.ts +3 -18
- package/dist/readiness.d.ts.map +1 -1
- package/dist/readiness.js +4 -10
- package/dist/reads.d.ts +38 -10
- package/dist/reads.d.ts.map +1 -1
- package/dist/reads.js +163 -85
- package/dist/recovery.d.ts +3 -0
- package/dist/recovery.d.ts.map +1 -1
- package/dist/recovery.js +57 -1
- package/dist/refresh.d.ts +7 -5
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +35 -7
- package/dist/refusal.d.ts +5 -0
- package/dist/refusal.d.ts.map +1 -0
- package/dist/refusal.js +57 -0
- package/dist/replica.d.ts +13 -19
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +66 -35
- package/dist/retried.d.ts +30 -0
- package/dist/retried.d.ts.map +1 -0
- package/dist/retried.js +72 -0
- package/dist/room-draft.d.ts +24 -10
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +142 -106
- package/dist/room-drafts.d.ts +24 -0
- package/dist/room-drafts.d.ts.map +1 -0
- package/dist/room-drafts.js +217 -0
- package/dist/rows.d.ts +16 -21
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +46 -174
- package/dist/runtime.d.ts +36 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +1 -0
- package/dist/save.d.ts +42 -84
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +349 -170
- package/dist/saves.d.ts +32 -0
- package/dist/saves.d.ts.map +1 -0
- package/dist/saves.js +413 -0
- package/dist/search.d.ts +12 -63
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +9 -52
- package/dist/seed.d.ts +11 -2
- package/dist/seed.d.ts.map +1 -1
- package/dist/seed.js +71 -1
- package/dist/shared.d.ts +1 -7
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +13 -105
- package/dist/shown.d.ts +32 -0
- package/dist/shown.d.ts.map +1 -0
- package/dist/shown.js +290 -0
- package/dist/socket/admission.d.ts +7 -0
- package/dist/socket/admission.d.ts.map +1 -0
- package/dist/socket/admission.js +39 -0
- package/dist/socket/answers.d.ts +6 -0
- package/dist/socket/answers.d.ts.map +1 -0
- package/dist/socket/answers.js +20 -0
- package/dist/socket/lanes.d.ts +21 -0
- package/dist/socket/lanes.d.ts.map +1 -0
- package/dist/socket/lanes.js +80 -0
- package/dist/socket/reads.d.ts +10 -0
- package/dist/socket/reads.d.ts.map +1 -0
- package/dist/socket/reads.js +31 -0
- package/dist/socket/session.d.ts +38 -0
- package/dist/socket/session.d.ts.map +1 -0
- package/dist/socket/session.js +23 -0
- package/dist/socket.d.ts +68 -0
- package/dist/socket.d.ts.map +1 -0
- package/dist/socket.js +171 -0
- package/dist/source.d.ts +54 -0
- package/dist/source.d.ts.map +1 -0
- package/dist/source.js +82 -0
- package/dist/status.d.ts +4 -5
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +3 -7
- package/dist/store/installs.d.ts +30 -0
- package/dist/store/installs.d.ts.map +1 -0
- package/dist/store/installs.js +34 -0
- package/dist/store/readiness.d.ts +10 -0
- package/dist/store/readiness.d.ts.map +1 -0
- package/dist/store/readiness.js +52 -0
- package/dist/store/records.d.ts +212 -0
- package/dist/store/records.d.ts.map +1 -0
- package/dist/store/records.js +430 -0
- package/dist/store/room-held.d.ts +8 -0
- package/dist/store/room-held.d.ts.map +1 -0
- package/dist/store/room-held.js +17 -0
- package/dist/store/space.d.ts +11 -0
- package/dist/store/space.d.ts.map +1 -0
- package/dist/store/space.js +41 -0
- package/dist/store/watchers.d.ts +17 -0
- package/dist/store/watchers.d.ts.map +1 -0
- package/dist/store/watchers.js +56 -0
- package/dist/store/writes.d.ts +24 -0
- package/dist/store/writes.d.ts.map +1 -0
- package/dist/store/writes.js +125 -0
- package/dist/store.d.ts +110 -158
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +167 -403
- package/dist/stored.d.ts +65 -59
- package/dist/stored.d.ts.map +1 -1
- package/dist/stored.js +356 -271
- package/dist/surface.d.ts +88 -0
- package/dist/surface.d.ts.map +1 -0
- package/dist/surface.js +223 -0
- package/dist/sync.d.ts +19 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +540 -0
- package/dist/tables.d.ts +127 -69
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js +218 -105
- package/dist/tallies.d.ts +18 -0
- package/dist/tallies.d.ts.map +1 -0
- package/dist/tallies.js +129 -0
- 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 +42 -100
- 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 +63 -11
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +21 -5
- package/package.json +3 -2
- package/dist/attempt.d.ts +0 -16
- package/dist/attempt.d.ts.map +0 -1
- package/dist/attempt.js +0 -70
- package/dist/fence.d.ts +0 -6
- package/dist/fence.d.ts.map +0 -1
- package/dist/fence.js +0 -10
- package/dist/fulltext.d.ts +0 -27
- package/dist/fulltext.d.ts.map +0 -1
- package/dist/fulltext.js +0 -368
- package/dist/words.d.ts +0 -19
- package/dist/words.d.ts.map +0 -1
- package/dist/words.js +0 -150
package/dist/compile.js
CHANGED
|
@@ -1,119 +1,38 @@
|
|
|
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 {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
9
|
-
function
|
|
10
|
-
return {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
function any(parts) {
|
|
19
|
-
return parts.length === 0 ? fragment('0') : joined(parts.map(grouped), ' OR ');
|
|
20
|
-
}
|
|
21
|
-
function grouped(part) {
|
|
22
|
-
return { sql: `(${part.sql})`, bind: part.bind };
|
|
23
|
-
}
|
|
24
|
-
function list(values) {
|
|
25
|
-
return fragment(`(${values.map(() => '?').join(', ')})`, values);
|
|
26
|
-
}
|
|
27
|
-
function sqlValue(value) {
|
|
28
|
-
if (value === null)
|
|
29
|
-
return { kind: 'null', value: null };
|
|
30
|
-
if (typeof value === 'boolean')
|
|
31
|
-
return { kind: 'boolean', value: value ? 1 : 0 };
|
|
32
|
-
if (typeof value === 'number')
|
|
33
|
-
return { kind: 'number', value };
|
|
34
|
-
return { kind: 'string', value };
|
|
35
|
-
}
|
|
36
|
-
function same(alias, value) {
|
|
37
|
-
const held = sqlValue(value);
|
|
38
|
-
return held.kind === 'null'
|
|
39
|
-
? fragment(`${alias}.kind = 'null'`)
|
|
40
|
-
: fragment(`${alias}.kind = ? AND ${alias}.value = ?`, [held.kind, held.value]);
|
|
41
|
-
}
|
|
42
|
-
const BOUNDS = { gt: '>', gte: '>=', lt: '<', lte: '<=' };
|
|
43
|
-
function pointed(alias, field, reach) {
|
|
44
|
-
const { keys, places, glob } = placedOf(field, reach);
|
|
45
|
-
return fragment(`${alias}.keys = ? AND ${alias}.places ${glob ? 'GLOB' : '='} ?`, [keys, places]);
|
|
46
|
-
}
|
|
47
|
-
function heldValues(doc, field, alias, where) {
|
|
48
|
-
return joined([
|
|
49
|
-
fragment(`SELECT 1 FROM replica_values ${alias} WHERE ${alias}.doc = ${doc}.id AND `),
|
|
50
|
-
grouped(pointed(alias, field, 'value')),
|
|
51
|
-
fragment(' AND '),
|
|
52
|
-
grouped(where),
|
|
53
|
-
], '');
|
|
54
|
-
}
|
|
55
|
-
function exists(part) {
|
|
56
|
-
return { sql: `EXISTS (${part.sql})`, bind: part.bind };
|
|
57
|
-
}
|
|
58
|
-
function itemOf(held, where) {
|
|
59
|
-
return exists(joined([
|
|
60
|
-
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 `),
|
|
61
|
-
grouped(where),
|
|
62
|
-
], ''));
|
|
63
|
-
}
|
|
64
|
-
function matching(held, values) {
|
|
65
|
-
return any(values.map((value) => any([
|
|
66
|
-
same(held, value),
|
|
67
|
-
all([fragment(`${held}.kind = 'array'`), itemOf(held, same('i', value))]),
|
|
68
|
-
])));
|
|
69
|
-
}
|
|
70
|
-
function tested(doc, field, test) {
|
|
71
|
-
const reached = exists(heldValues(doc, field, 'r', fragment('r.item = -1')));
|
|
72
|
-
if (test.op === 'exists')
|
|
73
|
-
return test.exists ? reached : fragment(`NOT ${reached.sql}`, reached.bind);
|
|
74
|
-
if (test.op === 'eq' || test.op === 'in') {
|
|
75
|
-
const values = test.op === 'eq' ? [test.value] : test.values;
|
|
76
|
-
return exists(heldValues(doc, field, 'v', any(values.map((value) => same('v', value)))));
|
|
77
|
-
}
|
|
78
|
-
if (test.op === 'ne' || test.op === 'nin') {
|
|
79
|
-
const values = test.op === 'ne' ? [test.value] : test.values;
|
|
80
|
-
const unmatched = matching('h', values);
|
|
81
|
-
return any([
|
|
82
|
-
fragment(`NOT ${reached.sql}`, reached.bind),
|
|
83
|
-
exists(heldValues(doc, field, 'h', all([fragment('h.item = -1'), fragment(`NOT (${unmatched.sql})`, unmatched.bind)]))),
|
|
84
|
-
]);
|
|
85
|
-
}
|
|
86
|
-
if (test.op === 'range') {
|
|
87
|
-
return exists(heldValues(doc, field, 'v', all([
|
|
88
|
-
fragment('v.item = -1'),
|
|
89
|
-
...test.bounds.map(([operator, bound]) => {
|
|
90
|
-
const held = sqlValue(bound);
|
|
91
|
-
return fragment(`v.kind = ? AND v.value ${BOUNDS[operator]} ?`, [
|
|
92
|
-
held.kind,
|
|
93
|
-
held.value,
|
|
94
|
-
]);
|
|
95
|
-
}),
|
|
96
|
-
])));
|
|
97
|
-
}
|
|
98
|
-
if (test.op === 'all') {
|
|
99
|
-
return exists(heldValues(doc, field, 'h', all([
|
|
100
|
-
fragment("h.item = -1 AND h.kind = 'array'"),
|
|
101
|
-
...test.values.map((value) => itemOf('h', same('i', value))),
|
|
102
|
-
])));
|
|
103
|
-
}
|
|
104
|
-
return exists(heldValues(doc, field, 'v', fragment("v.item = -1 AND v.kind = 'array' AND v.size = ?", [test.size])));
|
|
3
|
+
import { definitionsAt } from './collection.js';
|
|
4
|
+
import { documentKey, pathOfKey, roomOfKey } from './rows.js';
|
|
5
|
+
import { FROM_START, MAXIMUM_WINDOW } from './windows.js';
|
|
6
|
+
const UNTYPED = 'any';
|
|
7
|
+
const { all, any, exists, fragment, grouped, joined, listed } = sql;
|
|
8
|
+
export const VISIBLE = (alias) => `${alias}.visible = 1 AND ${alias}.present = 1 AND ${alias}.queried = 1`;
|
|
9
|
+
function located(alias) {
|
|
10
|
+
return fragment(`(SELECT k.locator FROM replica_keys k WHERE k.key = ${alias}.key)`);
|
|
11
|
+
}
|
|
12
|
+
export function within(alias, path) {
|
|
13
|
+
return fragment(`(${alias}.path = ? OR (${alias}.path >= ? AND ${alias}.path < ?))`, [
|
|
14
|
+
path,
|
|
15
|
+
`${path}/`,
|
|
16
|
+
`${path}0`,
|
|
17
|
+
]);
|
|
105
18
|
}
|
|
106
|
-
function
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return
|
|
111
|
-
|
|
19
|
+
function ancestors(area) {
|
|
20
|
+
const depth = area.path.split('/').length;
|
|
21
|
+
return area.open.flatMap((open) => {
|
|
22
|
+
const segments = open.split('/');
|
|
23
|
+
return segments.slice(depth - 1, -1).map((_, at) => segments.slice(0, depth + at).join('/'));
|
|
24
|
+
});
|
|
112
25
|
}
|
|
113
|
-
function
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
26
|
+
function fenced(alias, key, area, folders) {
|
|
27
|
+
const opened = any([
|
|
28
|
+
...area.open.map((open) => within(alias, open)),
|
|
29
|
+
...(folders ? ancestors(area).map((path) => fragment(`${alias}.path = ?`, [path])) : []),
|
|
30
|
+
]);
|
|
31
|
+
return all([
|
|
32
|
+
fragment(`${alias}.key = ?`, [key]),
|
|
33
|
+
within(alias, area.path),
|
|
34
|
+
fragment(`NOT ${grouped(opened).sql}`, opened.bind),
|
|
35
|
+
]);
|
|
117
36
|
}
|
|
118
37
|
function reached(bound, alias) {
|
|
119
38
|
const reach = bound.reach;
|
|
@@ -127,42 +46,20 @@ function reached(bound, alias) {
|
|
|
127
46
|
const key = bound.keys.get(roomOfKey(held));
|
|
128
47
|
return key === undefined ? [] : [[key, pathOfKey(held)]];
|
|
129
48
|
});
|
|
130
|
-
const
|
|
49
|
+
const outside = any(reach.areas.flatMap((area) => {
|
|
131
50
|
const key = bound.keys.get(area.room);
|
|
132
|
-
return key === undefined ? [] : [
|
|
133
|
-
});
|
|
134
|
-
const beneath = (area) => fragment(`${alias}.key = ? AND (${alias}.path = ? OR (${alias}.path >= ? AND ${alias}.path < ?))`, [area.key, area.path, `${area.path}/`, `${area.path}0`]);
|
|
135
|
-
const typed = (types) => fragment(`${alias}.type IN ${list(types).sql}`, types);
|
|
136
|
-
const outside = any(areas.map((area) => area.types.length === 0
|
|
137
|
-
? beneath(area)
|
|
138
|
-
: all([beneath(area), fragment(`NOT ${grouped(typed(area.types)).sql}`, area.types)])));
|
|
51
|
+
return key === undefined ? [] : [fenced(alias, key, area, false)];
|
|
52
|
+
}));
|
|
139
53
|
return all([
|
|
140
54
|
any([
|
|
141
|
-
...(rooms.length === 0 ? [] : [fragment(`${alias}.key IN ${
|
|
55
|
+
...(rooms.length === 0 ? [] : [fragment(`${alias}.key IN ${listed(rooms).sql}`, rooms)]),
|
|
142
56
|
...(documents.length === 0
|
|
143
57
|
? []
|
|
144
58
|
: [
|
|
145
59
|
fragment(`EXISTS (SELECT 1 FROM json_each(?) j WHERE j.value ->> 0 = ${alias}.key AND j.value ->> 1 = ${alias}.path)`, [JSON.stringify(documents)]),
|
|
146
60
|
]),
|
|
147
|
-
...areas.flatMap((area) => area.types.length === 0 ? [] : [all([beneath(area), typed(area.types)])]),
|
|
148
61
|
]),
|
|
149
|
-
...(areas.length === 0 ? [] : [fragment(`NOT ${grouped(outside).sql}`, outside.bind)]),
|
|
150
|
-
]);
|
|
151
|
-
}
|
|
152
|
-
function scoped(alias, scope) {
|
|
153
|
-
return all([
|
|
154
|
-
...(scope.types === null
|
|
155
|
-
? []
|
|
156
|
-
: [fragment(`${alias}.type IN ${list(scope.types).sql}`, scope.types)]),
|
|
157
|
-
...(scope.pathPrefix === null || scope.pathPrefix === ''
|
|
158
|
-
? []
|
|
159
|
-
: [
|
|
160
|
-
fragment(`${alias}.path >= ? AND ${alias}.path < ?`, [
|
|
161
|
-
`${scope.pathPrefix}/`,
|
|
162
|
-
`${scope.pathPrefix}0`,
|
|
163
|
-
]),
|
|
164
|
-
]),
|
|
165
|
-
...(scope.where === null ? [] : [conditioned(alias, scope.where)]),
|
|
62
|
+
...(reach.areas.length === 0 ? [] : [fragment(`NOT ${grouped(outside).sql}`, outside.bind)]),
|
|
166
63
|
]);
|
|
167
64
|
}
|
|
168
65
|
function related(bound, plan) {
|
|
@@ -170,17 +67,17 @@ function related(bound, plan) {
|
|
|
170
67
|
if (plan.hop !== null) {
|
|
171
68
|
parts.push(exists(joined([
|
|
172
69
|
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`),
|
|
173
|
-
grouped(
|
|
70
|
+
grouped(sql.placed('l', plan.hop.field, 'link')),
|
|
174
71
|
fragment('AND'),
|
|
175
|
-
grouped(all([scoped('m', plan.hop), reached(bound, 'm')])),
|
|
72
|
+
grouped(all([sql.scoped('m', plan.hop), reached(bound, 'm')])),
|
|
176
73
|
], ' ')));
|
|
177
74
|
}
|
|
178
75
|
if (plan.backlinks !== null) {
|
|
179
76
|
const field = plan.backlinks.field;
|
|
180
77
|
parts.push(exists(joined([
|
|
181
78
|
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`),
|
|
182
|
-
...(field === null ? [] : [grouped(
|
|
183
|
-
grouped(all([scoped('s', plan.backlinks), reached(bound, 's')])),
|
|
79
|
+
...(field === null ? [] : [grouped(sql.placed('l', field, 'link')), fragment('AND')]),
|
|
80
|
+
grouped(all([sql.scoped('s', plan.backlinks), reached(bound, 's')])),
|
|
184
81
|
], ' ')));
|
|
185
82
|
}
|
|
186
83
|
return parts;
|
|
@@ -188,11 +85,9 @@ function related(bound, plan) {
|
|
|
188
85
|
function filtered(bound, plan) {
|
|
189
86
|
const keys = bound.rooms.map(({ key }) => key);
|
|
190
87
|
return all([
|
|
191
|
-
fragment(`d.key IN ${
|
|
192
|
-
...(plan.paths === null ? [] : [fragment(`d.path IN ${list(plan.paths).sql}`, plan.paths)]),
|
|
193
|
-
...(plan.folder === null ? [] : [fragment('d.folder = ?', [plan.folder])]),
|
|
88
|
+
fragment(`d.key IN ${listed(keys).sql} AND ${VISIBLE('d')}`, keys),
|
|
194
89
|
reached(bound, 'd'),
|
|
195
|
-
|
|
90
|
+
sql.tests('d', plan),
|
|
196
91
|
...related(bound, plan),
|
|
197
92
|
]);
|
|
198
93
|
}
|
|
@@ -223,108 +118,65 @@ const GROUP_TIES = {
|
|
|
223
118
|
locale: undefined,
|
|
224
119
|
localeOptions: undefined,
|
|
225
120
|
};
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
return { placement: 1, klass: 3, scalar: JSON.stringify(value) };
|
|
237
|
-
}
|
|
238
|
-
function placement(term) {
|
|
239
|
-
const nulls = term.ordering.nulls === 'first' ? 0 : 2;
|
|
240
|
-
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]);
|
|
241
|
-
}
|
|
242
|
-
function klass(term) {
|
|
243
|
-
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);
|
|
244
|
-
}
|
|
245
|
-
function classed(term, which) {
|
|
246
|
-
const kinds = which === 1 ? "('boolean', 'number')" : which === 2 ? "('string')" : "('array')";
|
|
247
|
-
return fragment(`(CASE WHEN ${term.kind.sql} IN ${kinds} THEN ${term.value.sql} END)`, [
|
|
248
|
-
...term.kind.bind,
|
|
249
|
-
...term.value.bind,
|
|
250
|
-
]);
|
|
251
|
-
}
|
|
252
|
-
function collated(bound, term, which) {
|
|
253
|
-
if (which === 1)
|
|
254
|
-
return '';
|
|
255
|
-
const name = bound.collate(term.ordering, which === 3, term.total === true);
|
|
256
|
-
return name === null ? '' : ` COLLATE ${name}`;
|
|
257
|
-
}
|
|
258
|
-
function sorted(bound, term) {
|
|
259
|
-
const direction = term.ordering.direction === 'desc' ? 'DESC' : 'ASC';
|
|
260
|
-
return joined([
|
|
261
|
-
placement(term),
|
|
262
|
-
fragment(`${klass(term).sql} ${direction}`, klass(term).bind),
|
|
263
|
-
...[1, 2, 3].map((which) => {
|
|
264
|
-
const value = classed(term, which);
|
|
265
|
-
return fragment(`${value.sql}${collated(bound, term, which)} ${direction}`, value.bind);
|
|
266
|
-
}),
|
|
267
|
-
], ', ');
|
|
268
|
-
}
|
|
269
|
-
function beyond(bound, term, value) {
|
|
270
|
-
const held = classOf(value, term.ordering);
|
|
271
|
-
const place = placement(term);
|
|
272
|
-
if (held.placement !== 1) {
|
|
273
|
-
return {
|
|
274
|
-
beyond: fragment(`${place.sql} > ${held.placement}`, place.bind),
|
|
275
|
-
equal: fragment(`${place.sql} = ${held.placement}`, place.bind),
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
const after = term.ordering.direction === 'desc' ? '<' : '>';
|
|
279
|
-
const kind = klass(term);
|
|
280
|
-
const scalar = classed(term, held.klass);
|
|
281
|
-
const collation = collated(bound, term, held.klass);
|
|
282
|
-
return {
|
|
283
|
-
beyond: any([
|
|
284
|
-
fragment(`${place.sql} > 1`, place.bind),
|
|
285
|
-
all([
|
|
286
|
-
fragment(`${place.sql} = 1`, place.bind),
|
|
287
|
-
any([
|
|
288
|
-
fragment(`${kind.sql} ${after} ${held.klass}`, kind.bind),
|
|
289
|
-
all([
|
|
290
|
-
fragment(`${kind.sql} = ${held.klass}`, kind.bind),
|
|
291
|
-
fragment(`${scalar.sql} ${after} ?${collation}`, [...scalar.bind, held.scalar]),
|
|
292
|
-
]),
|
|
293
|
-
]),
|
|
294
|
-
]),
|
|
295
|
-
]),
|
|
296
|
-
equal: all([
|
|
297
|
-
fragment(`${place.sql} = 1 AND ${kind.sql} = ${held.klass}`, [...place.bind, ...kind.bind]),
|
|
298
|
-
fragment(`${scalar.sql} = ?${collation}`, [...scalar.bind, held.scalar]),
|
|
299
|
-
]),
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
function following(parts, tie) {
|
|
303
|
-
let condition = tie;
|
|
304
|
-
for (const part of [...parts].reverse()) {
|
|
305
|
-
condition = any([part.beyond, all([part.equal, condition])]);
|
|
306
|
-
}
|
|
307
|
-
return condition;
|
|
121
|
+
const GREATEST = { ...GROUP_ORDER, direction: 'desc', nulls: 'last' };
|
|
122
|
+
const TIMES = {
|
|
123
|
+
created: { value: 'd.created', instant: 'd.created_instant' },
|
|
124
|
+
updated: { value: "(d.change ->> '$.time')", instant: 'd.changed_instant' },
|
|
125
|
+
};
|
|
126
|
+
function subfolderOf(column, prefix) {
|
|
127
|
+
const root = prefix ?? '';
|
|
128
|
+
const base = root === '' ? '' : `${root}/`;
|
|
129
|
+
const rest = `substr(${column}, length(?) + 1)`;
|
|
130
|
+
return fragment(`(CASE WHEN ${column} = ? THEN NULL WHEN instr(${rest}, '/') = 0 THEN ${column} ELSE ? || substr(${rest}, 1, instr(${rest}, '/') - 1) END)`, [root, base, base, base, base]);
|
|
308
131
|
}
|
|
309
|
-
function keyTerm(
|
|
132
|
+
function keyTerm(key, ordering, index, prefix) {
|
|
310
133
|
if (key.of === 'field') {
|
|
311
134
|
const alias = `o${index}`;
|
|
312
135
|
return {
|
|
313
|
-
term: {
|
|
136
|
+
term: {
|
|
137
|
+
ordering,
|
|
138
|
+
kind: fragment(`${alias}.kind`),
|
|
139
|
+
value: fragment(`${alias}.value`),
|
|
140
|
+
instant: fragment(`${alias}.instant`),
|
|
141
|
+
},
|
|
314
142
|
join: joined([
|
|
315
143
|
fragment(`LEFT JOIN replica_values ${alias} ON ${alias}.doc = d.id AND `),
|
|
316
|
-
grouped(
|
|
144
|
+
grouped(sql.placed(alias, key.field, 'exact')),
|
|
317
145
|
fragment(` AND ${alias}.item = -1 AND ${alias}.kind NOT IN ('object', 'undefined')`),
|
|
318
146
|
], ''),
|
|
319
147
|
};
|
|
320
148
|
}
|
|
149
|
+
if (key.of === 'size') {
|
|
150
|
+
return {
|
|
151
|
+
term: {
|
|
152
|
+
ordering,
|
|
153
|
+
kind: fragment("(CASE WHEN d.bytes IS NULL THEN 'null' ELSE 'number' END)"),
|
|
154
|
+
value: fragment('d.bytes'),
|
|
155
|
+
},
|
|
156
|
+
join: null,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
if (key.of === 'created' || key.of === 'updated') {
|
|
160
|
+
const { value, instant } = TIMES[key.of];
|
|
161
|
+
return {
|
|
162
|
+
term: {
|
|
163
|
+
ordering,
|
|
164
|
+
kind: fragment(`(CASE WHEN ${value} IS NULL THEN 'null' ELSE 'string' END)`),
|
|
165
|
+
value: fragment(value),
|
|
166
|
+
instant: fragment(instant),
|
|
167
|
+
},
|
|
168
|
+
join: null,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
321
171
|
const expression = key.of === 'room'
|
|
322
|
-
? located(
|
|
172
|
+
? located('d')
|
|
323
173
|
: key.of === 'path'
|
|
324
174
|
? fragment('d.path')
|
|
325
175
|
: key.of === 'type'
|
|
326
176
|
? fragment('d.type')
|
|
327
|
-
:
|
|
177
|
+
: key.of === 'subfolder'
|
|
178
|
+
? subfolderOf('d.folder', prefix)
|
|
179
|
+
: fragment('d.folder');
|
|
328
180
|
return {
|
|
329
181
|
term: {
|
|
330
182
|
ordering,
|
|
@@ -345,12 +197,20 @@ function decoded(kind, value) {
|
|
|
345
197
|
return JSON.parse(value);
|
|
346
198
|
return null;
|
|
347
199
|
}
|
|
200
|
+
function sortedOf(kind, value, instant) {
|
|
201
|
+
return sql.sortValue(kind, decoded(kind, value), instant);
|
|
202
|
+
}
|
|
203
|
+
function sortedValue(row, index) {
|
|
204
|
+
return sortedOf(row[`k${index}`], row[`v${index}`], row[`i${index}`]);
|
|
205
|
+
}
|
|
348
206
|
function decodedKey(key, row, index) {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
return
|
|
352
|
-
|
|
353
|
-
|
|
207
|
+
const kind = row[`k${index}`];
|
|
208
|
+
if (key.of === 'field' && (kind === null || kind === undefined))
|
|
209
|
+
return undefined;
|
|
210
|
+
return sortedValue(row, index);
|
|
211
|
+
}
|
|
212
|
+
function instantOf(term) {
|
|
213
|
+
return term.instant ?? fragment('NULL');
|
|
354
214
|
}
|
|
355
215
|
const registered = new WeakMap();
|
|
356
216
|
function hashed(text) {
|
|
@@ -401,9 +261,6 @@ function collating(connection) {
|
|
|
401
261
|
return name;
|
|
402
262
|
};
|
|
403
263
|
}
|
|
404
|
-
function bodied(shape) {
|
|
405
|
-
return shape.sections !== null || shape.preamble || shape.markdown;
|
|
406
|
-
}
|
|
407
264
|
function masked(pointers) {
|
|
408
265
|
const root = { whole: false, members: new Map() };
|
|
409
266
|
for (const pointer of pointers) {
|
|
@@ -455,11 +312,20 @@ function sectionsOf(sections, names) {
|
|
|
455
312
|
return sections;
|
|
456
313
|
return Object.fromEntries(names.flatMap((name) => (Object.hasOwn(sections, name) ? [[name, sections[name]]] : [])));
|
|
457
314
|
}
|
|
458
|
-
function
|
|
315
|
+
function freeParts(room, path, type, content) {
|
|
316
|
+
const definitions = room.composition === undefined ? undefined : definitionsAt(room.composition, path);
|
|
317
|
+
const definition = definitions?.(type === UNTYPED ? undefined : type);
|
|
318
|
+
if (definition === undefined && type !== UNTYPED)
|
|
319
|
+
return null;
|
|
320
|
+
const read = parse(content, path, definition);
|
|
321
|
+
return read.ok ? read.document : null;
|
|
322
|
+
}
|
|
323
|
+
function documentOf(shape, queried, row, linked) {
|
|
324
|
+
const room = queried.locator;
|
|
459
325
|
const path = row.path;
|
|
460
326
|
const type = row.type;
|
|
461
|
-
const fileId = row.file_id
|
|
462
|
-
const revision = row.revision
|
|
327
|
+
const fileId = row.file_id;
|
|
328
|
+
const revision = row.revision;
|
|
463
329
|
if (type === null) {
|
|
464
330
|
const binary = row.binary === null ? null : JSON.parse(row.binary);
|
|
465
331
|
const content = row.content ?? null;
|
|
@@ -471,15 +337,16 @@ function documentOf(shape, room, row, linked) {
|
|
|
471
337
|
: {
|
|
472
338
|
binaryFile: binaryRecord(path, binary, revision ?? undefined),
|
|
473
339
|
}),
|
|
474
|
-
...(shape.markdown && content !== null
|
|
475
|
-
|
|
476
|
-
: {}),
|
|
340
|
+
...(shape.markdown && content !== null ? { markdown: content } : {}),
|
|
341
|
+
...(shape.change ? { change: changeOf(row) } : {}),
|
|
477
342
|
...(shape.fileId ? { fileId } : {}),
|
|
478
343
|
...(shape.revision ? { revision } : {}),
|
|
344
|
+
...heldFacts(shape, row),
|
|
479
345
|
};
|
|
480
346
|
}
|
|
481
|
-
const
|
|
482
|
-
|
|
347
|
+
const body = shape.sections !== null || shape.preamble
|
|
348
|
+
? freeParts(queried, path, type, row.content)
|
|
349
|
+
: null;
|
|
483
350
|
const fields = JSON.parse(row.fields ?? '{}');
|
|
484
351
|
return {
|
|
485
352
|
room,
|
|
@@ -496,15 +363,21 @@ function documentOf(shape, room, row, linked) {
|
|
|
496
363
|
? {}
|
|
497
364
|
: { sections: sectionsOf(body.sections, shape.sections) }),
|
|
498
365
|
...(shape.preamble && body !== null ? { preamble: body.preamble } : {}),
|
|
499
|
-
...(shape.markdown
|
|
366
|
+
...(shape.markdown ? { markdown: row.content } : {}),
|
|
500
367
|
...(shape.backlinks ? { backlinks: linked.get(documentKey(room, path)) ?? [] } : {}),
|
|
501
|
-
...(shape.change
|
|
502
|
-
? {
|
|
503
|
-
change: row.change === null ? null : JSON.parse(row.change),
|
|
504
|
-
}
|
|
505
|
-
: {}),
|
|
368
|
+
...(shape.change ? { change: changeOf(row) } : {}),
|
|
506
369
|
...(shape.fileId ? { fileId } : {}),
|
|
507
370
|
...(shape.revision ? { revision } : {}),
|
|
371
|
+
...heldFacts(shape, row),
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
function changeOf(row) {
|
|
375
|
+
return row.change === null ? null : JSON.parse(row.change);
|
|
376
|
+
}
|
|
377
|
+
function heldFacts(shape, row) {
|
|
378
|
+
return {
|
|
379
|
+
...(shape.size ? { size: row.bytes === null ? null : Number(row.bytes) } : {}),
|
|
380
|
+
...(shape.created ? { created: row.created } : {}),
|
|
508
381
|
};
|
|
509
382
|
}
|
|
510
383
|
function backlinksOf(connection, bound, rows) {
|
|
@@ -516,8 +389,8 @@ function backlinksOf(connection, bound, rows) {
|
|
|
516
389
|
held.push(row.path);
|
|
517
390
|
byKey.set(key, held);
|
|
518
391
|
}
|
|
392
|
+
const reach = reached(bound, 's');
|
|
519
393
|
for (const [key, targets] of byKey) {
|
|
520
|
-
const reach = reached(bound, 's');
|
|
521
394
|
const room = bound.rooms.find((one) => one.key === key).locator;
|
|
522
395
|
const found = connection.rows(`SELECT target, path, type, field FROM (SELECT l.target, s.path, s.type, l.field, ROW_NUMBER() OVER (PARTITION BY l.target ORDER BY s.path, l.field) AS rank FROM (SELECT DISTINCT doc, target, field FROM replica_links WHERE key = ? AND kind = 'field' AND target IN (SELECT value FROM json_each(?))) l JOIN replica_entries s ON s.id = l.doc AND ${VISIBLE('s')} WHERE ${reach.sql}) WHERE rank <= ? ORDER BY target, path, field`, [key, JSON.stringify(targets), ...reach.bind, MAXIMUM_WINDOW]);
|
|
523
396
|
for (const row of found) {
|
|
@@ -530,38 +403,36 @@ function backlinksOf(connection, bound, rows) {
|
|
|
530
403
|
return linked;
|
|
531
404
|
}
|
|
532
405
|
function documents(connection, bound, plan, shape, budget) {
|
|
533
|
-
const terms = shape.orderBy.map(({ key, options }, index) => keyTerm(
|
|
406
|
+
const terms = shape.orderBy.map(({ key, options }, index) => keyTerm(key, orderingOf(options), index, plan.root.pathPrefix));
|
|
534
407
|
const filter = filtered(bound, plan);
|
|
535
408
|
const cursor = plan.after?.values ?? null;
|
|
536
|
-
const room = located(
|
|
537
|
-
const
|
|
409
|
+
const room = located('d');
|
|
410
|
+
const after = cursor === null
|
|
538
411
|
? null
|
|
539
412
|
: (() => {
|
|
540
413
|
const key = cursor[shape.orderBy.length];
|
|
541
|
-
return any([
|
|
414
|
+
return sql.following(terms.map(({ term }, index) => sql.beyond(bound.collate, term, cursor[index])), any([
|
|
542
415
|
fragment(`${room.sql} > ?`, [...room.bind, roomOfKey(key)]),
|
|
543
416
|
fragment(`${room.sql} = ? AND d.path > ?`, [
|
|
544
417
|
...room.bind,
|
|
545
418
|
roomOfKey(key),
|
|
546
419
|
pathOfKey(key),
|
|
547
420
|
]),
|
|
548
|
-
]);
|
|
421
|
+
]));
|
|
549
422
|
})();
|
|
550
|
-
const after = cursor === null || tie === null
|
|
551
|
-
? null
|
|
552
|
-
: following(terms.map(({ term }, index) => beyond(bound, term, cursor[index])), tie);
|
|
553
423
|
const selected = joined([
|
|
554
|
-
fragment('SELECT d.id, d.key, d.path, d.type, d.binary, d.revision, d.fields, d.change, d.file_id'),
|
|
424
|
+
fragment('SELECT d.id, d.key, d.path, d.type, d.binary, d.revision, d.fields, d.change, d.file_id, d.bytes, d.created'),
|
|
555
425
|
...terms.map(({ term }, index) => joined([
|
|
556
426
|
fragment(`, ${term.kind.sql} AS k${index}`, term.kind.bind),
|
|
557
427
|
fragment(`, ${term.value.sql} AS v${index}`, term.value.bind),
|
|
428
|
+
fragment(`, ${instantOf(term).sql} AS i${index}`, instantOf(term).bind),
|
|
558
429
|
], '')),
|
|
559
430
|
fragment(' FROM replica_entries d'),
|
|
560
431
|
...terms.flatMap(({ join }) => (join === null ? [] : [fragment(' '), join])),
|
|
561
432
|
fragment(' WHERE '),
|
|
562
433
|
after === null ? filter : all([filter, after]),
|
|
563
434
|
fragment(' ORDER BY '),
|
|
564
|
-
joined([...terms.map(({ term }) => sorted(bound, term)), room, fragment('d.path')], ', '),
|
|
435
|
+
joined([...terms.map(({ term }) => sql.sorted(bound.collate, term)), room, fragment('d.path')], ', '),
|
|
565
436
|
...(plan.limit === null ? [] : [fragment(' LIMIT ?', [plan.limit + 1])]),
|
|
566
437
|
], '');
|
|
567
438
|
const rows = connection.rows(selected.sql, selected.bind);
|
|
@@ -569,8 +440,9 @@ function documents(connection, bound, plan, shape, budget) {
|
|
|
569
440
|
const kept = bodied(shape) ? withContent(connection, windowed, budget) : windowed;
|
|
570
441
|
const cut = kept.length;
|
|
571
442
|
const roomOf = new Map(bound.rooms.map(({ key, locator }) => [key, locator]));
|
|
443
|
+
const queried = new Map(bound.rooms.map((one) => [one.key, one]));
|
|
572
444
|
const linked = shape.backlinks ? backlinksOf(connection, bound, kept) : new Map();
|
|
573
|
-
const found = kept.map((row) => documentOf(shape,
|
|
445
|
+
const found = kept.map((row) => documentOf(shape, queried.get(Number(row.key)), row, linked));
|
|
574
446
|
const last = kept.at(-1);
|
|
575
447
|
const cursorAt = (row) => cursorOf([
|
|
576
448
|
...shape.orderBy.map(({ key }, index) => decodedKey(key, row, index)),
|
|
@@ -600,7 +472,7 @@ function withContent(connection, rows, budget) {
|
|
|
600
472
|
return rows;
|
|
601
473
|
const ids = rows.map((row) => row.id);
|
|
602
474
|
const sizes = new Map(connection
|
|
603
|
-
.rows(`SELECT id, octet_length(content) AS size FROM replica_entries WHERE id IN ${
|
|
475
|
+
.rows(`SELECT id, octet_length(content) AS size FROM replica_entries WHERE id IN ${listed(ids).sql}`, ids)
|
|
604
476
|
.map((row) => [row.id, Number(row.size ?? 0)]));
|
|
605
477
|
let cut = rows.length;
|
|
606
478
|
let spent = 0;
|
|
@@ -615,125 +487,213 @@ function withContent(connection, rows, budget) {
|
|
|
615
487
|
const kept = rows.slice(0, cut);
|
|
616
488
|
const read = kept.map((row) => row.id);
|
|
617
489
|
const held = new Map(connection
|
|
618
|
-
.rows(`SELECT id, content FROM replica_entries WHERE id IN ${
|
|
490
|
+
.rows(`SELECT id, content FROM replica_entries WHERE id IN ${listed(read).sql}`, read)
|
|
619
491
|
.map((row) => [row.id, row.content]));
|
|
620
492
|
return kept.map((row) => ({ ...row, content: held.get(row.id) ?? null }));
|
|
621
493
|
}
|
|
622
|
-
function storedFolders(bound, plan) {
|
|
623
|
-
if (plan.paths !== null || plan.
|
|
494
|
+
function storedFolders(bound, plan, subfolder) {
|
|
495
|
+
if (plan.paths !== null || plan.fileIds !== null)
|
|
496
|
+
return null;
|
|
497
|
+
if (plan.hop !== null || plan.backlinks !== null)
|
|
498
|
+
return null;
|
|
499
|
+
if (subfolder && plan.folder !== null)
|
|
624
500
|
return null;
|
|
625
501
|
const whole = bound.rooms.filter(({ locator }) => bound.reach === null || bound.reach.rooms.includes(locator));
|
|
626
502
|
if (whole.length === 0)
|
|
627
503
|
return null;
|
|
628
504
|
const keys = whole.map(({ key }) => key);
|
|
629
|
-
const prefix = plan.root.pathPrefix;
|
|
630
|
-
const
|
|
505
|
+
const prefix = plan.root.pathPrefix ?? '';
|
|
506
|
+
const outside = (bound.reach?.areas ?? []).flatMap((area) => {
|
|
631
507
|
const key = bound.keys.get(area.room);
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
key,
|
|
637
|
-
area.path,
|
|
638
|
-
`${area.path}/`,
|
|
639
|
-
`${area.path}0`,
|
|
640
|
-
]),
|
|
641
|
-
];
|
|
508
|
+
if (key === undefined)
|
|
509
|
+
return [];
|
|
510
|
+
const fence = fenced('f', key, area, true);
|
|
511
|
+
return [fragment(`NOT ${grouped(fence).sql}`, fence.bind)];
|
|
642
512
|
});
|
|
513
|
+
const under = prefix === ''
|
|
514
|
+
? subfolder
|
|
515
|
+
? [fragment("f.path <> ''")]
|
|
516
|
+
: []
|
|
517
|
+
: [
|
|
518
|
+
subfolder
|
|
519
|
+
? fragment('(f.path >= ? AND f.path < ?)', [`${prefix}/`, `${prefix}0`])
|
|
520
|
+
: within('f', prefix),
|
|
521
|
+
];
|
|
643
522
|
return all([
|
|
644
|
-
fragment(`f.key IN ${
|
|
645
|
-
...
|
|
646
|
-
...
|
|
647
|
-
|
|
648
|
-
: [
|
|
649
|
-
fragment('(f.path = ? OR (f.path >= ? AND f.path < ?))', [
|
|
650
|
-
prefix,
|
|
651
|
-
`${prefix}/`,
|
|
652
|
-
`${prefix}0`,
|
|
653
|
-
]),
|
|
654
|
-
]),
|
|
523
|
+
fragment(`f.key IN ${listed(keys).sql} AND f.layer = 0 AND f.present = 1`, keys),
|
|
524
|
+
...outside,
|
|
525
|
+
...under,
|
|
526
|
+
...(plan.outside ?? []).map((folder) => fragment(`NOT ${within('f', folder).sql}`, within('f', folder).bind)),
|
|
655
527
|
...(plan.folder === null ? [] : [fragment('f.path = ?', [plan.folder])]),
|
|
656
528
|
]);
|
|
657
529
|
}
|
|
530
|
+
const SIZES = [
|
|
531
|
+
'CASE WHEN d.binary IS NULL THEN 1 ELSE 0 END AS dn',
|
|
532
|
+
'CASE WHEN d.binary IS NULL THEN d.bytes ELSE 0 END AS ds',
|
|
533
|
+
'CASE WHEN d.binary IS NULL THEN 0 ELSE 1 END AS fn',
|
|
534
|
+
'CASE WHEN d.binary IS NULL THEN 0 ELSE d.bytes END AS fs',
|
|
535
|
+
];
|
|
536
|
+
const LATEST = "d.changed_instant || ' ' || (d.change ->> '$.time') AS u";
|
|
537
|
+
function greatestTerm(ordering) {
|
|
538
|
+
return { ordering, kind: fragment('gk'), value: fragment('gv'), instant: fragment('gi') };
|
|
539
|
+
}
|
|
540
|
+
function latestOf(held) {
|
|
541
|
+
if (typeof held !== 'string')
|
|
542
|
+
return null;
|
|
543
|
+
return held.slice(held.indexOf(' ') + 1);
|
|
544
|
+
}
|
|
658
545
|
function groups(connection, bound, plan, shape) {
|
|
546
|
+
const prefix = plan.root.pathPrefix;
|
|
659
547
|
const keyed = shape.groupBy.map((key, index) => {
|
|
660
|
-
const { term, join } = keyTerm(
|
|
548
|
+
const { term, join } = keyTerm(key, GROUP_ORDER, index, prefix);
|
|
661
549
|
const kind = fragment(`COALESCE(${term.kind.sql}, 'null')`, term.kind.bind);
|
|
662
550
|
return { term: { ...term, kind }, join };
|
|
663
551
|
});
|
|
552
|
+
const ordering = shape.orderBy.map(({ by }) => by);
|
|
553
|
+
const sized = shape.size || ordering.includes('size');
|
|
554
|
+
const dated = shape.updated || ordering.includes('updated');
|
|
555
|
+
const greatest = shape.greatest === null
|
|
556
|
+
? null
|
|
557
|
+
: keyTerm({ of: 'field', field: shape.greatest }, GREATEST, keyed.length, prefix);
|
|
664
558
|
const filter = filtered(bound, plan);
|
|
665
559
|
const inner = joined([
|
|
666
560
|
fragment('SELECT '),
|
|
667
561
|
joined(keyed.map(({ term }, index) => joined([
|
|
668
562
|
fragment(`${term.kind.sql} AS k${index}`, term.kind.bind),
|
|
669
563
|
fragment(`, ${term.value.sql} AS v${index}`, term.value.bind),
|
|
564
|
+
fragment(`, ${instantOf(term).sql} AS i${index}`, instantOf(term).bind),
|
|
670
565
|
], '')), ', '),
|
|
671
|
-
fragment(', 1 AS n
|
|
672
|
-
...
|
|
566
|
+
fragment(['', '1 AS n', ...(sized ? SIZES : []), ...(dated ? [LATEST] : [])].join(', ')),
|
|
567
|
+
...(greatest === null
|
|
568
|
+
? []
|
|
569
|
+
: [
|
|
570
|
+
fragment(`, ${greatest.term.kind.sql} AS gk`, greatest.term.kind.bind),
|
|
571
|
+
fragment(`, ${greatest.term.value.sql} AS gv`, greatest.term.value.bind),
|
|
572
|
+
fragment(`, ${instantOf(greatest.term).sql} AS gi`, instantOf(greatest.term).bind),
|
|
573
|
+
]),
|
|
574
|
+
fragment(' FROM replica_entries d'),
|
|
575
|
+
...[...keyed, ...(greatest === null ? [] : [greatest])].flatMap(({ join }) => join === null ? [] : [fragment(' '), join]),
|
|
673
576
|
fragment(' WHERE '),
|
|
674
577
|
filter,
|
|
675
578
|
], '');
|
|
676
|
-
const
|
|
677
|
-
|
|
678
|
-
|
|
579
|
+
const only = shape.groupBy.length === 1 ? shape.groupBy[0].of : null;
|
|
580
|
+
const subfolder = only === 'subfolder';
|
|
581
|
+
const folders = only === 'folder' || subfolder ? storedFolders(bound, plan, subfolder) : null;
|
|
582
|
+
const folderKey = subfolder ? subfolderOf('f.path', prefix) : fragment('f.path');
|
|
583
|
+
const zeros = [
|
|
584
|
+
...(sized ? ['0', '0', '0', '0'] : []),
|
|
585
|
+
...(dated ? ['NULL'] : []),
|
|
586
|
+
...(greatest === null ? [] : ['NULL', 'NULL', 'NULL']),
|
|
587
|
+
];
|
|
679
588
|
const source = folders === null
|
|
680
589
|
? inner
|
|
681
590
|
: joined([
|
|
682
591
|
inner,
|
|
683
|
-
fragment(
|
|
592
|
+
fragment(` UNION ALL SELECT 'string' AS k0, ${folderKey.sql} AS v0, NULL AS i0, 0 AS n${zeros.map((one) => `, ${one}`).join('')} FROM replica_folders f WHERE `, folderKey.bind),
|
|
684
593
|
folders,
|
|
685
594
|
], '');
|
|
686
|
-
const
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
595
|
+
const held = keyed
|
|
596
|
+
.map((_, index) => `k${index}, i${index}, CASE WHEN i${index} IS NULL THEN v${index} END`)
|
|
597
|
+
.join(', ');
|
|
598
|
+
const ranked = greatest === null
|
|
599
|
+
? source
|
|
600
|
+
: joined([
|
|
601
|
+
fragment(`SELECT *, ROW_NUMBER() OVER (PARTITION BY ${held} ORDER BY `),
|
|
602
|
+
sql.sorted(bound.collate, greatestTerm(GREATEST)),
|
|
603
|
+
fragment(', gv) AS gr FROM ('),
|
|
604
|
+
source,
|
|
605
|
+
fragment(')'),
|
|
606
|
+
], '');
|
|
607
|
+
const columns = [
|
|
608
|
+
...keyed.map((_, index) => `k${index}, min(v${index}) AS v${index}, i${index}`),
|
|
609
|
+
'sum(n) AS n',
|
|
610
|
+
...(sized ? ['sum(dn) AS dn', 'sum(ds) AS ds', 'sum(fn) AS fn', 'sum(fs) AS fs'] : []),
|
|
611
|
+
...(dated ? ['max(u) AS u'] : []),
|
|
612
|
+
...(greatest === null
|
|
613
|
+
? []
|
|
614
|
+
: ['gk', 'gv', 'gi'].map((column) => `min(CASE WHEN gr = 1 THEN ${column} END) AS ${column}`)),
|
|
615
|
+
].join(', ');
|
|
616
|
+
const outer = (index, order) => ({
|
|
617
|
+
ordering: order,
|
|
694
618
|
kind: fragment(`k${index}`),
|
|
695
619
|
value: fragment(`v${index}`),
|
|
696
620
|
});
|
|
697
|
-
const
|
|
698
|
-
|
|
699
|
-
|
|
621
|
+
const termOf = (by, order) => {
|
|
622
|
+
if (by === 'count')
|
|
623
|
+
return { ordering: order, kind: fragment("'number'"), value: fragment('n') };
|
|
624
|
+
if (by === 'size') {
|
|
625
|
+
return { ordering: order, kind: fragment("'number'"), value: fragment('(ds + fs)') };
|
|
626
|
+
}
|
|
627
|
+
if (by === 'updated') {
|
|
628
|
+
return {
|
|
629
|
+
ordering: order,
|
|
630
|
+
kind: fragment("(CASE WHEN u IS NULL THEN 'null' ELSE 'string' END)"),
|
|
631
|
+
value: fragment("substr(u, instr(u, ' ') + 1)"),
|
|
632
|
+
instant: fragment("substr(u, 1, instr(u, ' ') - 1)"),
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
if (by === 'greatest')
|
|
636
|
+
return greatestTerm(order);
|
|
637
|
+
return { ...outer(by, order), instant: fragment(`i${by}`) };
|
|
638
|
+
};
|
|
639
|
+
const valueOf = (row, by) => {
|
|
640
|
+
if (by === 'count')
|
|
641
|
+
return Number(row.n);
|
|
642
|
+
if (by === 'size')
|
|
643
|
+
return Number(row.ds) + Number(row.fs);
|
|
644
|
+
if (by === 'updated') {
|
|
645
|
+
const text = latestOf(row.u);
|
|
646
|
+
return text === null ? null : sql.sortValue('string', text, row.u.split(' ')[0]);
|
|
647
|
+
}
|
|
648
|
+
if (by === 'greatest')
|
|
649
|
+
return sortedOf(row.gk, row.gv, row.gi);
|
|
650
|
+
return sortedValue(row, by);
|
|
651
|
+
};
|
|
652
|
+
const ordered = shape.orderBy.map(({ by, direction, nulls }) => termOf(by, { ...GROUP_ORDER, direction, nulls: nulls ?? GROUP_ORDER.nulls }));
|
|
700
653
|
const ties = keyed.flatMap((_, index) => [
|
|
701
654
|
{ ...outer(index, GROUP_TIES), total: true },
|
|
702
655
|
{ ordering: GROUP_TIES, kind: fragment("'string'"), value: fragment(`k${index}`) },
|
|
703
656
|
]);
|
|
704
657
|
const cursor = plan.after?.values ?? null;
|
|
705
|
-
const
|
|
706
|
-
? null
|
|
707
|
-
: cursor[shape.orderBy.length].flatMap((value) => [
|
|
708
|
-
value,
|
|
709
|
-
kindOf(value ?? null),
|
|
710
|
-
]);
|
|
711
|
-
const after = cursor === null || tieValues === null
|
|
658
|
+
const after = cursor === null
|
|
712
659
|
? null
|
|
713
|
-
:
|
|
714
|
-
|
|
660
|
+
: (() => {
|
|
661
|
+
const tieValues = cursor[shape.orderBy.length].flatMap((value) => [value, heldKind(value ?? null)]);
|
|
662
|
+
return sql.following(ordered.map((term, index) => sql.beyond(bound.collate, term, cursor[index])), sql.following(ties
|
|
663
|
+
.slice(0, -1)
|
|
664
|
+
.map((term, index) => sql.beyond(bound.collate, term, tieValues[index])), sql.beyond(bound.collate, ties.at(-1), tieValues.at(-1)).beyond));
|
|
665
|
+
})();
|
|
666
|
+
const grouping = joined([fragment(`SELECT ${columns} FROM (`), ranked, fragment(`) GROUP BY ${held}`)], '');
|
|
715
667
|
const windowed = joined([
|
|
716
668
|
fragment('SELECT * FROM ('),
|
|
717
669
|
grouping,
|
|
718
670
|
fragment(')'),
|
|
719
671
|
...(after === null ? [] : [fragment(' WHERE '), after]),
|
|
720
672
|
fragment(' ORDER BY '),
|
|
721
|
-
joined([...ordered, ...ties].map((term) => sorted(bound, term)), ', '),
|
|
673
|
+
joined([...ordered, ...ties].map((term) => sql.sorted(bound.collate, term)), ', '),
|
|
722
674
|
...(plan.limit === null ? [] : [fragment(' LIMIT ?', [plan.limit + 1])]),
|
|
723
675
|
], '');
|
|
724
676
|
const rows = connection.rows(windowed.sql, windowed.bind);
|
|
725
677
|
const kept = plan.limit === null ? rows : rows.slice(0, plan.limit);
|
|
726
678
|
const keyOf = (row) => keyed.map((_, index) => decoded(row[`k${index}`], row[`v${index}`]));
|
|
727
|
-
const found = kept.map((row) => ({
|
|
679
|
+
const found = kept.map((row) => ({
|
|
680
|
+
key: keyOf(row),
|
|
681
|
+
count: Number(row.n),
|
|
682
|
+
...(shape.size
|
|
683
|
+
? {
|
|
684
|
+
documents: { count: Number(row.dn), size: Number(row.ds) },
|
|
685
|
+
files: { count: Number(row.fn), size: Number(row.fs) },
|
|
686
|
+
}
|
|
687
|
+
: {}),
|
|
688
|
+
...(shape.updated ? { updated: latestOf(row.u) } : {}),
|
|
689
|
+
...(shape.greatest === null ? {} : { greatest: decoded(row.gk, row.gv) }),
|
|
690
|
+
}));
|
|
728
691
|
const last = kept.at(-1);
|
|
729
692
|
const next = plan.limit === null || rows.length <= plan.limit
|
|
730
693
|
? undefined
|
|
731
694
|
: last === undefined
|
|
732
695
|
? (plan.after?.text ?? FROM_START)
|
|
733
|
-
: cursorOf([
|
|
734
|
-
...shape.orderBy.map(({ by }) => by === 'count' ? Number(last.n) : decoded(last[`k${by}`], last[`v${by}`])),
|
|
735
|
-
keyOf(last),
|
|
736
|
-
]);
|
|
696
|
+
: cursorOf([...shape.orderBy.map(({ by }) => valueOf(last, by)), keyOf(last)]);
|
|
737
697
|
const total = plan.total
|
|
738
698
|
? Number(connection.rows(joined([fragment('SELECT count(*) AS held FROM ('), grouping, fragment(')')], '').sql, grouping.bind)[0].held)
|
|
739
699
|
: undefined;
|
|
@@ -751,9 +711,6 @@ function boundOf(connection, plan, rooms) {
|
|
|
751
711
|
collate: collating(connection),
|
|
752
712
|
};
|
|
753
713
|
}
|
|
754
|
-
export function selected(connection, plan, rooms) {
|
|
755
|
-
return filtered(boundOf(connection, plan, rooms), plan);
|
|
756
|
-
}
|
|
757
714
|
export function answerQuery(connection, plan, rooms, budget = null) {
|
|
758
715
|
const bound = boundOf(connection, plan, rooms);
|
|
759
716
|
if (rooms.length === 0) {
|