1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
//! Indexes the two hot read paths could not use.
//!
//! **The SQL/PGQ hop.** Every edge index is partial on `deleted_at IS NULL`,
//! and the pattern cannot say `deleted_at IS NULL`: the column is outside the
//! edge element's `PROPERTIES`, so `MATCH` does not see it, and the tombstone
//! filter is applied afterwards by an ordinary scoped read. A predicate the
//! planner never sees cannot license a partial index, so each hop through
//! `GRAPH_TABLE` read the whole edge table (a parallel sequential scan) to
//! find a frontier's few edges. Measured on the Studio stand (2026-09-27/28):
//! 39 / 100 / 144 ms at depth 1 / 2 / 3 against 4 / 6 / 8 ms for the
//! two-query hop over the same graph, and 537 ms for one hop from a hub of a
//! 638 000-edge graph. With the endpoint indexes below, not partial, that hop
//! is an index scan (0.4 ms). The partial ones stay: the two-query hop and
//! adjacency reads name `deleted_at` and keep using them.
//!
//! **A projection filtered on a payload path.** The statement is `tenant AND
//! deleted_at IS NULL AND gts_node_type_id IN (…) AND payload @> … ORDER BY
//! node_key LIMIT n`. At a middling selectivity the planner walks
//! `(tenant_id, node_key)` for the ordering and filters row by row -- 81 581
//! rows discarded, 69 ms, for a type of 40 000 in a tenant of 240 000 --
//! because neither the payload GIN nor the type index yields rows in key
//! order. `(tenant_id, gts_node_type_id, node_key)` does: the same statement
//! took 0.6 ms, and through REST the listing went from 44 to 299 requests a
//! second at 32 clients.
//!
//! **Cost of running this on a populated graph.** The migration runner wraps
//! every migration in a transaction (README § Known limitations), so these are
//! plain `CREATE INDEX`es: each holds a `SHARE` lock on its table for its
//! build, which blocks writes to that table and not reads. B-tree builds over
//! the columns named here are fast -- on the 638 000-edge stand graph the edge
//! pair took about a second -- but a deployment far larger than that should
//! run this migration in a quiet window.
use *;
use ConnectionTrait;
;