OpenSearch Index 教學:Mapping、text 與 keyword
本文件說明 thortron_one 全文檢索(Full-Text Search)如何建立 OpenSearch index,以及 mapping 裡 text / keyword 的差異與選用原則。
對應實作:internal/pkgs/utilities/fulltextsearch/opensearch/client.go
1. 這套 FTS 在存什麼?
每個 PDF 頁面會寫成一筆 OpenSearch document,大致欄位如下:
| 欄位 | OpenSearch type | 說明 |
|---|---|---|
document_id | long | 文件儲存系統的 document ID |
filename | text + filename.keyword | 檔名(全文搜尋 + 精確值) |
content | text | 該頁擷取出來的文字 |
page | integer | 頁碼 |
source | keyword | 來源識別,目前固定 thortron_one |
indexed_at | date | 寫入時間(RFC3339) |
Document _id 格式:thortron_one-{documentID}-p{page}
2. Index 什麼時候建立?
連線時由 Client.New 呼叫 ensureIndex:
Client.New(url, index)
└─ ensureIndex
├─ Indices.Exists
│ ├─ 200 → index 已存在,不做任何事
│ └─ 404 → Indices.Create(套用下方 mappings)
└─ 連線成功後才能 IndexPages / Query
重點:
- 第一次連到空的 OpenSearch 時,會自動建立 index 與 mappings。
- Index 一旦存在就不會更新 mapping。若要改欄位型別(例如
keyword→text),必須刪除重建(見RecreateIndex/ migration 流程)。 DeleteByQuery只能清資料,不能改 mapping。
3. 實際 Mapping
建立 index 時送出的 body(精簡版):
{
"mappings": {
"properties": {
"document_id": { "type": "long" },
"filename": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"content": { "type": "text" },
"page": { "type": "integer" },
"source": { "type": "keyword" },
"indexed_at": { "type": "date" }
}
}
}
搜尋時使用 multi_match,查詢欄位為:
content(text)filename(text)
也就是:使用者關鍵字走全文檢索路徑,不是 filename.keyword 的精確比對。
4. text 與 keyword 差在哪?
兩者都是「字串相關」的型別,但索引與查詢行為完全不同。
對照表
text | keyword | |
|---|---|---|
| 主要用途 | 全文搜尋(full-text search) | 精確比對、排序、聚合(filter / sort / agg) |
| 寫入時 | 會經過 analyzer 分詞 | 整段字串當成一個 term,不分詞 |
| 查詢方式 | match / multi_match 等 | term / terms 等 |
| 典型例子 | 內文、標題、可模糊搜的檔名 | ID、狀態碼、來源、標籤、固定枚舉值 |
用例子理解
假設寫入:
content = "Install the battery pack carefully"
filename = "User Manual Rev2.pdf"
source = "thortron_one"
content(text)
- Analyzer 可能拆成:
install,battery,pack,carefully… - 搜尋
"battery"→ 可以命中 - 搜尋
"battery pack"→ 也可以命中(依 analyzer / 查詢型態)
source(keyword)
- 索引裡只有一個 term:
thortron_one - 適合:
termfiltersource = "thortron_one" - 若誤用
match去查"thortron",行為與預期常不一致(因為它本來就不是為全文搜尋設計)
filename(text + keyword multi-field)
同一份原始字串存成兩種索引:
| 欄位路徑 | 型別 | 行為 |
|---|---|---|
filename | text | 檔名可被分詞,給 multi_match 用 |
filename.keyword | keyword | 整段檔名精確值,可做 sort / aggregation / exact filter |
ignore_above: 256 的意思:
- 單位是 bytes(不是字元數)
- 超過 256 bytes 的值 不會寫進
filename.keyword filename的 text 全文搜尋仍然可用- 目的:避免超長檔名把 terms dictionary 撐很大