Skip to main content

Crate cloudillo_search

Crate cloudillo_search 

Source
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. indexer applies 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 search block. objects applies 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

ModuleResponsibility
rulesManifest JSON → validated IndexRules / ActionSearchRules
extractserde_json::Value → plain text
pruneDeleting manifest-named nodes from a document before extraction
objectsFile / profile / action → its one index row; the debounced per-object task
indexerStored document → deep index rows; the debounced per-document task
crdtYjs document → the same JSON shape RTDB exports
reindexBulk sweeps: startup backfill, weekly cron, rules-changed
[format]/api/doc-formats handlers
adminPOST /api/search/reindex
handlerGET /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_docs rows.
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.