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
| }
|
|