import type { DatabaseHandle } from '../store/db.js'; import type { Embedder } from '../embeddings/types.js'; import type { SearchResult } from '../types.js'; /** * Extra fields attached to a SearchResult when the hit came from a * particular chunk. Kept optional on the base type so existing callers * that ignore them keep compiling. */ export interface ChunkAwareResult extends SearchResult { chunkId?: string; chunkHeading?: string | null; chunkStartLine?: number | null; chunkEndLine?: number | null; chunkExcerpt?: string; } export type SearchUnique = 'notes' | 'chunks'; export declare class Search { private db; private embedder; constructor(db: DatabaseHandle, embedder: Embedder); /** * Semantic search. Since v1.4.0 this is backed by chunks_vec — the * top-scoring chunk per note wins, and we return one row per note by * default. Falls back to the legacy note-level nodes_vec path if the * chunks table is empty (e.g. brand-new DB before the first index run). */ semantic(query: string, limit?: number): Promise; /** * Chunk-aware semantic search. Over-fetch raw chunk hits, then either * dedupe to one-per-note (unique='notes') or keep raw chunk rows * (unique='chunks'). Useful when the caller wants the exact heading + * line span that matched. */ semanticChunks(query: string, limit?: number, unique?: SearchUnique): Promise; fulltext(query: string, limit?: number): SearchResult[]; /** * Hybrid retrieval: run semantic + full-text in parallel, fuse ranks via * Reciprocal Rank Fusion (RRF). No tuning knobs — RRF is robust enough * out of the box that we make this the new default. * * When `unique === 'chunks'` the result objects include the chunk metadata * fields (`chunkId`, `chunkHeading`, `chunkStartLine`, `chunkEndLine`, * `chunkExcerpt`) from the best-scoring semantic chunk for each note. * The default (`unique === 'notes'`) behaviour is unchanged. */ hybrid(query: string, limit?: number, unique?: SearchUnique): Promise; } /** * Reciprocal Rank Fusion across an arbitrary number of ranked lists. * * RRF score for item `i` seen at rank `r` in any list: `1 / (k + r)`, summed * across lists. `k = 60` is Cormack, Clarke & Büttcher's tuned constant from * the original 2009 paper — good enough that our callers don't need to tune. * * Returns `{item, score}` ordered by descending score. `keyFn` maps each * item to the id used for deduping — callers pass `r => r.nodeId` for * note-level fusion. */ export declare function reciprocalRankFusion(lists: T[][], keyFn: (item: T) => string, k?: number): Array<{ item: T; score: number; }>;