BM25 是資訊檢索領域經典的相關性排序演算法,通過詞頻、文檔長度和逆文檔頻率衡量關鍵詞與文檔的匹配程度,是 Elasticsearch 等搜尋引擎預設排序的基礎。Nova BM25 將該能力以原生索引的形式內建到 AnalyticDB PostgreSQL 中:您只需對文本列建立一個 nova_bm25 索引,即可在標準 SQL 裡完成中文分詞、關鍵詞召回、短語匹配和相關性排序,並可與分類、數值、時間等結構化條件以及向量檢索自由組合,無需引入外部搜尋系統。本文介紹如何在 AnalyticDB PostgreSQL 中安裝 Nova BM25 擴充並完成第一個中文全文檢索索引樣本。完成本文的操作後,您將掌握按相關性排序的全文檢索索引 SQL 編寫方法。
Nova BM25 的 SQL 函數位於 bm25 schema 下,索引存取方法名為 nova_bm25。
前提條件
AnalyticDB PostgreSQL 執行個體版本為 7.5.1.0 及以上,且已開啟向量檢索引擎最佳化。
當前
nova_bm25外掛程式暫未開放控制台自助安裝。如需使用,請提交工單,由支援人員協助完成執行個體升級與安裝。
操作步驟
步驟一:準備樣本資料
建立一張樣本表並寫入測試資料,用於後續全文檢索索引操作。
DROP TABLE IF EXISTS docs;
CREATE TABLE docs (
id BIGINT PRIMARY KEY,
title TEXT,
body TEXT,
category TEXT,
rating INT,
publish_at TIMESTAMP
) DISTRIBUTED BY (id);
INSERT INTO docs(id, title, body, category, rating, publish_at) VALUES
(1, '資料庫系統設計', 'PostgreSQL 支援全文檢索索引和交易處理', 'tech', 5, '2024-01-01 10:00:00'),
(2, '搜尋引擎開發', 'BM25 排序用於全文檢索索引相關性計算', 'tech', 5, '2024-02-01 10:00:00'),
(3, '向量檢索實踐', '混合檢索結合向量召回和關鍵詞召回', 'tech', 4, '2024-03-01 10:00:00'),
(4, '使用者Portrait analysis', '即時畫像處理用於推薦系統', 'biz', 3, '2024-04-01 10:00:00'),
(5, '中文標點測試', '資料庫,全文檢索索引;查詢最佳化!BM25 排名。', 'tech', 4, '2024-05-01 10:00:00');實際業務表中無需使用樣本中的欄位名。只需將需要全文檢索索引的文本列以及用於過濾的分類、數值、時間列納入 BM25 索引即可。
步驟二:建立中文 BM25 索引
為樣本表建立 BM25 索引,並根據欄位用途配置不同的分詞器和欄位類型。
CREATE INDEX docs_bm25_idx ON docs
USING nova_bm25 (body, title, category, rating, publish_at)
WITH (
text_fields = '{
"body": {"tokenizer": {"type": "jieba"}},
"title": {"tokenizer": {"type": "jieba"}},
"category": {"tokenizer": {"type": "keyword"}}
}',
numeric_fields = '{"rating": {}}',
datetime_fields = '{"publish_at": {}}'
);上述 WITH 配置根據欄位用途進行選擇。其中 text_fields 中的 tokenizer.type 指定文字欄位使用的分詞器:
需要全文檢索索引的中文文本列(如本文、標題),配置在
text_fields中,並使用jieba分詞。需要精確匹配的文本列(如分類、狀態、租戶、枚舉),同樣配置在
text_fields中,但使用keywordtokenizer,按整值匹配而不進行分詞。數值列(如評分、價格),配置在
numeric_fields中。時間列(如發布時間、更新時間),配置在
datetime_fields中。
步驟三:執行第一條查詢
使用以下 SQL 在 body 欄位上執行 BM25 全文檢索索引。查詢文本為 資料庫全文檢索索引。bm25.match 會根據欄位的 tokenizer 自動分詞,預設命中任意一個詞即返回,並按 BM25 分數排序。
SELECT
id,
title,
bm25.score(docs) AS score
FROM docs
WHERE body @@@ bm25.match('資料庫全文檢索索引')
ORDER BY bm25.score(docs) DESC
LIMIT 10;上述 SQL 中各關鍵區段說明如下:
body @@@ bm25.match(...):在body欄位上執行 BM25 全文檢索索引。bm25.score(docs):返回 BM25 相關性分數,分數越高表示越相關。ORDER BY bm25.score(docs) DESC:按相關性分數降序排列,將最相關的結果排在前面。
查詢結果樣本如下。具體的 score 數值可能因版本和分詞配置略有差異:
id | title | score |
2 | 搜尋引擎開發 | ... |
5 | 中文標點測試 | ... |
1 | 資料庫系統設計 | ... |
3 | 向量檢索實踐 | ... |
在實際應用中,只需將查詢文本傳遞給 bm25.match 即可執行全文檢索索引。
常見查詢寫法
AND 匹配(所有詞都必須命中)
預設情況下,bm25.match 命中任意一個詞即返回。如果要求所有詞都必須命中,可以指定 operator => 'and':
SELECT id, title, bm25.score(docs) AS score
FROM docs
WHERE body @@@ bm25.match('資料庫全文檢索索引', operator => 'and')
ORDER BY bm25.score(docs) DESC
LIMIT 10;也可以使用簡寫操作符 &&&,效果與上述寫法相同:
SELECT id, title, bm25.score(docs) AS score
FROM docs
WHERE body &&& '資料庫全文檢索索引'
ORDER BY bm25.score(docs) DESC
LIMIT 10;短語匹配
如果需要檢索一個完整短語(如 全文檢索索引),要求詞語相鄰且順序一致,請使用 bm25.phrase。
SELECT id, title, bm25.score(docs) AS score
FROM docs
WHERE body @@@ bm25.phrase('全文檢索索引')
ORDER BY bm25.score(docs) DESC
LIMIT 10;結構化過濾
BM25 全文檢索索引可以與普通 SQL 條件組合使用,實現分類、評分、時間等結構化過濾。
SELECT id, title, bm25.score(docs) AS score
FROM docs
WHERE body @@@ bm25.match('資料庫 檢索')
AND category = 'tech'
AND rating >= 4
AND publish_at >= '2024-01-01'
ORDER BY bm25.score(docs) DESC
LIMIT 10;多列加權查詢
當標題命中比本文命中更重要時,可以為標題欄位設定更高的權重。
SELECT id, title, bm25.score(docs) AS score
FROM docs
WHERE body @@@ bm25.multi_match(
ARRAY['title^3', 'body'],
query => '資料庫全文檢索索引'
)
ORDER BY bm25.score(docs) DESC
LIMIT 10;上述樣本中,title^3 表示標題欄位的權重為本文的 3 倍;body 未指定權重時預設為 1。
參與多列加權查詢的欄位必須屬於同一個 BM25 索引,且權重值必須為正數。
寫入後查詢可見度
本樣本按照"建立表 → 寫入資料 → 建立索引 → 執行查詢"的順序操作。建立索引前已經提交的資料,會在 CREATE INDEX 期間完成索引構建。因此,索引建立成功後,本樣本中的全部資料都可以直接查詢。
索引建立完成後,新執行的 INSERT 或 UPDATE 屬於增量寫入。Nova BM25 預設採用近即時查詢模式,這部分最新資料可能需要經過短暫的幕後處理,才會出現在預設查詢結果中。
如果當前串連需要立即查詢最新增量資料,可以執行以下語句:
SET nova_bm25.query_include_mutable = on;不再需要時恢複預設設定:
RESET nova_bm25.query_include_mutable;該設定僅影響當前資料庫連接。使用串連池時,串連可能被複用,請根據業務需要及時設定並恢複。
如果某個索引始終需要查詢最新增量資料,可以通過以下語句進行配置:
ALTER INDEX docs_bm25_idx
SET (query_skip_mutable = false);查詢最新增量資料需要額外處理尚未完成後台整理的索引資料,可能增加查詢延遲和資源消耗。
應用側參數綁定
為避免 SQL 注入風險,請勿將使用者輸入直接拼接進 SQL 字串。在 JDBC 中,建議使用 PreparedStatement 進行參數綁定:
PreparedStatement ps = conn.prepareStatement("""
SELECT id, title, bm25.score(docs) AS score
FROM docs
WHERE body @@@ bm25.match(?, operator => 'or')
ORDER BY bm25.score(docs) DESC
LIMIT ?
""");
ps.setString(1, userQuery);
ps.setInt(2, 10);後續步驟
瞭解常用查詢情境的完整樣本:Nova BM25常用查詢情境
查閱 Function API 的詳細說明:Nova BM25 Function API參考
瞭解索引和詞典的建立與維護:Nova BM25索引和詞典管理