Skip to content

feat: full-text search inverted index (jieba/CJK), indexing only#237

Merged
sufforest merged 1 commit into
mainfrom
wzy/search-index-infra
Jul 4, 2026
Merged

feat: full-text search inverted index (jieba/CJK), indexing only#237
sufforest merged 1 commit into
mainfrom
wzy/search-index-infra

Conversation

@sufforest

Copy link
Copy Markdown
Owner

First of a short series that gives vela real server-side search (POST /v3/search), closing the one genuine CS-API gap and the long-deferred CJK blocker.

This PR adds the indexing infrastructure only — the query path still uses the existing substring scan, so no user-facing change yet.

Why jieba + our own index. Synapse (English tsvector/porter) and conduwuit (whitespace split) can't segment Chinese, so CJK search doesn't work on either. Dendrite works via a separate Bleve index. vela keeps it in RocksDB (one store, one consistency/backup model) and uses jieba for real word segmentation — 你好世界 → 你好 / 世界.

What's here

  • vela-store::search: shared lazy jieba tokenizer + key/value codec; extracts body/name/topic per the spec's searchable keys.
  • search_index CF, key room_nid ++ token ++ 0xFF ++ stream_pos, value event_nid ++ field ++ tf. 0xFF is a safe separator (never valid UTF-8); stream_pos suffix gives free recency ordering.
  • Index-on-persist in persist_event_kind (same WriteBatch → atomic), de-index in mark_redacted_by; both gated on a runtime flag.
  • search_room_token: bounded, newest-first reverse-scan (no load-then-sort) for the query path.
  • !reindex-search admin command to backfill history; [search] enabled config (default on); jieba warmed at boot.

Tests: tokenizer (CJK segmentation, mixed, punctuation, term-frequency, over-long cap) + DB-backed round-trip (persist → prefix-scan → redact → gone, cross-room isolation, off-by-default + reindex).

Next: rewrite /search to use the index (typed request, tokenized AND query, rank/recent ordering, per-event history-visibility, skip E2EE).

adds the RocksDB-native index that POST /search will use: a `search_index`
CF keyed room+token+stream_pos, populated on the event write path and
dropped on redaction, tokenized with jieba so Chinese text is searchable
word-by-word (你好世界 -> 你好/世界) where synapse and conduwuit only
whitespace-split (and so can't match CJK at all).

- vela-store `search` module: shared lazy jieba tokenizer, key/value codec,
  searchable-field extraction (body/name/topic)
- index-on-persist inside persist_event_kind (same WriteBatch, atomic),
  de-index inside mark_redacted_by, both gated on a runtime flag
- search_room_token: bounded newest-first reverse-scan for the query path
- !reindex-search admin command + [search] enabled config (default on),
  jieba warmed at boot so the first message doesn't eat the dict load

the query path still uses the existing substring scan; switching it to the
index is the next change.
@sufforest sufforest merged commit 355a9af into main Jul 4, 2026
11 checks passed
@sufforest sufforest deleted the wzy/search-index-infra branch July 4, 2026 12:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant