Expand description
Full-text search for Cloudillo.
§Where the index lives, and why
Everything searchable — files, deep document parts, actions, profiles —
lands in one search_docs table in the meta SQLite database, mirrored into
an FTS5 virtual table. One table means one query surface and one ABAC
predicate shape; FTS5 supplies bm25() ranking and snippet() for free;
and RTDB, CRDT and blob content can all funnel through the same
serde_json::Value → text extractor.
§Rule-driven indexing
No indexing-specific Rust is needed to index new content. What text an object
carries is declared next to the thing it describes, in one extraction
language (rules, extract) shared by both sources of rules:
- Documents — an app registers a document-format manifest ([
format]) naming which RTDB/CRDT collections and fields carry text and what the deep-link key is.indexerapplies it to an exported document, so a hit points at a subpage rather than at the whole file. - Actions — an action type’s DSL definition carries a
searchblock.objectsapplies it. A type without one is not indexed; that absence is the whole allowlist.
Files and profiles keep a fixed mapping in objects rather than a
manifest: their schema is server-owned and not app-extensible, so there is
nothing for an app to declare.
§Who writes which rows
Every row is written from Rust, on the scheduler, after a debounce:
objects owns the whole-object 'F'/'P'/'A' rows and indexer the
deep 'D' parts. Neither decides who may see a hit: the meta adapter derives
every row’s ACL columns from its source table in SQL, in the same transaction
as the write, overwriting whatever this crate passed in. The ACL values this
crate supplies are advisory — SQL is the authority, so a visibility flip
landing mid-debounce cannot be undone by the stale value that run carries.
§Module map
| Module | Responsibility |
|---|---|
rules | Manifest JSON → validated IndexRules / ActionSearchRules |
extract | serde_json::Value → plain text |
prune | Deleting manifest-named nodes from a document before extraction |
objects | File / profile / action → its one index row; the debounced per-object task |
indexer | Stored document → deep index rows; the debounced per-document task |
crdt | Yjs document → the same JSON shape RTDB exports |
reindex | Bulk sweeps: startup backfill, weekly cron, rules-changed |
[format] | /api/doc-formats handlers |
admin | POST /api/search/reindex |
handler | GET /api/search |
Re-exports§
pub use settings::register_settings;
Modules§
- admin
POST /api/search/reindex— rebuilding one tenant’s index by hand.- crdt
- Reading a Yjs/CRDT document as plain JSON, so the same index manifest works for CRDT apps (prezillo, ideallo, quillo, calcillo) as for RTDB ones.
- extract
- Turning arbitrary document JSON into indexable plain text.
- format
/api/doc-formats— which app indexes a document type, and how.- handler
GET /api/search— the single full-text query surface.- indexer
- Turning a stored document into
search_docsrows. - objects
- Whole-object index rows:
'F'files,'P'profiles,'A'actions. - prune
- Deleting manifest-named nodes from a document before extraction.
- reindex
- Bulk (re)indexing sweeps.
- rules
- The document-format index manifest: what an app declares it wants indexed.
- settings
- Search subsystem settings registration
Constants§
- INDEX_
REV - Revision of this build’s extraction semantics.
Statics§
- MATERIALIZE_
PERMIT - Serialises document materialisation across the whole process.
Functions§
- init
- Register the search subsystem’s scheduler tasks.
- schedule_
recurring - Schedule the recurring index maintenance.
- store_
text - Whether this tenant keeps the extracted plain text alongside its index.