# Search Results

How KuraDB groups keyword and semantic hits, which fields it keeps private, and which consistency guarantees every result carries.

## Grouping

Both flat result sets are grouped by first-seen source, preserving rank order inside each group. The external representation is:

```json
[
  {
    "source": "/path/to/document.md",
    "matches": [
      {"chunk": 1, "content": "..."},
      {"chunk": 2, "content": "..."}
    ]
  }
]
```

Over REST, an empty selected branch is represented as `[]`. The MCP `search_rag` output omits empty branches.

## Private ranking data

Internal fields are dropped before serialization; the public `Group` and `Match` types carry only the four contract fields:

| Internal field | Why it stays private |
|---|---|
| Row ID | Storage implementation detail |
| Semantic score | Threshold and ranking may evolve |
| Keyword hit count | Internal ranking signal |
| Chunk total | Not part of the consumer contract |
| Overall total | The API does not provide pagination metadata |

Consumers should depend only on `source`, `matches`, `chunk`, and `content`.

## Consistency behavior

- Keyword SQL always filters dismissed rows.
- Semantic hydration always filters dismissed rows.
- Vectors whose dimensions differ from the query are ignored.
- Query cache is keyed by exact query text, without normalization.
- Keyword and semantic result arrays remain independent; KuraDB does not blend their scores.
- Search covers only databases loaded when the daemon or `kura mcp` process started.
