From 5a2be87f658d01bc5b3b7ecdba92ac69bf09dcf7 Mon Sep 17 00:00:00 2001
From: ai_xiaopei <xiaopei@aisim.cn>
Date: Thu, 03 Sep 2026 15:15:37 +0800
Subject: [PATCH] fix(search): 搜索准确性四连修——FTS5 MATCH 引号包裹(连字符不再被解析成 MINUS);KeywordSearch 截断按命中关键词数排序(高频 bigram 不再挤出强相关文档);原词奖励档(完整短语压过泛化 bigram);tags 字段真实参与评分(此前误用 Section)
---
internal/index/fts_test.go | 78 +++++++++++++++++++++++++++++++++++++++
1 files changed, 78 insertions(+), 0 deletions(-)
diff --git a/internal/index/fts_test.go b/internal/index/fts_test.go
index 1a42430..050f3e0 100644
--- a/internal/index/fts_test.go
+++ b/internal/index/fts_test.go
@@ -52,3 +52,81 @@
t.Errorf("result count = %d, want >= 1", len(results))
}
}
+
+// TestKeywordSearchASCII 覆盖 KeywordSearch 的 FTS 通道(ASCII 词 MATCH + rank 排序)
+func TestKeywordSearchASCII(t *testing.T) {
+ store, err := Open(filepath.Join(t.TempDir(), "test.db"))
+ if err != nil {
+ t.Fatalf("Open failed: %v", err)
+ }
+ defer store.Close()
+
+ // 两个节点都含 "pump",但 node1 标题也含(title 权重高 → rank 更靠前)
+ store.InsertNode(&graph.Node{
+ ID: 1,
+ Path: "知识/001-pump.md",
+ Title: "pump failure",
+ Section: "知识",
+ Content: "pump failure troubleshooting guide",
+ })
+ store.InsertNode(&graph.Node{
+ ID: 2,
+ Path: "知识/002-valve.md",
+ Title: "valve config",
+ Section: "知识",
+ Content: "the pump is configured here",
+ })
+ if err := store.CreateFTS(); err != nil {
+ t.Fatalf("CreateFTS failed: %v", err)
+ }
+ if err := store.PopulateFTS(); err != nil {
+ t.Fatalf("PopulateFTS failed: %v", err)
+ }
+
+ results, err := store.KeywordSearch([]string{"pump"}, 10)
+ if err != nil {
+ t.Fatalf("KeywordSearch failed: %v", err)
+ }
+ if len(results) != 2 {
+ t.Fatalf("result count = %d, want 2 (FTS 通道应命中两节点)", len(results))
+ }
+ // FTS 通道 rank 排序:标题含 pump 的 node1 应排第一
+ if results[0].ID != 1 {
+ t.Errorf("FTS rank 排序: 首位 = %d, want 1", results[0].ID)
+ }
+}
+
+// TestKeywordSearchMixed 覆盖双通道混合(ASCII 走 FTS + CJK 走 LIKE,seen 去重)
+func TestKeywordSearchMixed(t *testing.T) {
+ store, err := Open(filepath.Join(t.TempDir(), "test.db"))
+ if err != nil {
+ t.Fatalf("Open failed: %v", err)
+ }
+ defer store.Close()
+
+ store.InsertNode(&graph.Node{
+ ID: 1,
+ Path: "FAQ/001-pump.md",
+ Title: "pump 补气",
+ Section: "FAQ",
+ Content: "pump 补气失败排查",
+ })
+ if err := store.CreateFTS(); err != nil {
+ t.Fatalf("CreateFTS failed: %v", err)
+ }
+ if err := store.PopulateFTS(); err != nil {
+ t.Fatalf("PopulateFTS failed: %v", err)
+ }
+
+ // 同一节点被两个通道命中 → 去重后只出现一次
+ results, err := store.KeywordSearch([]string{"pump", "补气"}, 10)
+ if err != nil {
+ t.Fatalf("KeywordSearch failed: %v", err)
+ }
+ if len(results) != 1 {
+ t.Fatalf("双通道去重: count = %d, want 1", len(results))
+ }
+ if results[0].ID != 1 {
+ t.Errorf("命中节点 = %d, want 1", results[0].ID)
+ }
+}
--
Gitblit v1.10.0