Skip to content

Commit 1d99707

Browse files
committed
fix: add remove event update index num
1 parent aa91e93 commit 1d99707

File tree

2 files changed

+270
-21
lines changed

2 files changed

+270
-21
lines changed

internal/service/indexer.go

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -399,37 +399,47 @@ func (i *indexer) RemoveIndexes(ctx context.Context, workspacePath string, fileP
399399
if len(projects) == 0 {
400400
return fmt.Errorf("no project found in workspace %s", workspacePath)
401401
}
402-
402+
workspaceModel, err := i.workspaceRepository.GetWorkspaceByPath(workspacePath)
403+
if err != nil {
404+
return err
405+
}
403406
var errs []error
404407
projectFilesMap, err := i.groupFilesByProject(projects, filePaths)
405408
if err != nil {
406409
return fmt.Errorf("group files by project failed: %w", err)
407410
}
408-
411+
totalRemoved := 0
409412
for projectUuid, files := range projectFilesMap {
410413
pStart := time.Now()
411414
i.logger.Info("start to remove project %s files index", projectUuid)
412415

413-
if err := i.removeIndexByFilePaths(ctx, projectUuid, files); err != nil {
416+
removed, err := i.removeIndexByFilePaths(ctx, projectUuid, files)
417+
if err != nil {
414418
errs = append(errs, err)
415419
}
416-
417-
i.logger.Info("remove project %s files index end, cost %d ms", projectUuid,
418-
time.Since(pStart).Milliseconds())
420+
totalRemoved += removed
421+
i.logger.Info("remove project %s files index end, cost %d ms, removed %d index.", projectUuid,
422+
time.Since(pStart).Milliseconds(), removed)
423+
}
424+
// 更新为删除后的值
425+
if workspaceModel != nil {
426+
if err := i.workspaceRepository.UpdateCodegraphInfo(workspacePath,
427+
workspaceModel.CodegraphFileNum-totalRemoved, time.Now().Unix()); err != nil {
428+
return errors.Join(append(errs, err)...)
429+
}
419430
}
420-
421431
err = errors.Join(errs...)
422-
i.logger.Info("remove workspace %s files index successfully, cost %d ms, errors: %v",
423-
workspacePath, time.Since(start).Milliseconds(), utils.TruncateError(err))
432+
i.logger.Info("remove workspace %s files index successfully, cost %d ms, removed %d index, errors: %v",
433+
workspacePath, time.Since(start).Milliseconds(), totalRemoved, utils.TruncateError(err))
424434
return err
425435
}
426436

427437
// removeIndexByFilePaths 删除单个项目的索引
428-
func (i *indexer) removeIndexByFilePaths(ctx context.Context, projectUuid string, filePaths []string) error {
438+
func (i *indexer) removeIndexByFilePaths(ctx context.Context, projectUuid string, filePaths []string) (int, error) {
429439
// 1. 查询path相应的file_table
430440
deleteFileTables, err := i.searchFileElementTablesByPath(ctx, projectUuid, filePaths)
431441
if err != nil {
432-
return fmt.Errorf("get file tables for deletion failed: %w", err)
442+
return 0, fmt.Errorf("get file tables for deletion failed: %w", err)
433443
}
434444
deletePaths := make(map[string]any)
435445
for _, v := range deleteFileTables {
@@ -438,15 +448,16 @@ func (i *indexer) removeIndexByFilePaths(ctx context.Context, projectUuid string
438448

439449
// 2. 清理符号定义
440450
if err = i.cleanupSymbolOccurrences(ctx, projectUuid, deleteFileTables, deletePaths); err != nil {
441-
return fmt.Errorf("cleanup symbol definitions failed: %w", err)
451+
return 0, fmt.Errorf("cleanup symbol definitions failed: %w", err)
442452
}
443453

444454
// 3. 删除path索引
445-
if err = i.deleteFileIndexes(ctx, projectUuid, filePaths); err != nil {
446-
return fmt.Errorf("delete file indexes failed: %w", err)
455+
deleted, err := i.deleteFileIndexes(ctx, projectUuid, filePaths)
456+
if err != nil {
457+
return 0, fmt.Errorf("delete file indexes failed: %w", err)
447458
}
448459

449-
return nil
460+
return deleted, nil
450461
}
451462

452463
// searchFileElementTablesByPath 获取待删除的文件表和路径(包括文件夹)
@@ -572,9 +583,9 @@ func (i *indexer) cleanupSymbolOccurrences(ctx context.Context, projectUuid stri
572583
}
573584

574585
// deleteFileIndexes 删除文件索引
575-
func (i *indexer) deleteFileIndexes(ctx context.Context, puuid string, filePaths []string) error {
586+
func (i *indexer) deleteFileIndexes(ctx context.Context, puuid string, filePaths []string) (int, error) {
576587
var errs []error
577-
588+
deleted := 0
578589
for _, fp := range filePaths {
579590
// 删除path索引
580591
language, err := lang.InferLanguage(fp)
@@ -583,14 +594,16 @@ func (i *indexer) deleteFileIndexes(ctx context.Context, puuid string, filePaths
583594
}
584595
if err = i.storage.Delete(ctx, puuid, store.ElementPathKey{Language: language, Path: fp}); err != nil {
585596
errs = append(errs, err)
597+
} else {
598+
deleted++
586599
}
587600
}
588601

589602
if len(errs) > 0 {
590-
return errors.Join(errs...)
603+
return deleted, errors.Join(errs...)
591604
}
592605

593-
return nil
606+
return deleted, nil
594607
}
595608

596609
// IndexFiles 根据工作区路径、文件路径,批量保存索引
@@ -652,7 +665,7 @@ func (i *indexer) IndexFiles(ctx context.Context, workspacePath string, filePath
652665
TotalFilesCnt: len(projectFiles),
653666
Project: project,
654667
WorkspacePath: workspacePath,
655-
PreviousFileNum: workspaceModel.FileNum - len(projectFiles),
668+
PreviousFileNum: workspaceModel.FileNum,
656669
Concurrency: i.config.MaxConcurrency,
657670
BatchSize: i.config.MaxBatchSize,
658671
}
@@ -1424,6 +1437,10 @@ func (i *indexer) RemoveAllIndexes(ctx context.Context, workspacePath string) er
14241437
for _, p := range projects {
14251438
errs = append(errs, i.storage.DeleteAll(ctx, p.Uuid))
14261439
}
1440+
// 将数据库数据置为0
1441+
if err := i.workspaceRepository.UpdateCodegraphInfo(workspacePath, 0, time.Now().Unix()); err != nil {
1442+
return errors.Join(append(errs, fmt.Errorf("update codegraph info err:%v", err))...)
1443+
}
14271444
return errors.Join(errs...)
14281445
}
14291446

@@ -1580,7 +1597,6 @@ func (i *indexer) parseFiles(ctx context.Context, files []*types.FileWithModTime
15801597
FailedFilePaths: make([]string, 0, totalFiles/4), // 预估失败文件数约为文件数的25%
15811598
}
15821599

1583-
// TODO 大量文件,会导致错误很多
15841600
var errs []error
15851601

15861602
for _, f := range files {

test/api/index_by_event_test.go

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
package api
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"io"
7+
"net/http"
8+
"net/url"
9+
"strings"
10+
"testing"
11+
"time"
12+
13+
"github.com/stretchr/testify/suite"
14+
)
15+
16+
type IndexEventIntegrationTestSuite struct {
17+
BaseIntegrationTestSuite
18+
}
19+
20+
type indexEventTestCase struct {
21+
name string
22+
workspace string
23+
data []map[string]interface{}
24+
wantProcessTime time.Duration
25+
validateIndex func(t *testing.T, indexes []map[string]interface{})
26+
}
27+
28+
func (s *IndexEventIntegrationTestSuite) TestPublishEvent() {
29+
// 定义测试用例表
30+
testCases := []indexEventTestCase{
31+
{
32+
name: "打开工作区",
33+
workspace: s.workspacePath,
34+
data: []map[string]interface{}{
35+
{
36+
"eventType": "open_workspace",
37+
"eventTime": "2025-07-28 20:47:00",
38+
},
39+
},
40+
wantProcessTime: time.Second * 15,
41+
validateIndex: func(t *testing.T, indexes []map[string]interface{}) {},
42+
},
43+
44+
{
45+
name: "删除文件",
46+
data: []map[string]interface{}{
47+
{
48+
"eventType": "delete_file",
49+
"eventTime": "2025-07-28 20:47:00",
50+
"sourcePath": s.workspacePath,
51+
"targetPath": s.workspacePath,
52+
},
53+
},
54+
wantProcessTime: time.Second * 15,
55+
validateIndex: func(t *testing.T, indexes []map[string]interface{}) {},
56+
},
57+
{
58+
name: "删除文件夹",
59+
workspace: s.workspacePath,
60+
data: []map[string]interface{}{
61+
{
62+
"eventType": "delete_file",
63+
"eventTime": "2025-07-28 20:47:00",
64+
"sourcePath": s.workspacePath,
65+
"targetPath": s.workspacePath,
66+
},
67+
},
68+
wantProcessTime: time.Second * 15,
69+
validateIndex: func(t *testing.T, indexes []map[string]interface{}) {},
70+
},
71+
{
72+
name: "新增文件",
73+
workspace: s.workspacePath,
74+
data: []map[string]interface{}{
75+
{
76+
"eventType": "add_file",
77+
"eventTime": "2025-07-28 20:47:00",
78+
"sourcePath": s.workspacePath,
79+
"targetPath": s.workspacePath,
80+
},
81+
},
82+
wantProcessTime: time.Second * 15,
83+
validateIndex: func(t *testing.T, indexes []map[string]interface{}) {},
84+
},
85+
{
86+
name: "修改文件",
87+
workspace: s.workspacePath,
88+
data: []map[string]interface{}{
89+
{
90+
"eventType": "modify_file",
91+
"eventTime": "2025-07-28 20:47:00",
92+
"sourcePath": s.workspacePath,
93+
"targetPath": s.workspacePath,
94+
},
95+
},
96+
wantProcessTime: time.Second * 15,
97+
validateIndex: func(t *testing.T, indexes []map[string]interface{}) {},
98+
},
99+
{
100+
name: "重命名文件",
101+
workspace: s.workspacePath,
102+
data: []map[string]interface{}{
103+
{
104+
"eventType": "rename_file",
105+
"eventTime": "2025-07-28 20:47:00",
106+
"sourcePath": s.workspacePath,
107+
"targetPath": s.workspacePath,
108+
},
109+
},
110+
wantProcessTime: time.Second * 15,
111+
validateIndex: func(t *testing.T, indexes []map[string]interface{}) {},
112+
},
113+
{
114+
name: "重命名文件夹",
115+
workspace: s.workspacePath,
116+
data: []map[string]interface{}{
117+
{
118+
"eventType": "rename_file",
119+
"eventTime": "2025-07-28 20:47:00",
120+
"sourcePath": s.workspacePath,
121+
"targetPath": s.workspacePath,
122+
},
123+
},
124+
wantProcessTime: time.Second * 15,
125+
validateIndex: func(t *testing.T, indexes []map[string]interface{}) {},
126+
},
127+
}
128+
129+
// 执行表格驱动测试
130+
for _, tc := range testCases {
131+
s.T().Run(tc.name, func(t *testing.T) {
132+
// 发布事件
133+
134+
// 等待一定事件
135+
136+
// 查询索引,达到期望状态
137+
138+
})
139+
}
140+
}
141+
142+
func TestIndexEventIntegrationTestSuite(t *testing.T) {
143+
suite.Run(t, new(IndexEventIntegrationTestSuite))
144+
}
145+
146+
// publishEvent 发布索引事件
147+
func (s *IndexEventIntegrationTestSuite) publishEvent(workspace string, data any) error {
148+
var resp *http.Response
149+
var err error
150+
151+
// 准备请求体
152+
reqBody := map[string]interface{}{
153+
"workspace": workspace,
154+
"data": data,
155+
}
156+
157+
// 序列化请求体
158+
jsonData, err := json.Marshal(reqBody)
159+
if err != nil {
160+
return err
161+
}
162+
163+
// 创建HTTP请求
164+
req, err := s.CreatePOSTRequest(s.baseURL+"/codebase-indexer/api/v1/events", jsonData)
165+
if err != nil {
166+
return err
167+
}
168+
169+
// 发送请求
170+
resp, err = s.SendRequest(req)
171+
172+
s.Require().NoError(err)
173+
defer resp.Body.Close()
174+
175+
// 验证响应状态码
176+
if resp.StatusCode != http.StatusOK {
177+
return fmt.Errorf("unexpected status code: %d", resp.StatusCode)
178+
}
179+
// 读取响应体
180+
body, err := io.ReadAll(resp.Body)
181+
s.Require().NoError(err)
182+
183+
// 解析响应JSON
184+
var response map[string]interface{}
185+
if err = json.Unmarshal(body, &response); err != nil {
186+
return err
187+
}
188+
189+
s.Equal("ok", response["message"])
190+
if response["message"] != "ok" {
191+
return fmt.Errorf("expected message `ok` but got `%s`", response["message"])
192+
}
193+
return nil
194+
}
195+
196+
// 获取全部索引
197+
func (s *IndexEventIntegrationTestSuite) dumpIndex() ([]map[string]interface{}, error) {
198+
// 构建请求URL
199+
reqURL, err := url.Parse(s.baseURL + "/codebase-indexer/api/v1/index/export")
200+
s.Require().NoError(err)
201+
202+
// 添加查询参数
203+
q := reqURL.Query()
204+
q.Add("codebasePath", s.workspacePath)
205+
206+
reqURL.RawQuery = q.Encode()
207+
208+
// 创建HTTP请求
209+
req, err := s.CreateGETRequest(reqURL.String())
210+
s.Require().NoError(err)
211+
212+
// 发送请求
213+
resp, err := s.SendRequest(req)
214+
s.Require().NoError(err)
215+
defer resp.Body.Close()
216+
body, err := io.ReadAll(resp.Body)
217+
218+
// 先将读取到的body转换为字符串
219+
bodyStr := string(body)
220+
221+
// 按行分割内容
222+
lines := strings.Split(bodyStr, "\n")
223+
var indexes []map[string]interface{}
224+
for _, line := range lines {
225+
var indexLine map[string]interface{}
226+
err := json.Unmarshal([]byte(line), &indexLine)
227+
if err != nil {
228+
return nil, err
229+
}
230+
indexes = append(indexes, indexLine)
231+
}
232+
return indexes, nil
233+
}

0 commit comments

Comments
 (0)