From 8de9d03cad67d6944d40567bff90350d132d60b0 Mon Sep 17 00:00:00 2001
From: ai_xiaopei <xiaopei@aisim.cn>
Date: Sun, 26 Jul 2026 09:54:11 +0800
Subject: [PATCH] feat: add index gc command for cleaning orphan nodes

---
 internal/index/sqlite.go |   81 ++++++++++++++++++++++++++++++++++++++++
 1 files changed, 81 insertions(+), 0 deletions(-)

diff --git a/internal/index/sqlite.go b/internal/index/sqlite.go
index 6b7839a..db194fe 100644
--- a/internal/index/sqlite.go
+++ b/internal/index/sqlite.go
@@ -6,6 +6,7 @@
 	"fmt"
 	"os"
 	"path/filepath"
+	"strings"
 
 	_ "github.com/mattn/go-sqlite3"
 	"github.com/aisim/kb-cli/internal/graph"
@@ -177,3 +178,83 @@
 
 	return links, nil
 }
+
+// NodeInfo 节点基本信息(用于 GC)
+type NodeInfo struct {
+	ID   int64
+	Path string
+}
+
+// GetAllNodes 获取所有节点(用于 GC 检查)
+func (s *Store) GetAllNodes() ([]NodeInfo, error) {
+	rows, err := s.db.Query("SELECT id, path FROM nodes")
+	if err != nil {
+		return nil, fmt.Errorf("查询节点失败: %w", err)
+	}
+	defer rows.Close()
+
+	var nodes []NodeInfo
+	for rows.Next() {
+		var n NodeInfo
+		if err := rows.Scan(&n.ID, &n.Path); err != nil {
+			return nil, fmt.Errorf("扫描节点失败: %w", err)
+		}
+		nodes = append(nodes, n)
+	}
+
+	if err := rows.Err(); err != nil {
+		return nil, fmt.Errorf("遍历节点失败: %w", err)
+	}
+
+	return nodes, nil
+}
+
+// DeleteNodesByPaths 删除指定路径的节点及其关联边
+func (s *Store) DeleteNodesByPaths(paths []string) (int, error) {
+	if len(paths) == 0 {
+		return 0, nil
+	}
+
+	// 构建 IN 子句
+	placeholders := make([]string, len(paths))
+	args := make([]interface{}, len(paths))
+	for i, path := range paths {
+		placeholders[i] = "?"
+		args[i] = path
+	}
+
+	tx, err := s.db.Begin()
+	if err != nil {
+		return 0, fmt.Errorf("开始事务失败: %w", err)
+	}
+
+	// 先删除关联边
+	query := fmt.Sprintf(`
+		DELETE FROM edges 
+		WHERE from_node IN (SELECT id FROM nodes WHERE path IN (%s))
+		   OR to_node IN (SELECT id FROM nodes WHERE path IN (%s))
+	`, strings.Join(placeholders, ","), strings.Join(placeholders, ","))
+	
+	// 参数需要重复两次
+	allArgs := append(args, args...)
+	_, err = tx.Exec(query, allArgs...)
+	if err != nil {
+		tx.Rollback()
+		return 0, fmt.Errorf("删除边失败: %w", err)
+	}
+
+	// 删除节点
+	query = fmt.Sprintf("DELETE FROM nodes WHERE path IN (%s)", strings.Join(placeholders, ","))
+	result, err := tx.Exec(query, args...)
+	if err != nil {
+		tx.Rollback()
+		return 0, fmt.Errorf("删除节点失败: %w", err)
+	}
+
+	if err := tx.Commit(); err != nil {
+		return 0, fmt.Errorf("提交事务失败: %w", err)
+	}
+
+	affected, _ := result.RowsAffected()
+	return int(affected), nil
+}

--
Gitblit v1.9.1