@dikolab/kbdb 0.1.5 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -9
- package/dist/cli.cjs +181 -50
- package/dist/cli.cjs.map +3 -3
- package/dist/cli.mjs +181 -50
- package/dist/cli.mjs.map +3 -3
- package/dist/kbdb-worker.cjs +283 -29
- package/dist/kbdb-worker.cjs.map +4 -4
- package/dist/mod.cjs +13 -2
- package/dist/mod.cjs.map +2 -2
- package/dist/mod.mjs +13 -2
- package/dist/mod.mjs.map +2 -2
- package/dist/src/shared/cli/functions/format-output.function.d.ts +7 -7
- package/dist/src/shared/cli/functions/run-search.function.d.ts +25 -16
- package/dist/src/shared/cli/index.d.ts +1 -1
- package/dist/src/shared/cli/typings/cli-options.interface.d.ts +2 -0
- package/dist/src/shared/cli/typings/search-display.model.d.ts +25 -7
- package/dist/src/shared/mcp/functions/handle-recall.function.d.ts +29 -0
- package/dist/src/shared/mcp/typings/mcp-client.interface.d.ts +30 -11
- package/dist/src/shared/version/constants/version.constant.d.ts +1 -1
- package/dist/src/shared/wasm-codec/functions/decode-recall-result.function.d.ts +28 -0
- package/dist/src/shared/wasm-codec/functions/decode-scored-results.function.d.ts +14 -7
- package/dist/src/shared/wasm-codec/functions/encode-recall-params.function.d.ts +21 -0
- package/dist/src/shared/wasm-codec/functions/encode-search-params.function.d.ts +1 -1
- package/dist/src/shared/wasm-codec/index.d.ts +5 -2
- package/dist/src/shared/worker-client/classes/worker-client.class.d.ts +13 -3
- package/dist/src/shared/worker-client/index.d.ts +2 -1
- package/dist/src/shared/worker-client/typings/recall-result.model.d.ts +94 -0
- package/dist/src/shared/worker-client/typings/search-params.model.d.ts +2 -0
- package/dist/src/shared/worker-client/typings/search-result.model.d.ts +23 -7
- package/dist/src/shared/worker-daemon/classes/worker-daemon.class.d.ts +1 -0
- package/dist/wasm/fs-database/kbdb_fs_database.d.ts +18 -0
- package/dist/wasm/fs-database/kbdb_fs_database.js +30 -0
- package/dist/wasm/fs-database/kbdb_fs_database_bg.wasm +0 -0
- package/dist/wasm/fs-database/kbdb_fs_database_bg.wasm.d.ts +2 -0
- package/dist/wasm/kb-worker/kbdb_worker.d.ts +38 -0
- package/dist/wasm/kb-worker/kbdb_worker.js +56 -0
- package/dist/wasm/kb-worker/kbdb_worker_bg.wasm +0 -0
- package/dist/wasm/kb-worker/kbdb_worker_bg.wasm.d.ts +3 -0
- package/dist/worker.mjs +283 -29
- package/dist/worker.mjs.map +4 -4
- package/docs/details/README.md +26 -0
- package/docs/details/cli.md +608 -0
- package/docs/details/library-api.md +406 -0
- package/docs/details/mcp-server.md +430 -0
- package/docs/details/search-and-ranking.md +457 -0
- package/docs/details/storage.md +529 -0
- package/docs/goals/agents.md +751 -0
- package/docs/goals/architecture.svg +198 -0
- package/docs/goals/cli.md +2308 -0
- package/docs/goals/content-composer.md +609 -0
- package/docs/goals/content-parser.md +279 -0
- package/docs/goals/database.md +1452 -0
- package/docs/goals/document.md +368 -0
- package/docs/goals/mcp.md +1467 -0
- package/docs/goals/overview.md +124 -0
- package/docs/goals/query-parser.md +271 -0
- package/docs/goals/query-result.md +693 -0
- package/docs/goals/skills.md +738 -0
- package/docs/goals/worker-client.md +944 -0
- package/docs/goals/worker-daemon.md +1447 -0
- package/docs/modules/overview.md +298 -0
- package/docs/modules/rust/fs-database/functions/document.md +48 -0
- package/docs/modules/rust/fs-database/functions/integrity.md +68 -0
- package/docs/modules/rust/fs-database/functions/io.md +87 -0
- package/docs/modules/rust/fs-database/functions/module.md +22 -0
- package/docs/modules/rust/fs-database/functions/query-exec.md +44 -0
- package/docs/modules/rust/fs-database/functions/section.md +59 -0
- package/docs/modules/rust/fs-database/indexes/btree.md +35 -0
- package/docs/modules/rust/fs-database/indexes/corpus.md +61 -0
- package/docs/modules/rust/fs-database/indexes/inverted.md +60 -0
- package/docs/modules/rust/fs-database/indexes/module.md +64 -0
- package/docs/modules/rust/fs-database/indexes/positional.md +39 -0
- package/docs/modules/rust/fs-database/module.md +149 -0
- package/docs/modules/rust/fs-database/types/catalog.md +53 -0
- package/docs/modules/rust/fs-database/types/module.md +12 -0
- package/docs/modules/rust/fs-database/types/section.md +101 -0
- package/docs/modules/rust/kb-worker/functions/compose.md +57 -0
- package/docs/modules/rust/kb-worker/functions/module.md +20 -0
- package/docs/modules/rust/kb-worker/functions/retrieve.md +194 -0
- package/docs/modules/rust/kb-worker/functions/search.md +173 -0
- package/docs/modules/rust/kb-worker/functions/write.md +172 -0
- package/docs/modules/rust/kb-worker/module.md +171 -0
- package/docs/modules/rust/kb-worker/types/cache.md +90 -0
- package/docs/modules/rust/kb-worker/types/context.md +26 -0
- package/docs/modules/rust/kb-worker/types/module.md +16 -0
- package/docs/modules/rust/kb-worker/types/status.md +29 -0
- package/docs/modules/rust/kb-worker/types/token.md +50 -0
- package/docs/modules/rust/overview.md +495 -0
- package/docs/modules/rust/query-parser/functions/extract.md +56 -0
- package/docs/modules/rust/query-parser/functions/module.md +16 -0
- package/docs/modules/rust/query-parser/functions/parse.md +33 -0
- package/docs/modules/rust/query-parser/functions/pipeline.md +103 -0
- package/docs/modules/rust/query-parser/functions/rank.md +85 -0
- package/docs/modules/rust/query-parser/module.md +136 -0
- package/docs/modules/rust/query-parser/types/cache.md +39 -0
- package/docs/modules/rust/query-parser/types/keyphrase.md +60 -0
- package/docs/modules/rust/query-parser/types/module.md +17 -0
- package/docs/modules/rust/query-parser/types/token.md +42 -0
- package/docs/modules/rust/shared/content-composer/functions.md +114 -0
- package/docs/modules/rust/shared/content-composer/module.md +147 -0
- package/docs/modules/rust/shared/content-composer/types.md +93 -0
- package/docs/modules/rust/shared/content-parser/functions.md +72 -0
- package/docs/modules/rust/shared/content-parser/module.md +99 -0
- package/docs/modules/rust/shared/content-parser/types.md +71 -0
- package/docs/modules/rust/shared/corpus/module.md +24 -0
- package/docs/modules/rust/shared/corpus/types.md +141 -0
- package/docs/modules/rust/shared/document/module.md +25 -0
- package/docs/modules/rust/shared/document/types.md +154 -0
- package/docs/modules/rust/shared/encode/module.md +22 -0
- package/docs/modules/rust/shared/encode/traits.md +304 -0
- package/docs/modules/rust/shared/error/module.md +28 -0
- package/docs/modules/rust/shared/error/types.md +302 -0
- package/docs/modules/rust/shared/kbid/module.md +24 -0
- package/docs/modules/rust/shared/kbid/types.md +147 -0
- package/docs/modules/rust/shared/memory/functions.md +209 -0
- package/docs/modules/rust/shared/memory/module.md +24 -0
- package/docs/modules/rust/shared/module.md +168 -0
- package/docs/modules/rust/shared/query/module.md +27 -0
- package/docs/modules/rust/shared/query/types.md +236 -0
- package/docs/modules/rust/shared/section/module.md +25 -0
- package/docs/modules/rust/shared/section/types.md +294 -0
- package/docs/modules/rust/shared/term/module.md +25 -0
- package/docs/modules/rust/shared/term/types.md +139 -0
- package/docs/modules/typescript/cli.md +129 -0
- package/docs/modules/typescript/kbdb-worker.md +135 -0
- package/docs/modules/typescript/overview.md +378 -0
- package/docs/modules/typescript/shared/cli/constants.md +23 -0
- package/docs/modules/typescript/shared/cli/functions.md +630 -0
- package/docs/modules/typescript/shared/cli/module.md +125 -0
- package/docs/modules/typescript/shared/cli/typings.md +172 -0
- package/docs/modules/typescript/shared/hash/functions.md +20 -0
- package/docs/modules/typescript/shared/hash/module.md +21 -0
- package/docs/modules/typescript/shared/mcp/classes.md +179 -0
- package/docs/modules/typescript/shared/mcp/functions.md +386 -0
- package/docs/modules/typescript/shared/mcp/module.md +160 -0
- package/docs/modules/typescript/shared/mcp/typings.md +522 -0
- package/docs/modules/typescript/shared/module.md +94 -0
- package/docs/modules/typescript/shared/platform/functions.md +42 -0
- package/docs/modules/typescript/shared/platform/module.md +25 -0
- package/docs/modules/typescript/shared/runtime/functions.md +26 -0
- package/docs/modules/typescript/shared/runtime/module.md +27 -0
- package/docs/modules/typescript/shared/runtime/typings.md +13 -0
- package/docs/modules/typescript/shared/version/module.md +27 -0
- package/docs/modules/typescript/shared/wasm-codec/functions.md +391 -0
- package/docs/modules/typescript/shared/wasm-codec/module.md +98 -0
- package/docs/modules/typescript/shared/worker-client/classes.md +264 -0
- package/docs/modules/typescript/shared/worker-client/functions.md +175 -0
- package/docs/modules/typescript/shared/worker-client/module.md +84 -0
- package/docs/modules/typescript/shared/worker-client/typings.md +508 -0
- package/docs/modules/typescript/shared/worker-daemon/classes.md +239 -0
- package/docs/modules/typescript/shared/worker-daemon/constants.md +37 -0
- package/docs/modules/typescript/shared/worker-daemon/functions.md +221 -0
- package/docs/modules/typescript/shared/worker-daemon/module.md +112 -0
- package/docs/modules/typescript/shared/worker-daemon/typings.md +126 -0
- package/docs/overview.md +188 -0
- package/docs/release-notes/0.1.0.md +189 -0
- package/docs/release-notes/0.1.1.md +46 -0
- package/docs/release-notes/0.1.2.md +38 -0
- package/docs/release-notes/0.1.3.md +42 -0
- package/docs/release-notes/0.2.0.md +147 -0
- package/docs/release-notes/README.md +9 -0
- package/package.json +8 -2
- package/src/shared/cli/functions/dispatch-command.function.ts +3 -3
- package/src/shared/cli/functions/format-output.function.ts +41 -34
- package/src/shared/cli/functions/parse-args.function.ts +7 -0
- package/src/shared/cli/functions/run-search.function.ts +53 -27
- package/src/shared/cli/index.ts +4 -1
- package/src/shared/cli/typings/cli-options.interface.ts +2 -0
- package/src/shared/cli/typings/search-display.model.ts +26 -7
- package/src/shared/mcp/functions/create-tool-definitions.function.ts +64 -4
- package/src/shared/mcp/functions/handle-recall.function.ts +66 -0
- package/src/shared/mcp/functions/handle-tool-call.function.ts +11 -1
- package/src/shared/mcp/typings/mcp-client.interface.ts +33 -15
- package/src/shared/version/constants/version.constant.ts +1 -1
- package/src/shared/wasm-codec/functions/decode-recall-result.function.ts +267 -0
- package/src/shared/wasm-codec/functions/decode-scored-results.function.ts +81 -27
- package/src/shared/wasm-codec/functions/encode-recall-params.function.ts +68 -0
- package/src/shared/wasm-codec/functions/encode-search-params.function.ts +4 -1
- package/src/shared/wasm-codec/index.ts +11 -3
- package/src/shared/worker-client/classes/worker-client.class.ts +24 -4
- package/src/shared/worker-client/index.ts +11 -1
- package/src/shared/worker-client/typings/recall-result.model.ts +98 -0
- package/src/shared/worker-client/typings/search-params.model.ts +2 -0
- package/src/shared/worker-client/typings/search-result.model.ts +24 -7
- package/src/shared/worker-daemon/classes/worker-daemon.class.ts +25 -4
- package/src/wasm/fs-database/kbdb_fs_database.d.ts +18 -0
- package/src/wasm/fs-database/kbdb_fs_database.js +30 -0
- package/src/wasm/fs-database/kbdb_fs_database_bg.wasm +0 -0
- package/src/wasm/fs-database/kbdb_fs_database_bg.wasm.d.ts +2 -0
- package/src/wasm/kb-worker/kbdb_worker.d.ts +38 -0
- package/src/wasm/kb-worker/kbdb_worker.js +56 -0
- package/src/wasm/kb-worker/kbdb_worker_bg.wasm +0 -0
- package/src/wasm/kb-worker/kbdb_worker_bg.wasm.d.ts +3 -0
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
# How Search Works
|
|
2
|
+
|
|
3
|
+
kbdb uses field-weighted BM25F ranking to score search
|
|
4
|
+
results by relevance. Sections are indexed across
|
|
5
|
+
three fields -- heading, body, and code -- each with a
|
|
6
|
+
different weight so that a match in a title counts
|
|
7
|
+
more than a match buried in body text. Queries
|
|
8
|
+
support simple keyword searches, exact phrases,
|
|
9
|
+
boolean operators, and term exclusions.
|
|
10
|
+
|
|
11
|
+
## Query Syntax
|
|
12
|
+
|
|
13
|
+
Queries are plain text with optional operators.
|
|
14
|
+
The query parser tokenizes and normalizes query
|
|
15
|
+
terms through the same pipeline used for indexing,
|
|
16
|
+
so search terms match consistently regardless of
|
|
17
|
+
case or inflection.
|
|
18
|
+
|
|
19
|
+
### Examples
|
|
20
|
+
|
|
21
|
+
| Query | Meaning |
|
|
22
|
+
|---------------------|----------------------------------|
|
|
23
|
+
| `auth flow` | Sections containing both terms |
|
|
24
|
+
| `"error handling"` | Exact phrase match |
|
|
25
|
+
| `auth -oauth` | "auth" but not "oauth" |
|
|
26
|
+
| `auth AND token` | Both terms required |
|
|
27
|
+
| `cache OR redis` | Either term matches |
|
|
28
|
+
|
|
29
|
+
### Syntax Reference
|
|
30
|
+
|
|
31
|
+
| Syntax | Effect |
|
|
32
|
+
|-----------------|------------------------------------|
|
|
33
|
+
| `word` | Match normalized/stemmed form |
|
|
34
|
+
| `"two words"` | Match exact phrase in order |
|
|
35
|
+
| `-word` | Exclude sections containing term |
|
|
36
|
+
| `AND` | Both operands required |
|
|
37
|
+
| `OR` | Either operand sufficient |
|
|
38
|
+
|
|
39
|
+
Operators can be combined. Unquoted multi-word
|
|
40
|
+
queries are implicitly ANDed -- `auth flow` matches
|
|
41
|
+
sections containing both "auth" and "flow".
|
|
42
|
+
|
|
43
|
+
## Tokenization Pipeline
|
|
44
|
+
|
|
45
|
+
Both indexing and search queries pass through the
|
|
46
|
+
same eight-stage pipeline. Using identical processing
|
|
47
|
+
for both sides guarantees that stored terms match
|
|
48
|
+
query terms.
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
Raw text
|
|
52
|
+
|
|
|
53
|
+
v
|
|
54
|
+
1. Strip content markers
|
|
55
|
+
|
|
|
56
|
+
v
|
|
57
|
+
2. Unicode NFC normalization
|
|
58
|
+
|
|
|
59
|
+
v
|
|
60
|
+
3. Word boundary detection
|
|
61
|
+
|
|
|
62
|
+
v
|
|
63
|
+
4. Identifier splitting
|
|
64
|
+
|
|
|
65
|
+
v
|
|
66
|
+
5. Case folding
|
|
67
|
+
|
|
|
68
|
+
v
|
|
69
|
+
6. Stop word removal
|
|
70
|
+
|
|
|
71
|
+
v
|
|
72
|
+
7. Stemming
|
|
73
|
+
|
|
|
74
|
+
v
|
|
75
|
+
8. N-gram extraction
|
|
76
|
+
|
|
|
77
|
+
v
|
|
78
|
+
Normalized tokens
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
1. **Marker stripping** -- Remove all six content
|
|
82
|
+
marker types (`$(image:)`, `$(code:)`, `$(text:)`,
|
|
83
|
+
`$(document:)`, `$(link:)`, `$(doc-link:)`) before
|
|
84
|
+
processing. Markers are structural references, not
|
|
85
|
+
searchable content.
|
|
86
|
+
|
|
87
|
+
2. **Unicode NFC normalization** -- Compose decomposed
|
|
88
|
+
Unicode sequences into their canonical form. For
|
|
89
|
+
example, `e` + combining acute accent becomes the
|
|
90
|
+
single codepoint `e` (U+00E9). Ensures equivalent
|
|
91
|
+
Unicode representations produce the same tokens.
|
|
92
|
+
|
|
93
|
+
3. **Word boundary detection** -- Split text at
|
|
94
|
+
Unicode-defined word boundaries using the Unicode
|
|
95
|
+
Segmentation algorithm. Handles punctuation,
|
|
96
|
+
whitespace, and mixed-script text correctly.
|
|
97
|
+
|
|
98
|
+
4. **Identifier splitting** -- Decompose programming
|
|
99
|
+
identifiers into constituent words.
|
|
100
|
+
`camelCase` becomes `["camel", "case"]`;
|
|
101
|
+
`snake_case` becomes `["snake", "case"]`;
|
|
102
|
+
`PascalCase` becomes `["pascal", "case"]`.
|
|
103
|
+
This stage makes code searchable by individual
|
|
104
|
+
words within identifiers.
|
|
105
|
+
|
|
106
|
+
5. **Case folding** -- Convert all tokens to
|
|
107
|
+
lowercase. `HTTP`, `Http`, and `http` all become
|
|
108
|
+
`http`.
|
|
109
|
+
|
|
110
|
+
6. **Stop word removal** -- Remove common words with
|
|
111
|
+
no discriminative value (`the`, `is`, `and`,
|
|
112
|
+
etc.). The stop word list is configurable.
|
|
113
|
+
|
|
114
|
+
7. **Stemming** -- Reduce words to root forms using
|
|
115
|
+
the Snowball/Porter2 stemmer. `running` and `runs`
|
|
116
|
+
both stem to `run`. Both the stemmed form and the
|
|
117
|
+
original are stored so exact-match queries still
|
|
118
|
+
work.
|
|
119
|
+
|
|
120
|
+
8. **N-gram extraction** -- Generate bigrams and
|
|
121
|
+
trigrams for phrase indexing and multi-word term
|
|
122
|
+
matching. Supports phrase queries without requiring
|
|
123
|
+
a separate phrase index.
|
|
124
|
+
|
|
125
|
+
### Per-Field Processing
|
|
126
|
+
|
|
127
|
+
During indexing, the pipeline runs once per field for
|
|
128
|
+
each section:
|
|
129
|
+
|
|
130
|
+
| Content source | Field |
|
|
131
|
+
|-------------------------|-----------|
|
|
132
|
+
| `title` attribute | heading |
|
|
133
|
+
| Body text + description | body |
|
|
134
|
+
| Code content | code |
|
|
135
|
+
|
|
136
|
+
Per-field term frequencies are stored separately in
|
|
137
|
+
the inverted index. This separation is what enables
|
|
138
|
+
BM25F field weights to be tuned at query time without
|
|
139
|
+
rebuilding the indexes.
|
|
140
|
+
|
|
141
|
+
## BM25F Ranking
|
|
142
|
+
|
|
143
|
+
BM25F (Field-Weighted Okapi BM25) is the primary
|
|
144
|
+
ranking function. It is a probabilistic scoring model
|
|
145
|
+
from information retrieval that balances three
|
|
146
|
+
factors: how often a term appears in a section, how
|
|
147
|
+
rare the term is across the corpus, and how long the
|
|
148
|
+
section is relative to average.
|
|
149
|
+
|
|
150
|
+
### The Three Fields
|
|
151
|
+
|
|
152
|
+
Each section is indexed across three fields, each
|
|
153
|
+
with a default weight:
|
|
154
|
+
|
|
155
|
+
| Field | Weight | Content source |
|
|
156
|
+
|-----------|--------|----------------------------|
|
|
157
|
+
| `heading` | 2.0x | Section `title` attribute |
|
|
158
|
+
| `body` | 1.0x | Body text + `description` |
|
|
159
|
+
| `code` | 1.5x | Code section content |
|
|
160
|
+
|
|
161
|
+
A term match in a heading contributes twice as much
|
|
162
|
+
to the score as the same match in body text. A match
|
|
163
|
+
in code contributes 1.5 times as much. This reflects
|
|
164
|
+
the intuition that titles are the strongest signal of
|
|
165
|
+
what a section is about.
|
|
166
|
+
|
|
167
|
+
### How Scoring Works
|
|
168
|
+
|
|
169
|
+
For each query term `t` matched in section `d`:
|
|
170
|
+
|
|
171
|
+
1. **IDF (Inverse Document Frequency)** -- Measures
|
|
172
|
+
how rare the term is across all sections. Common
|
|
173
|
+
terms like "function" get a low IDF; rare terms
|
|
174
|
+
like "kademlia" get a high IDF.
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
idf(t) = ln((N - df(t) + 0.5) / (df(t) + 0.5) + 1)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
`N` = total indexed sections, `df(t)` = number of
|
|
181
|
+
sections containing `t`.
|
|
182
|
+
|
|
183
|
+
2. **Per-field score** -- For each field `f` (heading,
|
|
184
|
+
body, code), compute a BM25 score using that
|
|
185
|
+
field's term frequency and average length:
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
score_f(t, d) = idf(t)
|
|
189
|
+
* (tf_f(t,d) * (k1 + 1))
|
|
190
|
+
/ (tf_f(t,d) + k1 * (1 - b + b * |d_f| / avgdl_f))
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Where:
|
|
194
|
+
- `tf_f(t,d)` = term frequency in field `f`
|
|
195
|
+
- `|d_f|` = token count of field `f` in section `d`
|
|
196
|
+
- `avgdl_f` = average token count of field `f`
|
|
197
|
+
across the corpus
|
|
198
|
+
- `k1` = 1.2 (term saturation parameter)
|
|
199
|
+
- `b` = 0.75 (length normalization parameter)
|
|
200
|
+
|
|
201
|
+
3. **Weighted combination** -- Multiply each field
|
|
202
|
+
score by its weight and sum:
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
bm25f(t, d) = 2.0 * score_heading(t, d)
|
|
206
|
+
+ 1.0 * score_body(t, d)
|
|
207
|
+
+ 1.5 * score_code(t, d)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
4. **Total section score** -- Sum `bm25f(t, d)`
|
|
211
|
+
across all matched query terms.
|
|
212
|
+
|
|
213
|
+
### Key Parameters
|
|
214
|
+
|
|
215
|
+
| Parameter | Default | Effect |
|
|
216
|
+
|------------------|---------|---------------------------|
|
|
217
|
+
| `k1` | 1.2 | Term saturation. Higher = more weight to repeated terms |
|
|
218
|
+
| `b` | 0.75 | Length normalization. 0 = none, 1 = full |
|
|
219
|
+
| `heading` weight | 2.0 | Title match influence |
|
|
220
|
+
| `body` weight | 1.0 | Body text match influence |
|
|
221
|
+
| `code` weight | 1.5 | Code match influence |
|
|
222
|
+
|
|
223
|
+
### Why Per-Field Storage Matters
|
|
224
|
+
|
|
225
|
+
Per-field term frequencies (`heading_tf`, `body_tf`,
|
|
226
|
+
`code_tf`) are stored separately in the inverted
|
|
227
|
+
index rather than as a single aggregated value. This
|
|
228
|
+
means field weights can be changed at query time --
|
|
229
|
+
for example, boosting code matches to 3.0x for a
|
|
230
|
+
code-focused search -- without rebuilding any index
|
|
231
|
+
files.
|
|
232
|
+
|
|
233
|
+
### Per-Field Length Normalization
|
|
234
|
+
|
|
235
|
+
Corpus statistics store per-field average lengths
|
|
236
|
+
(`avg_heading_length`, `avg_body_length`,
|
|
237
|
+
`avg_code_length`) alongside overall averages. This
|
|
238
|
+
prevents unfair comparisons: a 3-word title is
|
|
239
|
+
normalized against the average title length, not
|
|
240
|
+
against the average body length of 500 words.
|
|
241
|
+
|
|
242
|
+
## Supplementary Scoring Signals
|
|
243
|
+
|
|
244
|
+
Three additional signals are added to the BM25F base
|
|
245
|
+
score:
|
|
246
|
+
|
|
247
|
+
### Proximity Boost
|
|
248
|
+
|
|
249
|
+
Boosts sections where query terms appear close
|
|
250
|
+
together. For multi-term queries, the proximity
|
|
251
|
+
scorer computes the minimum distance between each
|
|
252
|
+
pair of query terms using their stored positions. A
|
|
253
|
+
smaller span produces a higher boost. Single-term
|
|
254
|
+
queries receive no proximity boost.
|
|
255
|
+
|
|
256
|
+
```
|
|
257
|
+
final proximity = inverse of minimum span
|
|
258
|
+
across all query term pairs
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Zone Scoring
|
|
262
|
+
|
|
263
|
+
Structural weighting based on position within a
|
|
264
|
+
section. Terms in the first ~20% of a section (the
|
|
265
|
+
"heading zone") receive a higher weight than terms in
|
|
266
|
+
the rest of the body. The zone boundary is computed
|
|
267
|
+
as `0.2 * token_count` using the pre-computed total
|
|
268
|
+
token count. This approximates the idea that
|
|
269
|
+
headings and opening paragraphs carry more topical
|
|
270
|
+
signal.
|
|
271
|
+
|
|
272
|
+
### Freshness Weighting (Optional)
|
|
273
|
+
|
|
274
|
+
Time-decay boost that favors recently added sections.
|
|
275
|
+
Uses exponential decay with a configurable half-life:
|
|
276
|
+
after one half-life period, the freshness score is
|
|
277
|
+
halved. Can be disabled entirely by omitting the
|
|
278
|
+
freshness weight from score combination.
|
|
279
|
+
|
|
280
|
+
### Score Combination
|
|
281
|
+
|
|
282
|
+
```
|
|
283
|
+
final_score = bm25f_score
|
|
284
|
+
+ proximity_weight * proximity_score
|
|
285
|
+
+ zone_weight * zone_score
|
|
286
|
+
+ freshness_weight * freshness_score
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
BM25F is always the dominant signal. Proximity, zone,
|
|
290
|
+
and freshness are additive boosts with configurable
|
|
291
|
+
weights. Results are sorted by `final_score`
|
|
292
|
+
descending; ties broken by kbid lexicographic order.
|
|
293
|
+
|
|
294
|
+
## Keyword Extraction
|
|
295
|
+
|
|
296
|
+
When content is added to the knowledge base, the
|
|
297
|
+
system extracts searchable terms and computes
|
|
298
|
+
weights using statistical methods that require no
|
|
299
|
+
training data or external models.
|
|
300
|
+
|
|
301
|
+
### RAKE (Rapid Automatic Keyword Extraction)
|
|
302
|
+
|
|
303
|
+
Splits text on stop words to identify candidate
|
|
304
|
+
keyphrases. Scores candidates by word degree divided
|
|
305
|
+
by frequency ratio -- multi-word phrases with
|
|
306
|
+
infrequent component words score highest. Runs on
|
|
307
|
+
both text and code content.
|
|
308
|
+
|
|
309
|
+
### N-Gram Scoring
|
|
310
|
+
|
|
311
|
+
Bigrams and trigrams are scored by co-occurrence
|
|
312
|
+
frequency. High-frequency pairs and triples become
|
|
313
|
+
indexed terms, supporting multi-word searches without
|
|
314
|
+
exact phrase syntax.
|
|
315
|
+
|
|
316
|
+
### TF-IDF Term Weighting
|
|
317
|
+
|
|
318
|
+
For each section, per-field term frequency (TF) is
|
|
319
|
+
computed separately for heading, body, and code
|
|
320
|
+
fields. These per-field TFs are combined with
|
|
321
|
+
corpus-wide inverse document frequency (IDF) to
|
|
322
|
+
produce weights reflecting each term's
|
|
323
|
+
discriminative value. The per-field TFs are stored in
|
|
324
|
+
the inverted index; IDF is computed from corpus
|
|
325
|
+
statistics at query time.
|
|
326
|
+
|
|
327
|
+
### What Gets Indexed
|
|
328
|
+
|
|
329
|
+
| Section type | Body indexed? | Title indexed? | Description indexed? |
|
|
330
|
+
|----------------|---------------|----------------|----------------------|
|
|
331
|
+
| `text/*` | Yes (body) | Yes (heading) | Yes (body) |
|
|
332
|
+
| `image/*` | No | Yes (heading) | Yes (body) |
|
|
333
|
+
|
|
334
|
+
Image binary content is skipped entirely. Image
|
|
335
|
+
sections are searchable only through their `title`
|
|
336
|
+
(required) and `description` (optional) attributes.
|
|
337
|
+
|
|
338
|
+
## Index Structure
|
|
339
|
+
|
|
340
|
+
All index files live in `.kbdb/indexes/`. They are
|
|
341
|
+
binary, derived, and rebuildable from source data.
|
|
342
|
+
The `.gitignore` at the database root excludes them
|
|
343
|
+
from version control.
|
|
344
|
+
|
|
345
|
+
```
|
|
346
|
+
indexes/
|
|
347
|
+
+-- meta.toml human-readable metadata
|
|
348
|
+
+-- terms.idx inverted index (B+ tree)
|
|
349
|
+
+-- positions.idx positional index (B+ tree)
|
|
350
|
+
+-- corpus.dat corpus statistics (binary)
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### `terms.idx` -- Inverted Index
|
|
354
|
+
|
|
355
|
+
B+ tree mapping normalized terms to posting lists.
|
|
356
|
+
Each posting entry contains:
|
|
357
|
+
|
|
358
|
+
| Field | Type | Description |
|
|
359
|
+
|--------------|-------|----------------------------|
|
|
360
|
+
| `kbid` | bytes | Section identifier |
|
|
361
|
+
| `heading_tf` | f32 | Term frequency in heading |
|
|
362
|
+
| `body_tf` | f32 | Term frequency in body |
|
|
363
|
+
| `code_tf` | f32 | Term frequency in code |
|
|
364
|
+
|
|
365
|
+
Supports term lookup, IDF computation (via document
|
|
366
|
+
frequency counts), and per-field term frequency
|
|
367
|
+
retrieval. O(log n) lookup via B+ tree structure.
|
|
368
|
+
|
|
369
|
+
### `positions.idx` -- Positional Index
|
|
370
|
+
|
|
371
|
+
B+ tree mapping `(term, kbid)` pairs to arrays of
|
|
372
|
+
zero-based token offsets. Supports three functions:
|
|
373
|
+
|
|
374
|
+
- **Phrase queries** -- Verify that terms appear in
|
|
375
|
+
the correct order at consecutive positions.
|
|
376
|
+
- **Proximity scoring** -- Compute minimum span
|
|
377
|
+
between query terms.
|
|
378
|
+
- **Zone scoring** -- Determine whether a term falls
|
|
379
|
+
in the heading zone (first 20% of the section).
|
|
380
|
+
|
|
381
|
+
### `corpus.dat` -- Corpus Statistics
|
|
382
|
+
|
|
383
|
+
Binary file storing corpus-wide aggregates:
|
|
384
|
+
|
|
385
|
+
| Field | Type | Description |
|
|
386
|
+
|-----------------------|------|--------------------------|
|
|
387
|
+
| `total_sections` | u32 | Indexed section count |
|
|
388
|
+
| `total_terms` | u32 | Distinct term count |
|
|
389
|
+
| `avg_section_length` | f32 | Average tokens/section |
|
|
390
|
+
| `avg_heading_length` | f32 | Average heading tokens |
|
|
391
|
+
| `avg_body_length` | f32 | Average body tokens |
|
|
392
|
+
| `avg_code_length` | f32 | Average code tokens |
|
|
393
|
+
| per-term doc freqs | -- | Per-term section counts |
|
|
394
|
+
|
|
395
|
+
Used for IDF computation and per-field BM25 length
|
|
396
|
+
normalization.
|
|
397
|
+
|
|
398
|
+
### `meta.toml` -- Index Metadata
|
|
399
|
+
|
|
400
|
+
Human-readable TOML file with informational metadata
|
|
401
|
+
about the indexes:
|
|
402
|
+
|
|
403
|
+
- Last rebuild timestamp
|
|
404
|
+
- Total term count
|
|
405
|
+
- Corpus statistics summary
|
|
406
|
+
|
|
407
|
+
This file is informational only -- the binary index
|
|
408
|
+
files are the source of truth for search operations.
|
|
409
|
+
|
|
410
|
+
## Ranking Pipeline (End to End)
|
|
411
|
+
|
|
412
|
+
```
|
|
413
|
+
"auth token" (query string)
|
|
414
|
+
|
|
|
415
|
+
v
|
|
416
|
+
Tokenize + parse query (query-parser.wasm)
|
|
417
|
+
|
|
|
418
|
+
v
|
|
419
|
+
QueryPlan: terms=["auth","token"], mode=AND
|
|
420
|
+
|
|
|
421
|
+
v
|
|
422
|
+
Execute plan against indexes (fs-database.wasm)
|
|
423
|
+
|
|
|
424
|
+
v
|
|
425
|
+
Vec<RawMatch>: kbids + per-field TFs + positions
|
|
426
|
+
|
|
|
427
|
+
v
|
|
428
|
+
Fetch corpus statistics (fs-database.wasm)
|
|
429
|
+
|
|
|
430
|
+
v
|
|
431
|
+
Score: BM25F (query-parser.wasm)
|
|
432
|
+
Score: proximity (query-parser.wasm)
|
|
433
|
+
Score: zone (query-parser.wasm)
|
|
434
|
+
Score: freshness (optional) (query-parser.wasm)
|
|
435
|
+
|
|
|
436
|
+
v
|
|
437
|
+
Combine scores, sort descending, truncate
|
|
438
|
+
|
|
|
439
|
+
v
|
|
440
|
+
Shape into result mode (sections/documents/stats)
|
|
441
|
+
|
|
|
442
|
+
v
|
|
443
|
+
Ranked results
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
`kb-worker.wasm` orchestrates the entire flow,
|
|
447
|
+
calling `query-parser.wasm` for text processing and
|
|
448
|
+
scoring, and `fs-database.wasm` for storage lookups.
|
|
449
|
+
Neither standalone module calls the other directly.
|
|
450
|
+
|
|
451
|
+
## Result Modes
|
|
452
|
+
|
|
453
|
+
| Mode | Returns |
|
|
454
|
+
|-------------|-------------------------------------|
|
|
455
|
+
| `sections` | Individual matching sections with scores, snippets, and matched terms |
|
|
456
|
+
| `documents` | Documents containing matches, with aggregated scores and per-section details |
|
|
457
|
+
| `stats` | Match counts and top terms without content bodies |
|