package index
|
|
import (
|
"path/filepath"
|
"testing"
|
|
"github.com/aisim/kb-cli/internal/graph"
|
)
|
|
func TestFTSSearch(t *testing.T) {
|
tmpDir := t.TempDir()
|
dbPath := filepath.Join(tmpDir, "test.db")
|
|
store, err := Open(dbPath)
|
if err != nil {
|
t.Fatalf("Open failed: %v", err)
|
}
|
defer store.Close()
|
|
// 插入测试数据
|
store.InsertNode(&graph.Node{
|
ID: 1,
|
Path: "FAQ/充装类/001-test.md",
|
Title: "充装问题排查",
|
Section: "FAQ",
|
Tags: []string{"充装"},
|
Content: "关于智能枪充装问题的排查方法",
|
})
|
store.InsertNode(&graph.Node{
|
ID: 2,
|
Path: "知识/002-config.md",
|
Title: "充装规格配置",
|
Section: "知识",
|
Tags: []string{"配置"},
|
Content: "充装规格配置说明",
|
})
|
|
// 创建 FTS
|
if err := store.CreateFTS(); err != nil {
|
t.Fatalf("CreateFTS failed: %v", err)
|
}
|
if err := store.PopulateFTS(); err != nil {
|
t.Fatalf("PopulateFTS failed: %v", err)
|
}
|
|
// 搜索 - FTS5 对中文按字符 tokenize,"充装" 两个节点都包含
|
results, err := store.FTSSearch([]string{"充装"}, 10)
|
if err != nil {
|
t.Fatalf("FTSSearch failed: %v", err)
|
}
|
if len(results) < 1 {
|
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)
|
}
|
}
|