1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
//! Narrow a lexical document hit down to the chunk that actually matched
//! (#1000).
//!
//! `documents_fts` holds one row per DOCUMENT, so `search_fts` answers "this
//! file is relevant" and nothing more. After #998 the vector half ranks chunks,
//! which left the two halves of hybrid search describing different units: RRF
//! still produces an ordering, but the lexical side contributes a coarser
//! signal than it should, and a document whose relevant passage sits late in
//! the file is under-served by exactly the half that should find an exact term
//! match.
//!
//! This refines a document hit to its best chunk rather than building a second
//! FTS index. Two reasons:
//!
//! - The chunk boundaries already exist and are deterministic. `chunks_for` is
//! the single place that decides them, so recomputing them from the body
//! yields the same pieces the embedder used, and both halves agree without
//! storing anything twice.
//! - qmd owns `documents_fts` through triggers on `documents`. Writing chunk
//! rows into it would couple us to a third-party schema, the same coupling
//! #998 deliberately avoided.
//!
//! Scoring reuses `section_rank`, so chunk-level lexical matching inherits the
//! stemming and diacritic folding rather than growing a second implementation
//! that drifts.
use crateSection;
use crateRanked;
/// The chunk of `body` that best matches `query`.
///
/// Returns the chunk text and its character offset, or `None` when the body is
/// empty or nothing scores above zero. `None` means the caller should fall back
/// to whatever it did before, not that the document is irrelevant: FTS already
/// judged relevance, this only decides WHERE.