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/index/fts.go |  101 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 101 insertions(+), 0 deletions(-)

diff --git a/internal/index/fts.go b/internal/index/fts.go
new file mode 100644
index 0000000..5e67985
--- /dev/null
+++ b/internal/index/fts.go
@@ -0,0 +1,101 @@
+package index
+
+import (
+	"database/sql"
+	"encoding/json"
+	"strings"
+)
+
+// FTSResult 全文搜索结果
+type FTSResult struct {
+	ID      int64
+	Path    string
+	Title   string
+	Section string
+	Rank    float64
+}
+
+// CreateFTS 创建 FTS5 虚拟表
+func (s *Store) CreateFTS() error {
+	_, err := s.db.Exec(`
+		CREATE VIRTUAL TABLE IF NOT EXISTS nodes_fts USING fts5(
+			title,
+			content,
+			tags,
+			entities,
+			content='nodes',
+			content_rowid='id'
+		)
+	`)
+	return err
+}
+
+// PopulateFTS 填充 FTS 索引
+func (s *Store) PopulateFTS() error {
+	_, err := s.db.Exec(`
+		INSERT INTO nodes_fts(rowid, title, content, tags, entities)
+		SELECT id, title, content_fts, tags, entities FROM nodes
+	`)
+	return err
+}
+
+// FTSSearch 全文搜索
+func (s *Store) FTSSearch(keywords []string, limit int) ([]FTSResult, error) {
+	if len(keywords) == 0 {
+		return nil, nil
+	}
+
+	// 构建 FTS5 查询
+	query := strings.Join(keywords, " OR ")
+
+	rows, err := s.db.Query(`
+		SELECT n.id, n.path, n.title, n.section, fts.rank
+		FROM nodes_fts fts
+		JOIN nodes n ON n.id = fts.rowid
+		WHERE nodes_fts MATCH ?
+		ORDER BY fts.rank
+		LIMIT ?
+	`, query, limit)
+	if err != nil {
+		return nil, err
+	}
+	defer rows.Close()
+
+	var results []FTSResult
+	for rows.Next() {
+		var r FTSResult
+		if err := rows.Scan(&r.ID, &r.Path, &r.Title, &r.Section, &r.Rank); err != nil {
+			return nil, err
+		}
+		results = append(results, r)
+	}
+	return results, nil
+}
+
+// GetNodeContent 获取节点内容
+func (s *Store) GetNodeContent(id int64) (string, []string, []string, error) {
+	var content, tagsJSON, entitiesJSON string
+	err := s.db.QueryRow("SELECT content_fts, tags, entities FROM nodes WHERE id = ?", id).
+		Scan(&content, &tagsJSON, &entitiesJSON)
+	if err == sql.ErrNoRows {
+		return "", nil, nil, nil
+	}
+	if err != nil {
+		return "", nil, nil, err
+	}
+
+	// 解析 JSON 数组
+	var tags, entities []string
+	if tagsJSON != "" {
+		if err := json.Unmarshal([]byte(tagsJSON), &tags); err != nil {
+			tags = nil
+		}
+	}
+	if entitiesJSON != "" {
+		if err := json.Unmarshal([]byte(entitiesJSON), &entities); err != nil {
+			entities = nil
+		}
+	}
+
+	return content, tags, entities, nil
+}

--
Gitblit v1.9.1