# Tuning Guide

Guidance for tuning `@coda/search` parameters in production. For algorithm background, see [Concepts](concepts.md).

---

## Parameters

### rrfK (rank weighting)

Controls how aggressively top-ranked results are weighted relative to lower-ranked ones in [RRF fusion](concepts.md#rrf-score-fusion).

- **Low k (1-5)**: top rank dominates. Use when one signal is highly reliable.
- **Default k=25**: balanced. Works well when all signals are equally trusted.
- **High k (50-100)**: more uniform weighting across all candidates. Use when signals disagree frequently.

Set via `HybridSearchConfig.rrfK`. Ignored when a custom `scoreFusion` is provided.

### vectorOverFetch

The pipeline fetches `limit * vectorOverFetch` candidates from the vector index before fusion. Higher values improve recall (more candidates enter fusion) at the cost of more fusion work.

- **Default 10**: good balance for most workloads.
- **Increase to 15-20** when corpus is large and vector recall matters more than fusion speed.
- **Decrease to 4-6** for low-latency applications where keyword and glossary signals dominate.

Set via `HybridSearchConfig.vectorOverFetch`.

### efSearch (HNSW recall vs latency)

Controls the candidate list size during [HNSW](concepts.md#hnsw-vector-search) graph traversal at query time. Higher values produce better recall at the cost of longer query times.

- **Default 50**: sufficient for corpora of tens of thousands of documents.
- **Increase to 100-200** when recall is critical and latency budget allows.
- **Decrease to 20-30** for ultra-low-latency queries where approximate results are acceptable.

Set via `HnswConfig.efSearch` when constructing the `HnswIndex` or [`QuantizedHnswIndex`](concepts.md#uint8-quantization).

### minTokenLength

Minimum length for query tokens. Tokens shorter than this are filtered before keyword search and [glossary expansion](concepts.md#glossary-matching).

- **Default 3**: filters out noise tokens (`of`, `by`, `to`, etc.) that survive stop word removal.
- **Set to 2** when your domain has meaningful 2-character abbreviations (e.g., `US`, `IP`, `ID`).
- **Set to 1** only if single-character tokens carry meaning in your domain.

Set via `HybridSearchConfig.minTokenLength`.

### bigrams

When enabled, adjacent query words are combined into bigram tokens (e.g., `"master recording"` produces `"master_record"`). This only matches documents indexed with the same bigram via the [tokenization pipeline](concepts.md#tokenization-pipeline), providing precise compound term matching.

- **Default false**: sufficient for most use cases.
- **Enable** when your corpus contains compound terms that are semantically distinct from their parts (e.g., `"master recording"` vs `"master"` + `"recording"`).

Set via `HybridSearchConfig.bigrams`.

---

## Performance Characteristics

### Latency expectations

| Operation                         | Typical time | Notes                                                                                                                          |
| --------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| Search query (in-memory)          | ~10 ms       | [BM25](concepts.md#bm25-keyword-scoring) + [HNSW](concepts.md#hnsw-vector-search) + [RRF](concepts.md#rrf-score-fusion) fusion |
| Single query embedding            | ~5 ms        | ONNX inference (GPU)                                                                                                           |
| Cold index build (10K items)      | ~5 min       | Dominated by embedding                                                                                                         |
| Warm rebuild (10K items, 100 new) | ~15 s        | Re-embeds only changed items                                                                                                   |
| Incremental update (50 items)     | ~3 s         | Re-embed + index merge                                                                                                         |

Embedding dominates cold start time. Use snapshot persistence to avoid re-embedding unchanged items on restart.

### Memory usage

The primary memory consumers are the float32 vector store and the [HNSW graph](concepts.md#hnsw-vector-search). For a corpus of `N` documents at `D` dimensions:

- **Float32 vectors:** `N * D * 4` bytes (e.g., 10K x 1024 = ~40 MB)
- **Quantized HNSW graph:** `N * D * 1` byte + graph overhead (e.g., 10K x 1024 = ~15 MB)
- **Total vector memory:** roughly `N * D * 5` bytes

The [BM25](concepts.md#bm25-keyword-scoring) inverted index and [trie](concepts.md#prefix-fallback-via-trie) add comparatively little memory (proportional to unique token count, not vector dimensions).

### Algorithmic complexity

| Component                                            | Time complexity      | Space complexity | Notes                                  |
| ---------------------------------------------------- | -------------------- | ---------------- | -------------------------------------- |
| [BM25](concepts.md#bm25-keyword-scoring) index build | O(n \* d)            | O(n \* d)        | n documents, d unique terms            |
| BM25 query                                           | O(q \* df)           | O(1)             | q query terms, df = document frequency |
| [HNSW](concepts.md#hnsw-vector-search) insert        | O(m \* log n)        | O(n \* m)        | amortized                              |
| HNSW query                                           | O(efSearch \* log n) | O(efSearch)      | approximate                            |
| [RRF](concepts.md#rrf-score-fusion) fusion           | O(k \* n)            | O(n)             | k ranked lists                         |
| [Glossary](concepts.md#glossary-matching) lookup     | O(q \* g)            | O(g)             | g = glossary entries                   |
