weft

Community research — one round, before milestone 2

Date: 2026-08-13. Method: desk research over public sources — bleve source code (tag v2.6.0), GitHub issues across seven engines, vendor docs. What this is not: user interviews. The PRD’s evidence gap (“사용자 인터뷰 0건”) is narrowed, not closed.

Two questions were asked, matching the two unverified claims in the PRD’s Evidence section.


1. Is bleve’s fusion actually closed to new signal kinds?

The PRD assumed it; milestone 1 was told to verify it by reading code. Verified, with one correction.

Closed, structurally. bleve’s RRF/RSF path (v2.5.4+) fuses exactly 1 FTS stream + len(req.KNN) vector streams. The stream count is open — but only across kNN sub-queries; the stream kind is fixed at text-plus-vectors:

So a graph, recency or popularity signal cannot join bleve’s rank fusion as a first-class stream — which is the exact failure mode weft’s Scorer/Fuser design exists to remove.

The correction: “requires forking bleve” is overstated. Two escape hatches exist without touching internals:

  1. v2.6.0 shipped CustomScoreQuery (PR #2289) — a per-hit Go callback with doc-values access. It can fold a third signal into the FTS stream’s scores before fusion. The signal reshapes stream 0’s ranks; it does not get its own stream, weight, or rank list.
  2. The fusion package is exported and operates on public DocumentMatch fields. An application can run its own retrievals, populate ScoreBreakdown with arbitrary signals, and call N-way RRF itself — outside the engine pipeline, so pagination, facets and collection are then the app’s problem. That is fusion as a formula, not fusion as an architecture; it is also roughly the position weft’s Fuse starts from, minus the shared index and scorer contract.

Demand evidence inside bleve’s own tracker: #77 “Custom scoring” (2014, open), #396 “custom scoring function” (2016, open ~9 years), #620 “Boosting by freshness” (2017, open — exactly weft’s recency scorer), #1330 “how to change hit score” (2020, open, redirected to #396). The maintainers shipping custom_score in 2025 is supply responding to that demand — and also bleve actively closing its own gap.

2. Does anyone want pluggable ranking signals?

For. Explicit asks with the right shape exist across engines: OpenSearch k-NN #1271 (combine hybrid query with function_score — the exact three-signal composition), tantivy #815 (BM25 + vector weighted together; author hand-merges in middleware today), typesense #376 (ES-style function/decay scoring incl. recency), meilisearch discussion #548 (relevance vs favourite_count), ArcadeDB #4066 (fusion designed as “N ranked sub-pipelines, intentionally not tied to dense+sparse”). Vendors keep shipping partial answers (bleve #2289, OpenSearch neural-search #1152 RRF weights, Elastic linear retriever), which is the strongest signal the pressure is real.

Against — and this shapes positioning more than the “for” column.

3. Prior art — where an open N-signal ranking already exists

SystemN-signal fusion open?Embeddable library?Language
Lucene (FunctionScoreQuery + Expressions; TopDocs.rrf in 10.x)Yes — expression over _score + any doc-values fields; N-way RRF over arbitrary TopDocsYesJava
Vespa rank profilesYes — arbitrary expressions, phased rankingNo — server platformJava/C++
Elasticsearch retrievers / OpenSearch hybrid+LTRPartially — N child retrievers with weights, but retrievers must be engine query typesNo — serversJava
bleveNo — fixed FTS+kNN streams (see §1)YesGo
tantivyDIY — custom collectors over fast fields; no fusion primitiveYesRust
Qdrant / WeaviateSub-query fusion, engine-typed sub-queries onlyNo — serversRust/Go
ParadeDB / SQLite FTS5Yes at the SQL layer — signals as columnsNot for Go apps / C via cgoSQL/C

Lucene is the finding that matters. Adding a ranking signal in embeddable Lucene = index a doc-values field + edit an expression — no engine code touched. The capability weft is building is not novel; it has existed for years, in Java. What does not exist is the Go equivalent: bleve is closed (§1), bluge is unmaintained (custom-score example exists, repo dormant since ~2022), riot archived, zinc/blast/phalanx are servers.

4. What this changes for weft

  1. The architecture bet survives, sharpened. Honest positioning: “Lucene-Expressions-class open ranking, in Go, with fusion-native signals” — a language-and-design gap, not a capability-first invention. The PRD’s Problem statement (“fusion is always the special case”) is now backed by bleve’s actual source, not assumption.
  2. New competitive risk, worth tracking: bleve is moving (v2.5.4 fusion, v2.6.0 custom_score, within ~a year). If bleve ever exposes fusion streams as an interface, weft’s differentiator thins to from-scratch purity. Watch bleve releases at each weft milestone.
  3. Demand realism: the buyer for “N pluggable signals” is narrower than the buyer for “hybrid search” — it is exactly the PRD’s stated primary user (infrastructure contributors extending ranking in-process), and that population’s revealed alternative today is middleware-side merging. Milestone 6 docs should speak to the person currently hand-rolling RRF in app code.
  4. Interviews remain undone. This round was desk research. The PRD risk row stays open with its mitigation updated; the next research round should be conversations, not archaeology.

Caveats