Skip to content

Commit 971b1af

Browse files
committed
refactor(quick_search): 优化查询逻辑,支持多关键词分词搜索
重构快速搜索的查询流程: 1. 提取regionID预计算逻辑,避免重复计算 2. 新增分词查询功能,按空白符拆分搜索关键词以召回更全面结果 3. 新增搜索结果去重逻辑,避免重复返回相同条目 4. 简化原有单查询的冗余代码,统一处理带/不带region过滤的查询流程
1 parent af80042 commit 971b1af

76 files changed

Lines changed: 316 additions & 269 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ require (
2525
require (
2626
github.com/DotNetAge/gochat v0.2.7
2727
github.com/DotNetAge/gograph v0.2.6
28-
github.com/DotNetAge/goharness v0.2.21
28+
github.com/DotNetAge/goharness v0.2.22
2929
github.com/DotNetAge/gorag/v2 v2.0.9
3030
github.com/creack/pty v1.1.24
3131
go.etcd.io/bbolt v1.4.3
@@ -140,6 +140,6 @@ require (
140140

141141
replace github.com/coder/hnsw => ./third_party/hnsw
142142

143-
replace github.com/DotNetAge/gorag/v2 => ../gorag
143+
// replace github.com/DotNetAge/gorag/v2 => ../gorag
144144

145-
replace github.com/DotNetAge/goharness => ../goharness
145+
// replace github.com/DotNetAge/goharness => ../goharness

go.sum

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,6 @@ github.com/DotNetAge/gochat v0.2.7 h1:W06T9dRx46QlWkFDlpRSGtpdgNb8m9meTPxycRX/xZ
1515
github.com/DotNetAge/gochat v0.2.7/go.mod h1:w7m36rMZoDwmReJNTLUGHhok6DSqnIidg9wFKwYM1dM=
1616
github.com/DotNetAge/gograph v0.2.6 h1:LhYERtYTPaWXvbBy9bO/6XGmYTW/OZGXQ+zyfycsexo=
1717
github.com/DotNetAge/gograph v0.2.6/go.mod h1:Ia2wvbkpdJvFJEZ1vw+IklbhNbWjPli2dnU21jbmy7I=
18-
github.com/DotNetAge/goharness v0.2.21 h1:KHKf21IucQ3Y5UEaVh2S8AwshNgVR95wEfAoVCSYZUk=
19-
github.com/DotNetAge/goharness v0.2.21/go.mod h1:2+Ze4Att5hP3oRR9/5Rqv506hr38G5qwaWQz6ChOu8c=
20-
github.com/DotNetAge/gorag/v2 v2.0.9 h1:kO0dmD2Ca6Zm7OGt9R8OiGwGNkLBsdn1cw2S1NPqBqk=
21-
github.com/DotNetAge/gorag/v2 v2.0.9/go.mod h1:K8YAydeJR41JMY59xrzgMNDKRHVg3cwB2pYwJSwVjbE=
2218
github.com/DotNetAge/gort v0.1.4 h1:nUZdy3cN3Kif21GWIYkxlIS/iFr62eN5boBmCzmI3xw=
2319
github.com/DotNetAge/gort v0.1.4/go.mod h1:m6mjzyaP/Ufx+Uv7uh51BSK7N4LVR/viWEHV9ZQ3OM8=
2420
github.com/DotNetAge/govector v0.1.8 h1:LwNJecCbLgj0ISXBRUCQziPYBbYxtr+3jVEKvXzK7wo=

internal/tools/quick_search.go

Lines changed: 73 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -93,23 +93,18 @@ func (t *QuickSearch) Execute(ctx context.Context, params map[string]any) (any,
9393
}
9494
}
9595

96-
gq := query.NewGraphQuery(queryStr).(*query.GraphQuery)
97-
gq.SetTextQuery("") // Force vector search + entity enrichment, skip LLM text→Cypher path
98-
gq.SetLimit(limit)
99-
gq.SetDepth(1)
100-
10196
// Apply projectDir filter — default to current working directory
10297
projectDir, _ := params["projectDir"].(string)
10398
if projectDir == "" {
10499
if cwd, err := os.Getwd(); err == nil {
105100
projectDir = cwd
106101
}
107102
}
108-
if projectDir != "" {
109-
regionID := fmt.Sprintf("%x", sha256.Sum256([]byte(filepath.Clean(projectDir))))
110-
gq.AddFilter("region_id", regionID)
111103

112-
// Pre-check: skip search if no indexed data for this region
104+
// Pre-check + 预计算 regionID 供 per-token 使用
105+
var regionID string
106+
if projectDir != "" {
107+
regionID = fmt.Sprintf("%x", sha256.Sum256([]byte(filepath.Clean(projectDir))))
113108
total, countErr := t.indexer.CountByRegion(ctx, projectDir)
114109
if countErr == nil && total == 0 {
115110
return map[string]any{
@@ -118,23 +113,56 @@ func (t *QuickSearch) Execute(ctx context.Context, params map[string]any) (any,
118113
}
119114
}
120115

121-
hits, err := t.indexer.Search(ctx, gq)
122-
if err != nil {
123-
return nil, fmt.Errorf("QuickSearch 失败:%w", err)
124-
}
116+
// 将查询按空白符拆分为多个关键词,分别检索后合并去重。
117+
// LLM 倾向于输入空格分隔的关键词而非自然语句(如 "redis 迁移 配置"),
118+
// 多次查询比单次语义搜索能召回更全面的结果。
119+
tokens := splitQueryTokens(queryStr)
125120

126-
// Fallback: retry without region_id filter when region-filtered search yields nothing
127-
if len(hits) == 0 {
128-
gq2 := query.NewGraphQuery(queryStr).(*query.GraphQuery)
129-
gq2.SetTextQuery("")
130-
gq2.SetLimit(limit)
131-
gq2.SetDepth(1)
132-
hits2, err2 := t.indexer.Search(ctx, gq2)
133-
if err2 == nil && len(hits2) > 0 {
134-
hits = hits2
121+
var allHits []core.Hit
122+
seen := make(map[string]bool)
123+
124+
for _, token := range tokens {
125+
gq := query.NewGraphQuery(token).(*query.GraphQuery)
126+
gq.SetTextQuery("") // Force vector search + entity enrichment, skip LLM text→Cypher path
127+
gq.SetLimit(limit)
128+
gq.SetDepth(1)
129+
130+
if regionID != "" {
131+
gq.AddFilter("region_id", regionID)
132+
}
133+
134+
hits, err := t.indexer.Search(ctx, gq)
135+
if err != nil {
136+
// 单个 token 查询失败,跳过
137+
continue
138+
}
139+
140+
// Fallback: retry without region_id filter when region-filtered search yields nothing
141+
if len(hits) == 0 {
142+
gq2 := query.NewGraphQuery(token).(*query.GraphQuery)
143+
gq2.SetTextQuery("")
144+
gq2.SetLimit(limit)
145+
gq2.SetDepth(1)
146+
hits2, err2 := t.indexer.Search(ctx, gq2)
147+
if err2 == nil && len(hits2) > 0 {
148+
hits = hits2
149+
}
150+
}
151+
152+
for _, h := range hits {
153+
if seen[h.ID] {
154+
continue
155+
}
156+
seen[h.ID] = true
157+
allHits = append(allHits, h)
135158
}
136159
}
137160

161+
hits := allHits
162+
if len(hits) > limit {
163+
hits = hits[:limit]
164+
}
165+
138166
// Filter by tags if specified (post-filter)
139167
if raw, ok := params["tags"].([]any); ok && len(raw) > 0 {
140168
var filterTags []string
@@ -167,6 +195,29 @@ func (t *QuickSearch) Execute(ctx context.Context, params map[string]any) (any,
167195
return formatQuickSearchResults(queryStr, hits), nil
168196
}
169197

198+
// splitQueryTokens 将查询拆分为多个关键词。
199+
// 如果查询本身是自然语句(含空格但长度 > 50),视为完整查询不拆分。
200+
// 否则按空白符拆分为多个关键词,过滤掉过短的词。
201+
func splitQueryTokens(query string) []string {
202+
if len(query) > 50 {
203+
return []string{query}
204+
}
205+
parts := strings.Fields(query)
206+
if len(parts) <= 1 {
207+
return []string{query}
208+
}
209+
tokens := make([]string, 0, len(parts))
210+
for _, p := range parts {
211+
if len(p) >= 2 {
212+
tokens = append(tokens, p)
213+
}
214+
}
215+
if len(tokens) == 0 {
216+
return []string{query}
217+
}
218+
return tokens
219+
}
220+
170221
// ── QuickSearch output formatting ─────────────────────────────────────────────────
171222

172223
func formatQuickSearchResults(query string, hits []core.Hit) string {
Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

runtime/web/assets/architecture-7EHR7CIX-B0IKWh1v.js

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import{x as e}from"./mermaid-parser.core-CjNDvZPI.js";export{e as createArchitectureServices};

runtime/web/assets/architectureDiagram-3BPJPVTR-CHyLBo5T.js renamed to runtime/web/assets/architectureDiagram-3BPJPVTR-BRaask9i.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)