Optionalembedding_model?: stringOptionalexplain?: booleanv3.11.6 (S-5) — when true, attach a per-hit SearchHitExplain
breakdown (RRF rank/score, graph-boost, reranker, and the opt-in recency /
feedback rank movement) to every returned hit. Diagnostic — adds a small
per-hit object and a few Maps while ranking. Absent/false ⇒ no explain
field and zero extra allocation (byte-identical to pre-S-5). Meaningful
only on the single-query path (searchHybridMulti re-fuses sub-query
lists, so per-stage ranks wouldn't map to the final order — it drops it).
Optionalfilter_frontmatter?: Record<string, FrontmatterFilterValue>v3.10 (rc.10) — optional frontmatter filter. A { key: value } map;
a hit is kept only if its note's YAML frontmatter satisfies EVERY pair
(AND across keys). Per key, the value matches by scalar-equality
(strings case-insensitive) or array-membership, and a filter value may
itself be an array for OR — see frontmatterMatches. A note with
no frontmatter, or missing a filtered key, is excluded (a filter is a
positive assertion). Absent ⇒ no filtering (byte-identical to pre-3.10).
Filtering happens on the fused candidate pool (already excluded-pruned),
so a strict filter may return fewer than limit hits — that's correct.
Optionalfolder?: stringOptionalgranularity?: "block" | "note"v2.2.0: "note" (default) returns 1 hit per note, picking the best chunk; "block" returns each chunk as a distinct hit so you see the multiple-paragraph case where one note covers a topic in two places.
Optionalgraph_boost?: booleanv2.3.0: post-RRF graph boost — rerank by counting how many other top-K hits link to each one. Default true; set false to disable for diagnostic comparison (e.g. measuring whether boost helped).
Optionallimit?: numberOptionalmin_signals?: numberPath to the .embed.db (file may or may not exist — checked at call time).
Optionalfeedback?: { scores: ReadonlyMap<string, number>; weight: number }v3.11.0 — optional opt-in closed-loop feedback re-ranking. When weight > 0
AND scores is non-empty, the final fused order is re-sorted by a blend of
relevance rank and each note's feedback score (useful/(useful+notUseful+1),
from obsidian_mark_useful via the FeedbackStore). weight = 0 / empty
scores is a provable no-op. Applied AFTER recency so a "human said this
helped" signal is the final tie-break. scores is keyed by relPath.
FTS5 index, if --persistent-index is enabled at server start.
Optionalhnsw?: HnswSearchContext | nullv2.13.0 — optional HNSW context for the embeddings-search arm. When passed, the embedding-side k-NN goes through the in-memory HNSW approximate nearest-neighbor index instead of the O(n) brute-force cosine in EmbedDb.search(). Built on serve start; lives in ServerDeps.hnswContext. Null/undefined → brute-force fallback.
Optionalrecency?: { staleDays: number; weight: number }v3.10 (rc.5) — optional opt-in recency re-ranking. When weight > 0,
the final fused order is re-sorted by a blend of relevance rank and the
note's live-mtime recency (see recencyScore in staleness.ts). weight = 0 (or
undefined) is a no-op — the default keeps ranking purely relevance-driven.
staleDays is the recency half-life (the age at which recency = 0.5).
Optionalreranker?: { alias?: string; topN?: number }v2.9.0 — optional cross-encoder reranker config. When set, the top-N hits from RRF (default 50) are re-scored by a BGE-style cross-encoder and re-sorted before truncation. Adds ~30-50ms per query on M1 CPU for a 50-candidate set.
alias resolves to a RERANKER_MODELS entry. topN defaults to 50.
Lazy-loaded from the local transformers.js cache. Runtime callers are
offline-enforced; pre-cache with enquire-mcp install-model <alias>.
Failures are swallowed and surface
via signal_errors.reranker so the whole search doesn't break on a
model load issue.
OptionalrerankerOverride?: { score(query: string, passages: readonly string[]): Promise<number[]> }v2.9.0 — test-only injection point. When set, this pre-loaded
reranker is used instead of lazy-loading via loadReranker(alias).
Lets unit tests validate the rerank-and-resort plumbing without
pulling in the real ML model. Unused in production callers.
v3.11.6-rc.7 (competitive-study C-4) — MULTI-QUERY FAN-OUT. Runs searchHybrid once per phrasing and RRF-merges the result LISTS by note path, so a note that ranks well for ANY phrasing floats to the top. Useful when the note may use different vocabulary than the caller's first phrasing (complements HyDE, which reshapes ONE query into an answer vector; this fuses N independent phrasings). RRF fuses RANKS, not scores, so the per-query reranker scores — explicitly "not comparable across queries" — are combined safely by position.
Each sub-query runs the full hybrid pipeline (BM25 + TF-IDF + embeddings + optional rerank/recency/feedback); cost scales linearly with the phrasing count, which is why the caller-facing schema caps it at MAX_FANOUT_QUERIES (the tool handler prepends the main
query, so the worst case is MAX_FANOUT_QUERIES + 1 = 9 full pipeline runs).Granularity caveat (rc.12): the fan-out fuses BY NOTE PATH (
idOf: h.path), sogranularity: "block"degrades to note-level in the fused result — one hit per note, not per chunk (chunk_indexreflects the best sub-hit and may be absent). Use single-query mode when per-chunk hits matter.