From 09c2a03cefa687832257d3b816da50db3cfcc0f2 Mon Sep 17 00:00:00 2001
From: ai_xiaopei <xiaopei@aisim.cn>
Date: Sat, 25 Jul 2026 23:48:11 +0800
Subject: [PATCH] feat: 实现输出格式化
---
internal/search/engine.go | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 106 insertions(+), 0 deletions(-)
diff --git a/internal/search/engine.go b/internal/search/engine.go
new file mode 100644
index 0000000..95d7c37
--- /dev/null
+++ b/internal/search/engine.go
@@ -0,0 +1,106 @@
+package search
+
+import (
+ "sort"
+ "strings"
+
+ "github.com/aisim/kb-cli/internal/index"
+)
+
+// SearchOptions 搜索选项
+type SearchOptions struct {
+ Expanded []string // 扩展词
+ Symptom []string // 症状词
+ TopN int // 返回前 N 条
+}
+
+// SearchResult 搜索结果
+type SearchResult struct {
+ ID int64
+ Path string
+ Title string
+ Section string
+ Score int
+}
+
+// Search 执行搜索
+func Search(store *index.Store, keywords []string, opts SearchOptions) ([]SearchResult, error) {
+ // 合并所有关键词
+ allKeywords := append(keywords, opts.Expanded...)
+ allKeywords = append(allKeywords, opts.Symptom...)
+
+ // FTS5 搜索
+ ftsResults, err := store.FTSSearch(allKeywords, 100)
+ if err != nil {
+ return nil, err
+ }
+
+ // 评分
+ scoreMap := make(map[int64]int)
+ for _, r := range ftsResults {
+ score := 0
+
+ // 关键词匹配评分
+ for _, kw := range keywords {
+ score += scoreResult(r, kw, ScoreNormal)
+ }
+ for _, kw := range opts.Expanded {
+ score += scoreResult(r, kw, ScoreExpanded)
+ }
+ for _, kw := range opts.Symptom {
+ score += scoreResult(r, kw, ScoreSymptom)
+ }
+
+ scoreMap[r.ID] = score
+ }
+
+ // 转换为结果列表
+ var results []SearchResult
+ for _, r := range ftsResults {
+ results = append(results, SearchResult{
+ ID: r.ID,
+ Path: r.Path,
+ Title: r.Title,
+ Section: r.Section,
+ Score: scoreMap[r.ID],
+ })
+ }
+
+ // 按分数排序
+ sort.Slice(results, func(i, j int) bool {
+ return results[i].Score > results[j].Score
+ })
+
+ // 限制返回数量
+ if opts.TopN > 0 && len(results) > opts.TopN {
+ results = results[:opts.TopN]
+ }
+
+ return results, nil
+}
+
+// scoreResult 计算单个结果的得分
+func scoreResult(r index.FTSResult, keyword string, scoreType ScoreType) int {
+ score := 0
+ kw := strings.ToLower(keyword)
+
+ // 路径匹配
+ if strings.Contains(strings.ToLower(r.Path), kw) {
+ score += CalcScore(keyword, "path", scoreType)
+ }
+
+ // 标题匹配
+ if strings.Contains(strings.ToLower(r.Title), kw) {
+ score += CalcScore(keyword, "title", scoreType)
+ }
+
+ // 板块匹配
+ if strings.Contains(strings.ToLower(r.Section), kw) {
+ score += CalcScore(keyword, "tag", scoreType)
+ }
+
+ // 内容匹配(FTS 已经匹配,给基础分)
+ score += CalcScore(keyword, "content", scoreType)
+
+ return score
+}
--
Gitblit v1.9.1