~bigbes/sr-ht-spec

ref: bb3d22db80100b831492f6806f09f8bad3e141be sr-ht-spec/search/mixed_test.go -rw-r--r-- 8.6 KiB
e0938e94 — Eugene Blikh 27 days ago
refactor: make the filter-polarity trap inexpressible

service.SpaceFilter meant "empty membership selects nothing" — a newly
created project has no members — while search.Query.Spaces was a bare
[]core.SpaceRef whose empty case meant every space. Passing a project's
members into a query therefore turned an empty project into the whole
corpus: a silent scope inversion, invisible when it happens, and passing
every test written with a non-empty project.

The filter moves to core.SpaceFilter with unexported fields, and
search.Query takes it whole. There is no slice to hand over any more, so
the inversion cannot be written. Its three states are distinct: every
space (EverythingFilter), exactly these — possibly none — (SpacesFilter),
and the zero value, which is neither. Search refuses the zero value
rather than defaulting it, because both plausible defaults are wrong for
one of the two callers that can produce one, and returns no hits for a
filter that selects no space without asking the index.

service.SpaceFilter is now an alias for the core type, so ResolveProject
and its callers keep their names. Tests that built a Query without a
scope now say core.EverythingFilter(), which is what they always meant.
36b9e183 — Eugene Blikh 27 days ago
feat: search — one global bleve index with per-line ru/en routing

Absorbs warren's index/ and search/ packages, keyword half only, with the
three structural changes the design calls for:

- One global index, not one per project. Every document carries its space;
  a project is a term filter over that field (Query.Spaces), so a merge
  touches one index and the meta-project is a filter that excludes nothing.
- Rebuilds, not incremental updates. RebuildSpace replaces one space at a
  revision, RebuildAll replaces the corpus by building beside the live index
  and swapping. Both report duration in Stats.
- Keyword only. warren's vector store and RRF fusion are not ported, not
  even as dead code; Search returns ranked hits a later ranker can fuse.

Resolves the design's open mixed Russian/English question. Per-document
routing is not sufficient: the ru analyzer passes English through unstemmed
and vice versa, so a Russian spec quoting English requirements loses
singular/plural matching on whichever half is the minority. Text is routed
per line into ru- and en-analyzed field pairs and queried across both.

Not ported: chunking (an embedding concern, and the vector path is Phase 5),
the graph/pages/meta JSON artifacts, and the vault-commit shell-out.