ai_xiaopei
2026-07-25 7445005745523d430a3cd22c3208cce2f383f1ee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
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
}