# 關鍵字搜尋

查詢字串如何轉成關鍵字、比對已儲存的 chunk、排序，並在回應中分組。

## 查詢處理

`search.Search`（`internal/search/search.go`）先驗證請求，再準備關鍵字：

| 步驟 | 規則 |
|---|---|
| 資料庫 | 必須是已載入的資料庫名稱，否則回傳 `invalid argument: "<name>" not exist` |
| 查詢 | `q` 不得為空，否則回傳 `invalid argument: q is required` |
| 筆數上限 | 1 到 100；未提供、0、負數、非數字或超過 100 時改用 10 |
| Target | 空值或 `keyword`；其他值回傳 `invalid argument: unknown target "<value>"` |
| 關鍵字 | `q` 轉小寫、依空白切分並去除重複 |

## 比對與排序

每個關鍵字都轉成不分大小寫的子字串比對（`LOWER(content) LIKE '%keyword%'`），只比對有效資料列（`dismiss = FALSE`）。chunk 只要含任一關鍵字即命中。

| 順序 | 依據 |
|---|---|
| 1 | chunk 含有的不重複關鍵字數，多者在前 |
| 2 | 資料列 id，舊者在前 |

子字串比對代表 `go` 也會命中 `golang` 與 `google`。關鍵字中的 `%` 與 `_` 會被當作 SQL `LIKE` 萬用字元。轉小寫依 SQLite `LOWER()`，只轉換 ASCII 字母；其他文字依原樣比對。

## 回應格式

命中的 chunk 依來源檔分組，檔案順序依各檔第一個 chunk 在排序中出現的位置：

```json
{
  "keyword": [
    {
      "source": "/Users/you/.config/kuradb/notes/inbox/a.md",
      "matches": [
        { "chunk": 1, "content": "Alpha Go notes\nline two" },
        { "chunk": 2, "content": "Second paragraph about SQLite" }
      ]
    }
  ]
}
```

| 欄位 | 意義 |
|---|---|
| `source` | 檔案在資料庫 inbox 內的絕對路徑 |
| `matches[].chunk` | 該檔內從 1 起算的 chunk 編號 |
| `matches[].content` | chunk 全文 |

資料列 id、命中數等內部排序值不會回傳。沒有命中時，HTTP API 回傳 `{"keyword": []}`，MCP 工具回傳空物件。

## 相關頁面

- [HTTP API](/zh/http-api)
- [MCP 伺服器](/zh/mcp-server)
- [支援的檔案格式](/zh/file-formats)
