The key-value substrate the index rests on, and the one IndexStore
implementation written over it.
The index — the trie, the secondary roots, the rule indexes, the exception
re-check index, and the inverted term index — is all sets and counters keyed
by structured vectors. KvIndexStore encodes that structure once, in terms of a
small KvBackend protocol; a backend is then just an adapter that says how a
scalar, a counter, and a set live in some store. An in-memory map
(vaelii.impl.memory) and an on-disk WAL (vaelii.impl.disk.kv) are two such
adapters; a SQL or overlay backend is another.
A sentex is indexed by its trie path. Every node, identified by its path prefix, is exactly three keys:
count-key [:trie :count prefix] -> integer: how many sentexes live at the leaves under this prefix (selectivity without walking). set-key [:trie :children prefix] -> a SET: the next possible token labels (the node's child edges). leaf-key [:trie :handles prefix] -> a SET: the handles of the sentexes whose path ends exactly here.
Child edges and leaf handles are separate keys because the trie is ragged.
Paths differ in length with arity, so one sentex's full path can be a proper
prefix of another's: (rel A B) in CxCee keys as [rel A B CxCee],
and (rel A B CxCee X) in CxDee keys as [rel A B CxCee X CxDee] — the first path is an interior node of the second. Storing both
handles and child tokens in one set therefore mixed them, and a caller could not
tell them apart by type (a handle is an integer, and so is the token 1970). Two
keys make the distinction structural: lookup reads only the leaf key at its
terminus, so it can never return a token as a handle, and children reads only
the child set, so plan's fan-out divisor can never count a handle as a branch.
Alongside the trie sit ten smaller count tries whose last level is the context — the
argument roots, the predicate extent, the two rule indexes, the rule extent, the
opposed bodies, the self tuples, the taxonomy's supporters and the mint family's two
("The count tries that end in the context" below) — and three flat sets whose
cardinality is their own size: the context root, the exception re-check index and the
inverted term index. One more flat set, the term roster
[:term-roster], holds the term index's names rather than handles, so the
vocabulary can be listed and counted in O(terms) instead of a walk over every record.
Contract: lookup expects a full path (sentence tokens + a context slot; the context may itself be a variable). A short pattern terminates on an interior node, whose leaf key is empty, so it yields nothing rather than that node's child labels dressed up as handles.
KvBackendLogical keys are structured vectors and set members are bare values; a backend
turns those into whatever its store wants (an in-memory map uses them directly; the
on-disk backend nippy-frames them into its log). The required ops are
kv-batch (the whole index write for one sentex lands as one unit — one batched
write) and kv-intersect (the multi-column narrowing sentexes-with-args needs, one
set intersection rather than N fetch-and-filter reads). A batch op is a vector
[op key & args] with op one of :put, :delete, :increment,
:decrement, :add-to-set, :remove-from-set; kv-batch
returns one reply per op in order (only :increment/:decrement replies — the post-op
counter value — are read; the rest are placeholders that keep the vector aligned).
kv-member? is a membership test, not a fetch, and it is its own op because on
several backends those cost different orders. exception-rule? is the gate
chain/rule-view-of takes once per candidate rule per new datum, so answering it by
materializing the roster and testing the result makes forward chaining a product of
two KB-sized quantities. A flat-map backend hands the stored set back by reference
and hides the distinction entirely; a dense one holds the roster as an IntPostings,
where 1e5 gate calls against a roster of 1,000 cost 15,926 ms built-then-tested
against 87 ms on the flat map — so the op exists to let each backend answer with the
probe it already has (a hash lookup, a binary search, a bitmap test).
The key-value substrate the index rests on, and the one `IndexStore`
implementation written over it.
The index — the trie, the secondary roots, the rule indexes, the exception
re-check index, and the inverted term index — is *all* sets and counters keyed
by structured vectors. `KvIndexStore` encodes that structure once, in terms of a
small `KvBackend` protocol; a backend is then just an adapter that says how a
scalar, a counter, and a set live in some store. An in-memory map
(`vaelii.impl.memory`) and an on-disk WAL (`vaelii.impl.disk.kv`) are two such
adapters; a SQL or overlay backend is another.
## The count-aware trie
A sentex is indexed by its trie path. Every node, identified by its path
prefix, is exactly three keys:
count-key [:trie :count prefix] -> integer: how many sentexes live at the leaves
under this prefix (selectivity without walking).
set-key [:trie :children prefix] -> a SET: the next possible token labels (the
node's child edges).
leaf-key [:trie :handles prefix] -> a SET: the handles of the sentexes whose path
ends exactly here.
**Child edges and leaf handles are separate keys because the trie is ragged.**
Paths differ in length with arity, so one sentex's *full* path can be a proper
prefix of another's: `(rel A B)` in `CxCee` keys as `[rel A B CxCee]`,
and `(rel A B CxCee X)` in `CxDee` keys as `[rel A B CxCee X
CxDee]` — the first path is an interior node of the second. Storing both
handles and child tokens in one set therefore mixed them, and a caller could not
tell them apart by type (a handle is an integer, and so is the token `1970`). Two
keys make the distinction structural: `lookup` reads only the leaf key at its
terminus, so it can never return a token as a handle, and `children` reads only
the child set, so `plan`'s fan-out divisor can never count a handle as a branch.
Alongside the trie sit ten smaller count tries whose last level is the context — the
argument roots, the predicate extent, the two rule indexes, the rule extent, the
opposed bodies, the self tuples, the taxonomy's supporters and the mint family's two
("The count tries that end in the context" below) — and three flat sets whose
cardinality is their own size: the context root, the exception re-check index and the
inverted term index. One more flat set, the **term roster**
`[:term-roster]`, holds the term index's *names* rather than handles, so the
vocabulary can be listed and counted in O(terms) instead of a walk over every record.
Contract: lookup expects a *full* path (sentence tokens + a context slot; the
context may itself be a variable). A short pattern terminates on an interior
node, whose leaf key is empty, so it yields nothing rather than that node's child
labels dressed up as handles.
## The `KvBackend`
Logical keys are structured vectors and set members are bare values; a backend
turns those into whatever its store wants (an in-memory map uses them directly; the
on-disk backend nippy-frames them into its log). The required ops are
`kv-batch` (the whole index write for one sentex lands as one unit — one batched
write) and `kv-intersect` (the multi-column narrowing `sentexes-with-args` needs, one
set intersection rather than N fetch-and-filter reads). A batch op is a vector
`[op key & args]` with `op` one of `:put`, `:delete`, `:increment`,
`:decrement`, `:add-to-set`, `:remove-from-set`; `kv-batch`
returns one reply per op in order (only `:increment`/`:decrement` replies — the post-op
counter value — are read; the rest are placeholders that keep the vector aligned).
**`kv-member?` is a membership test, not a fetch**, and it is its own op because on
several backends those cost different orders. `exception-rule?` is the gate
`chain/rule-view-of` takes once per candidate rule per new datum, so answering it by
materializing the roster and testing the result makes forward chaining a product of
two KB-sized quantities. A flat-map backend hands the stored set back by reference
and hides the distinction entirely; a dense one holds the roster as an `IntPostings`,
where 1e5 gate calls against a roster of 1,000 cost 15,926 ms built-then-tested
against 87 ms on the flat map — so the op exists to let each backend answer with the
probe it already has (a hash lookup, a binary search, a bitmap test).(all-supporters index)Every taxonomy key with a supporter, as {k {handle ctx}}, by a walk of every entry in
index's roots store: O(store), for a raw taxonomy's private store, which holds the
supporter families and nothing else.
Every taxonomy key with a supporter, as `{k {handle ctx}}`, by a walk of every entry in
`index`'s roots store: O(store), for a raw taxonomy's private store, which holds the
supporter families and nothing else.(apply-op m [op k a])Apply one kv-batch write op to map m, returning [m' reply]. Only
:increment/:decrement carry a meaningful reply (the post-op counter value); the
rest reply nil. :remove-from-set drops the key when the set empties, so an absent
key and an empty set are indistinguishable — a set with no members does not exist.
The op semantics live here, with the protocol that names them, and not in the
backends. Both map-shaped backends fold their writes through this — the in-memory
one (vaelii.impl.memory) over its state map, the on-disk one
(vaelii.impl.disk.kv) over the RAM half of its write-ahead log, where it is also
what replays the log, since a WAL frame there is the write op itself rather than
the resulting value. Written twice, the two copies could answer one op differently,
and the disk side is replay: a seventh op added to the live path and missed in the
fold would be a write that applies once and never comes back.
An unrecognized op goes to unknown-op!, which every adapter shares: a bulk load
taking the transient path, a dense backend's own batch, a fork decorator's, and an
ordinary write taking this one may not disagree about what an unreadable op is.
:decrement floors at zero, per the KvBackend contract: these counters are
cardinalities. The floor is in every fold rather than in one of them, because the WAL
replays through this and the live write went through a backend — a floor applied on
only one side would make a reopened store disagree with the one that wrote it.
Apply one `kv-batch` write op to map `m`, returning `[m' reply]`. Only `:increment`/`:decrement` carry a meaningful reply (the post-op counter value); the rest reply nil. `:remove-from-set` drops the key when the set empties, so an absent key and an empty set are indistinguishable — a set with no members does not exist. **The op semantics live here, with the protocol that names them, and not in the backends.** Both map-shaped backends fold their writes through this — the in-memory one (`vaelii.impl.memory`) over its state map, the on-disk one (`vaelii.impl.disk.kv`) over the RAM half of its write-ahead log, where it is also what *replays* the log, since a WAL frame there is the write op itself rather than the resulting value. Written twice, the two copies could answer one op differently, and the disk side is replay: a seventh op added to the live path and missed in the fold would be a write that applies once and never comes back. An unrecognized op goes to `unknown-op!`, which every adapter shares: a bulk load taking the transient path, a dense backend's own batch, a fork decorator's, and an ordinary write taking this one may not disagree about what an unreadable op is. `:decrement` floors at zero, per the `KvBackend` contract: these counters are cardinalities. The floor is in every fold rather than in one of them, because the WAL replays through this and the live write went through a backend — a floor applied on only one side would make a reopened store disagree with the one that wrote it.
(arg-census index pred pos term)[n k]: how many facts with functor pred and term at argument pos are stored, in
every context, and how many contexts state one. The argument node's own count and its
context-children count, two count reads. nil where extent-census is nil.
`[n k]`: how many facts with functor `pred` and `term` at argument `pos` are stored, in every context, and how many contexts state one. The argument node's own count and its context-children count, two count reads. nil where `extent-census` is nil.
(arg-count-in index pred pos term ctxs)How many facts with functor pred and term at argument pos are stored in the
contexts of the set ctxs: one leaf count per context of the argument node seen-ctxs
keeps. nil where extent-census is nil.
How many facts with functor `pred` and `term` at argument `pos` are stored in the contexts of the set `ctxs`: one leaf count per context of the argument node `seen-ctxs` keeps. nil where `extent-census` is nil.
(arg-slots sentex)[[pred pos term] ...] - the argument-root nodes a fact enters, each term canonical.
Empty for a rule and for a non-fact, matching root-keys.
The canonicalization is here rather than in the key constructors so a term reaching a key or a backend read is canonical whichever of the three rosters it is going into.
`[[pred pos term] ...]` - the argument-root nodes a fact enters, each term canonical. Empty for a rule and for a non-fact, matching `root-keys`. The canonicalization is here rather than in the key constructors so a term reaching a key or a backend read is canonical whichever of the three rosters it is going into.
(children-held index prefix)The child tokens under the trie's interior prefix as the backend holds them, a set, or
nil when index is not a KvIndexStore (a ColumnarIndexStore holds its trie itself).
On the map backends the answer is the stored set, so two reads with no write between
them answer one object; children copies it into a vector.
The child tokens under the trie's interior `prefix` as the backend holds them, a set, or nil when `index` is not a `KvIndexStore` (a `ColumnarIndexStore` holds its trie itself). On the map backends the answer is the stored set, so two reads with no write between them answer one object; `children` copies it into a vector.
(extent-census index pred ctxs)[seen n]: the contexts of the set ctxs (every context when nil) in which a fact with
functor pred is stored, either polarity, and how many contexts state one. The children
of the predicate extent's node [pred]: one count read, then, when it is not zero, the
intersection with ctxs on the smaller side, with no leaf read. nil for an index store
this finds no KvIndexStore in, as slot-predicates.
`[seen n]`: the contexts of the set `ctxs` (every context when nil) in which a fact with functor `pred` is stored, either polarity, and how many contexts state one. The children of the predicate extent's node `[pred]`: one count read, then, when it is not zero, the intersection with `ctxs` on the smaller side, with no leaf read. nil for an index store this finds no `KvIndexStore` in, as `slot-predicates`.
(extent-contexts index pred contexts)The contexts of contexts (every context when nil) stating a fact of functor pred,
either polarity: the predicate extent's children intersected with contexts, which costs
min(|children|, |contexts|) probes and reads no leaf — or nil for an index store this
finds no extent in (a test's reify). Not an IndexStore op; read where
slot-predicates reads.
The contexts of `contexts` (every context when nil) stating a fact of functor `pred`, either polarity: the predicate extent's children intersected with `contexts`, which costs min(|children|, |contexts|) probes and reads no leaf — or nil for an index store this finds no extent in (a test's `reify`). Not an `IndexStore` op; read where `slot-predicates` reads.
(extent-count-in index pred ctxs)How many facts with functor pred are stored in the contexts of the set ctxs: one
leaf count per context extent-census keeps. nil where extent-census is nil.
How many facts with functor `pred` are stored in the contexts of the set `ctxs`: one leaf count per context `extent-census` keeps. nil where `extent-census` is nil.
(flat-family-adds backend trie sentex handle){:ops [...] :counts {:terms n :roots n :roster n :slots n}} — the non-trie write ops
entering sentex under handle, and the number of ops each family takes.
Both roster reads run here, before the caller batches the ops, so a name enters the term roster and a predicate enters its argument slot exactly on the first sentex to carry it.
`{:ops [...] :counts {:terms n :roots n :roster n :slots n}}` — the non-trie write ops
entering `sentex` under `handle`, and the number of ops each family takes.
Both roster reads run here, before the caller batches the ops, so a name enters the
term roster and a predicate enters its argument slot exactly on the first sentex to
carry it.(flat-family-retires backend trie sentex handle){:ops [...] :counts {:terms n :roots n :roster n :slots n}} — the mirror of
flat-family-adds: the ops taking handle out of those same families, and the
number of ops each family takes.
Every read runs here, before the caller batches the ops, so a name leaves the term roster, a predicate leaves its argument slot and a node leaves the argument trie exactly on the last sentex to carry it.
`{:ops [...] :counts {:terms n :roots n :roster n :slots n}}` — the mirror of
`flat-family-adds`: the ops taking `handle` out of those same families, and the
number of ops each family takes.
Every read runs here, before the caller batches the ops, so a name leaves the term
roster, a predicate leaves its argument slot and a node leaves the argument trie
exactly on the last sentex to carry it.Which key shapes this build's index is written in.
The key families below — [:trie :count|:children|:handles prefix], the context root,
the five count tries ending in the context, the exception index, the term index and the
rosters — are
the index's portable form, so a dump of them is only readable by a build that agrees on
them. An index written in a layout this build does not use reads as empty rather
than as wrong (a lookup finds no key and answers nothing), which is fail-safe and
undiagnosable — so the layout is stated as a number and checked, rather than discovered
by a KB that quietly stopped answering.
Bump it whenever a key shape changes: a new family, a renamed tag, a different arity, or a different value type at an existing key.
2 scopes the argument roots by predicate ([:argument-root pred pos term]) and adds
the [:argument-slot pos term] roster that keeps the predicate-agnostic reads
answerable. 3 adds the [:unary-slot term] roster beside it, which is what lets a
membership question read a term's types without fetching every fact that names it.
4 keys the argument roots as a count trie over [pred pos term ctx]: a node's
[:argument-root :count|:children [pred pos term]] and the leaf
[:argument-root :handles [pred pos term ctx]]. 5 replaces the functor root with the
predicate extent, a count trie over [pred ctx], and keys the two rule indexes as count
tries over [key ctx] (:rule-antecedent, :rule-consequent). 6 adds the rule
extent, a count trie over [kind ctx] (:rule-extent), and the antecedent trie's root
level, [:rule-antecedent-keys]. 7 adds the bodies stored in both polarities: the
count trie :opposed over [body ctx], its root level [:opposed-bodies], and the
members by context, [:opposed-in ctx]. 8 adds the taxonomy's supporter families: a
count trie over [k ctx] keyed by the key a declaration installs (:tax-support), and
[:tax-installs h]. 9 adds the mint family: the :mint count trie over [term ctx]
with its root level [:mint-terms], and the :mint-in count trie over [ctx]. 10
adds the shape roster, [:shape-count f n] and [:shape-lengths f], [:unary-multi],
the terms the unary roster lists two or more predicates of, and the self-tuple count
trie over [pred ctx] (:self-tuple).
Which key shapes this build's index is written in. The key families below — `[:trie :count|:children|:handles prefix]`, the context root, the five count tries ending in the context, the exception index, the term index and the rosters — *are* the index's portable form, so a dump of them is only readable by a build that agrees on them. An index written in a layout this build does not use reads as **empty** rather than as wrong (a lookup finds no key and answers nothing), which is fail-safe and undiagnosable — so the layout is stated as a number and checked, rather than discovered by a KB that quietly stopped answering. **Bump it whenever a key shape changes**: a new family, a renamed tag, a different arity, or a different value type at an existing key. 2 scopes the argument roots by predicate (`[:argument-root pred pos term]`) and adds the `[:argument-slot pos term]` roster that keeps the predicate-agnostic reads answerable. 3 adds the `[:unary-slot term]` roster beside it, which is what lets a membership question read a term's types without fetching every fact that names it. 4 keys the argument roots as a count trie over `[pred pos term ctx]`: a node's `[:argument-root :count|:children [pred pos term]]` and the leaf `[:argument-root :handles [pred pos term ctx]]`. 5 replaces the functor root with the predicate extent, a count trie over `[pred ctx]`, and keys the two rule indexes as count tries over `[key ctx]` (`:rule-antecedent`, `:rule-consequent`). 6 adds the rule extent, a count trie over `[kind ctx]` (`:rule-extent`), and the antecedent trie's root level, `[:rule-antecedent-keys]`. 7 adds the bodies stored in both polarities: the count trie `:opposed` over `[body ctx]`, its root level `[:opposed-bodies]`, and the members by context, `[:opposed-in ctx]`. 8 adds the taxonomy's supporter families: a count trie over `[k ctx]` keyed by the key a declaration installs (`:tax-support`), and `[:tax-installs h]`. 9 adds the mint family: the `:mint` count trie over `[term ctx]` with its root level `[:mint-terms]`, and the `:mint-in` count trie over `[ctx]`. 10 adds the shape roster, `[:shape-count f n]` and `[:shape-lengths f]`, `[:unary-multi]`, the terms the unary roster lists two or more predicates of, and the self-tuple count trie over `[pred ctx]` (`:self-tuple`).
(installed-keys index h)The taxonomy keys handle h installs, as a set: one read.
The taxonomy keys handle `h` installs, as a set: one read.
(justification-family-entry? entry)Is entry, a [key value] index entry, one of the mint family's, which the stored
justifications derive rather than the stored sentexes?
Is `entry`, a `[key value]` index entry, one of the mint family's, which the stored justifications derive rather than the stored sentexes?
(mint-context-count index)How many contexts some mint is stored in, read without building the set.
How many contexts some mint is stored in, read without building the set.
(mint-contexts index)The contexts some mint is stored in, as a set: the :mint-in node's children.
The contexts some mint is stored in, as a set: the `:mint-in` node's children.
(mint-count index)How many records the mint family files: the count of the :mint-in node.
How many records the mint family files: the count of the `:mint-in` node.
(mint-filed? index term c h)Is record h filed as a mint about term in context c? One membership test.
Is record `h` filed as a mint about `term` in context `c`? One membership test.
(mint-term-count index)How many terms some mint is about, read without building the set.
How many terms some mint is about, read without building the set.
(mint-term? index term)Is some mint about term? One membership test.
Is some mint about `term`? One membership test.
(mint-terms index)The terms some mint is about, as a set: the :mint trie's root level.
The terms some mint is about, as a set: the `:mint` trie's root level.
(mints-about index term)The handles of the mints about term, in every context.
The handles of the mints about `term`, in every context.
(mints-in index c)The handles of the mints stored in context c: one :mint-in leaf.
The handles of the mints stored in context `c`: one `:mint-in` leaf.
(opposed-bodies index)Every body stored in both polarities, as a set, or nil for an index store this finds
no KvIndexStore in.
Every body stored in both polarities, as a set, or nil for an index store this finds no `KvIndexStore` in.
(opposed-body? index body)Is body stored in both polarities? One membership test, or nil for an index store
this finds no KvIndexStore in.
Is `body` stored in both polarities? One membership test, or nil for an index store this finds no `KvIndexStore` in.
(opposed-count index)How many bodies are stored in both polarities: the size of opposed-bodies-key, one
read. nil for an index store this finds no KvIndexStore in.
How many bodies are stored in both polarities: the size of `opposed-bodies-key`, one read. nil for an index store this finds no `KvIndexStore` in.
(opposed-in index ctxs)The members of every opposed body stated in a context of ctxs: one leaf read per
context, [:opposed-in ctx], so the read costs |ctxs| plus the members it returns. nil
for an index store this finds no KvIndexStore in.
The members of every opposed body stated in a context of `ctxs`: one leaf read per context, `[:opposed-in ctx]`, so the read costs |ctxs| plus the members it returns. nil for an index store this finds no `KvIndexStore` in.
(opposed-members index body)The handles of body's facts of both polarities while it is stored in both, in every
context, or nil for an index store this finds no KvIndexStore in.
The handles of `body`'s facts of both polarities while it is stored in both, in every context, or nil for an index store this finds no `KvIndexStore` in.
(post-mint! index term c h)File record h under term in context c in one batch, unless it is filed there.
File record `h` under `term` in context `c` in one batch, unless it is filed there.
(post-supporter! index k h ctx)Enter h (stated in ctx) as a supporter of taxonomy key k in index's roots
store, in one batch, and answer k's supporter count after it: the batch's reply to
the node's increment, nil where the backend replies none (a bulk load's transient). A
no-op answering nil when h already is one, or when index keeps no roots store.
Enter `h` (stated in `ctx`) as a supporter of taxonomy key `k` in `index`'s roots store, in one batch, and answer `k`'s supporter count after it: the batch's reply to the node's increment, nil where the backend replies none (a bulk load's transient). A no-op answering nil when `h` already is one, or when `index` keeps no roots store.
(retire-mint! index term c h)Take record h out from under term in context c in one batch, when it is filed
there.
Take record `h` out from under `term` in context `c` in one batch, when it is filed there.
(retire-supporter! index k h)Take h out of taxonomy key k's supporters in index's roots store, in one batch,
and answer true; a no-op answering false when it is not one.
Take `h` out of taxonomy key `k`'s supporters in `index`'s roots store, in one batch, and answer true; a no-op answering false when it is not one.
(root-keys sentex)The handle postings a sentex enters outside the trie and the term index: its context
root always, plus the leaf under each of its fact-nodes in its context.
The handle postings a sentex enters outside the trie and the term index: its context root always, plus the leaf under each of its `fact-nodes` in its context.
(roots-backend index)The KvBackend under index's root families (roots-store), or nil.
The `KvBackend` under `index`'s root families (`roots-store`), or nil.
(roster-adds backend terms)The write ops entering terms (one sentex's, from sentex-terms) in the roster —
read BEFORE their postings are written, so a name is entered exactly by the first
sentex to mention it.
The write ops entering `terms` (one sentex's, from `sentex-terms`) in the roster — read BEFORE their postings are written, so a name is entered exactly by the first sentex to mention it.
(roster-retires backend terms handle)The write ops retiring the names in terms that handle is the last mention of —
read BEFORE their postings are removed, so a name dies exactly when its posting is
#{handle}.
The write ops retiring the names in `terms` that `handle` is the last mention of —
read BEFORE their postings are removed, so a name dies exactly when its posting is
`#{handle}`.(rule-extent index kind contexts)The handles of the rules of kind (:rule, every rule; :solve, the rules a solve
reads) stated in one of contexts (every context when nil): the rule extent's children
intersected with contexts, and a leaf read per context kept. nil for an index store
this finds no rule extent in.
The handles of the rules of `kind` (`:rule`, every rule; `:solve`, the rules a solve reads) stated in one of `contexts` (every context when nil): the rule extent's children intersected with `contexts`, and a leaf read per context kept. nil for an index store this finds no rule extent in.
(rule-extent-contexts index kind contexts)The contexts of contexts (every context when nil) stating a rule of kind, with no
leaf read, or nil for an index store this finds no rule extent in.
The contexts of `contexts` (every context when nil) stating a rule of `kind`, with no leaf read, or nil for an index store this finds no rule extent in.
(rule-key? index k)Does a stored rule take the antecedent key k? One membership test, or nil for an
index store this finds no rule index in.
Does a stored rule take the antecedent key `k`? One membership test, or nil for an index store this finds no rule index in.
(rule-keys index)The antecedent keys some stored rule takes (rules/antecedent-key), as a set, or nil
for an index store this finds no rule index in.
The antecedent keys some stored rule takes (`rules/antecedent-key`), as a set, or nil for an index store this finds no rule index in.
The count prefix of the batch-seal counter: incremented as the last op of every
index-sentex batch and decremented as the last op of an unindex's cleanup batch,
so it equals the indexed-sentex count exactly when every batch landed whole. The
durable open's coverage gate compares it against the record count: the WAL logs one
frame per op, so a torn tail keeps a batch's prefix — the root counter count-at [] reads is op 0 and survives every tear, which is what makes it the wrong
instrument — while this counter is the op a tear loses first. A namespaced keyword,
so it collides with no term path; only the count key is written, so no trie walk
ever meets it. Zero on a store whose index arrived by index-load replay or was
written before the counter existed — the gate falls back to the root count there.
The count prefix of the batch-seal counter: incremented as the **last** op of every `index-sentex` batch and decremented as the last op of an unindex's cleanup batch, so it equals the indexed-sentex count exactly when every batch landed whole. The durable open's coverage gate compares it against the record count: the WAL logs one frame per op, so a torn tail keeps a batch's *prefix* — the root counter `count-at []` reads is op 0 and survives every tear, which is what makes it the wrong instrument — while this counter is the op a tear loses first. A namespaced keyword, so it collides with no term path; only the count key is written, so no trie walk ever meets it. Zero on a store whose index arrived by `index-load` replay or was written before the counter existed — the gate falls back to the root count there.
(self-tuples index f ctxs)The handles of the ground positive binary self tuples (f a a) of functor f stated
in a context of ctxs (every context when nil), or nil for an index store this finds no
roots store in: the :self-tuple node's children intersected with ctxs, and a leaf
read per context kept.
The handles of the ground positive binary self tuples `(f a a)` of functor `f` stated in a context of `ctxs` (every context when nil), or nil for an index store this finds no roots store in: the `:self-tuple` node's children intersected with `ctxs`, and a leaf read per context kept.
(sentex-terms sentex)The distinct terms that make a sentex findable: its indexable content terms (see sentex/index-terms — connective-free, no numbers/strings/variables) plus its context.
The distinct terms that make a sentex findable: its indexable content terms (see sentex/index-terms — connective-free, no numbers/strings/variables) plus its context.
(shape-lengths index f)The lengths the positive facts of functor f are stored at, as a set, or nil for an
index store this finds no shape roster in.
The lengths the positive facts of functor `f` are stored at, as a set, or nil for an index store this finds no shape roster in.
(slot-adds backend sentex)Write ops entering a sentex's predicates in their slots - read BEFORE the postings are written, so a predicate is entered exactly by the fact that creates its node.
The unary roster is the exception and takes no read at all: its entry is written by
every unary fact rather than by the first, because the node that guards the others is
[pred 1 term], which a binary fact of the same predicate about the same term also
creates - so a reference count read off it would skip the unary entry whenever the
binary fact arrived first, and a missing entry there loses a membership. An
:add-to-set of a member already present is a no-op inside the same batch. A term
joins unary-multi-key when the entry is its second predicate, which two reads of the
unary roster decide.
Write ops entering a sentex's predicates in their slots - read BEFORE the postings are written, so a predicate is entered exactly by the fact that creates its node. The unary roster is the exception and takes no read at all: its entry is written by every unary fact rather than by the first, because the node that guards the others is `[pred 1 term]`, which a *binary* fact of the same predicate about the same term also creates - so a reference count read off it would skip the unary entry whenever the binary fact arrived first, and a missing entry there loses a membership. An `:add-to-set` of a member already present is a no-op inside the same batch. A term joins `unary-multi-key` when the entry is its second predicate, which two reads of the unary roster decide.
(slot-predicates index pos term)The predicates the slot roster lists at (pos, term) — every predicate holding a fact,
in either polarity, with term at 1-based argument pos — or nil for an index store
this finds no roster in.
The roster read the two predicate-agnostic reads above open with, without the postings
they then union: a caller asking which predicates hold a term pays one set read and no
handle. Not an IndexStore op. Every index store the engine builds keeps the roster
in a KvIndexStore — itself, or the one a ColumnarIndexStore delegates its roots to
as :embedded — and this reads it there. An empty slot answers #{}, so nil means
only that the store holds neither (a test's reify), and the caller falls back to the
reads the protocol has.
The predicates the slot roster lists at `(pos, term)` — every predicate holding a fact,
in either polarity, with `term` at 1-based argument `pos` — or nil for an index store
this finds no roster in.
The roster read the two predicate-agnostic reads above open with, without the postings
they then union: a caller asking *which predicates* hold a term pays one set read and no
handle. Not an `IndexStore` op. Every index store the engine builds keeps the roster
in a `KvIndexStore` — itself, or the one a `ColumnarIndexStore` delegates its roots to
as `:embedded` — and this reads it there. An empty slot answers `#{}`, so nil means
only that the store holds neither (a test's `reify`), and the caller falls back to the
reads the protocol has.(supporter-adds backend k h ctx)Write ops entering handle h, stated in ctx, as a supporter of taxonomy key k, or
nil when it already is one.
Write ops entering handle `h`, stated in `ctx`, as a supporter of taxonomy key `k`, or nil when it already is one.
(supporter-count index k)How many handles support taxonomy key k: the node's count, one read.
How many handles support taxonomy key `k`: the node's count, one read.
(supporter-retires backend k h)Write ops taking handle h out of taxonomy key k's supporters, or nil when it is not
one. Reads the contexts under k for the one whose leaf holds h.
Write ops taking handle `h` out of taxonomy key `k`'s supporters, or nil when it is not one. Reads the contexts under `k` for the one whose leaf holds `h`.
(supporters index k)Taxonomy key k's supporters as {handle ctx}: the node's children, then one leaf
read per context. nil for an index store this finds no roots store in.
Taxonomy key `k`'s supporters as `{handle ctx}`: the node's children, then one leaf
read per context. nil for an index store this finds no roots store in.(trie-reads count-at children leaf)The two trie reads the opposed family's writes take, over count-at (prefix ->
count) and children and leaf (prefix -> set): :count, and :leaves, the
[child handles] of each child of a prefix whose leaf one level below holds a handle.
The two trie reads the opposed family's writes take, over `count-at` (prefix -> count) and `children` and `leaf` (prefix -> set): `:count`, and `:leaves`, the `[child handles]` of each child of a prefix whose leaf one level below holds a handle.
(unary-multi-terms index)The terms the unary roster lists two or more predicates of, as a set, or nil for an
index store this finds no roster in. A superset of the terms holding two memberships,
as the unary roster is a superset (unary-slot-key).
The terms the unary roster lists two or more predicates of, as a set, or nil for an index store this finds no roster in. A superset of the terms holding two memberships, as the unary roster is a superset (`unary-slot-key`).
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |