Skip to content

Commit 0cfaedd

Browse files
committed
添加搜索高亮功能支持与测试优化:1. 在Response结构体中添加Highlighting字段支持搜索高亮 2. 创建新的搜索高亮功能API 3. 添加功能测试与单元测试 4. 优化现有测试用例,减少测试数据量并添加超时控制
1 parent d9dec58 commit 0cfaedd

File tree

5 files changed

+511
-88
lines changed

5 files changed

+511
-88
lines changed

pkg/api/class_search.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package api
2+
3+
import (
4+
"context"
5+
"errors"
6+
7+
"github.com/scagogogo/sonatype-central-sdk/pkg/request"
8+
"github.com/scagogogo/sonatype-central-sdk/pkg/response"
9+
)
10+
11+
// SearchClassesWithHighlighting 根据完全限定类名搜索制品,并返回高亮信息
12+
// 参数:
13+
// - ctx: 上下文,可用于取消或设置超时
14+
// - fullyQualifiedClassName: 完全限定类名,如"org.specs.runner.JUnit"
15+
// - limit: 最大返回结果数量,如果小于等于0则不限制
16+
//
17+
// 返回:
18+
// - 搜索结果: 包含匹配的版本信息以及高亮片段
19+
// - 错误: 如果搜索过程中发生错误
20+
func (c *Client) SearchClassesWithHighlighting(ctx context.Context, fullyQualifiedClassName string, limit int) (*response.Response[*response.Version], error) {
21+
// 创建搜索请求
22+
query := request.NewQuery().SetFullyQualifiedClassName(fullyQualifiedClassName)
23+
searchReq := request.NewSearchRequest().SetQuery(query)
24+
25+
// 设置限制条数
26+
if limit > 0 {
27+
searchReq.SetLimit(limit)
28+
}
29+
30+
// 添加高亮相关参数
31+
searchReq.AddCustomParam("hl", "true")
32+
searchReq.AddCustomParam("hl.fl", "fch") // 高亮完全限定类名字段
33+
searchReq.AddCustomParam("hl.snippets", "3") // 最多返回3个高亮片段
34+
35+
// 执行搜索请求
36+
result, err := SearchRequestJsonDoc[*response.Version](c, ctx, searchReq)
37+
if err != nil {
38+
return nil, err
39+
}
40+
41+
// 验证响应
42+
if result == nil || result.ResponseBody == nil {
43+
return nil, errors.New("empty response body")
44+
}
45+
46+
return result, nil
47+
}
48+
49+
// ExtractHighlightedClasses 从搜索结果中提取出每个制品中的高亮类名
50+
// 参数:
51+
// - result: 带有高亮信息的搜索结果
52+
//
53+
// 返回:
54+
// - 映射关系: 文档ID到该文档中包含的高亮类名列表
55+
func ExtractHighlightedClasses(result *response.Response[*response.Version]) map[string][]string {
56+
if result == nil || result.Highlighting == nil {
57+
return nil
58+
}
59+
60+
// 创建结果映射
61+
highlightedClasses := make(map[string][]string)
62+
63+
// 遍历每个文档的高亮信息
64+
for docId, fields := range result.Highlighting {
65+
// 提取类字段(fch)的高亮片段
66+
if classHighlights, exists := fields["fch"]; exists && len(classHighlights) > 0 {
67+
highlightedClasses[docId] = classHighlights
68+
}
69+
}
70+
71+
return highlightedClasses
72+
}
73+
74+
// SearchFullyQualifiedClassNames 搜索完全限定类名并返回包含这些类的所有制品版本
75+
// 参数:
76+
// - ctx: 上下文,可用于取消或设置超时
77+
// - className: 要搜索的完全限定类名或部分类名(支持模式匹配)
78+
// - limit: 最大返回结果数量
79+
//
80+
// 返回:
81+
// - 版本列表: 包含匹配类的所有制品版本
82+
// - 高亮信息: 每个制品中匹配的具体类名
83+
// - 错误: 如果搜索过程中发生错误
84+
func (c *Client) SearchFullyQualifiedClassNames(ctx context.Context, className string, limit int) ([]*response.Version, map[string][]string, error) {
85+
// 执行带高亮的搜索
86+
result, err := c.SearchClassesWithHighlighting(ctx, className, limit)
87+
if err != nil {
88+
return nil, nil, err
89+
}
90+
91+
// 提取高亮的类名
92+
highlights := ExtractHighlightedClasses(result)
93+
94+
return result.ResponseBody.Docs, highlights, nil
95+
}

pkg/api/class_search_test.go

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
package api
2+
3+
import (
4+
"context"
5+
"testing"
6+
"time"
7+
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
// TestSearchClassesWithHighlighting 测试带高亮的类搜索功能
12+
func TestSearchClassesWithHighlighting(t *testing.T) {
13+
// 使用真实客户端
14+
client := createRealClient(t)
15+
16+
// 设置更长的超时时间
17+
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
18+
defer cancel()
19+
20+
// 测试全限定类名
21+
className := "org.apache.commons.io.FileUtils"
22+
23+
// 设置子测试的超时时间
24+
subCtx, subCancel := context.WithTimeout(ctx, 20*time.Second)
25+
defer subCancel()
26+
27+
// 执行带高亮的搜索
28+
result, err := client.SearchClassesWithHighlighting(subCtx, className, 3)
29+
30+
// 如果API连接失败,跳过测试
31+
if err != nil {
32+
t.Logf("搜索 %s 时出错: %v", className, err)
33+
t.Skip("无法连接到Maven Central API")
34+
return
35+
}
36+
37+
// 验证返回结果
38+
assert.NotNil(t, result, "搜索结果不应为空")
39+
assert.NotNil(t, result.ResponseBody, "响应体不应为空")
40+
assert.NotEmpty(t, result.ResponseBody.Docs, "搜索结果不应为空")
41+
42+
// 验证高亮信息
43+
assert.NotNil(t, result.Highlighting, "高亮信息不应为空")
44+
45+
// 输出结果信息
46+
t.Logf("找到 %d 个包含 %s 的结果", result.ResponseBody.NumFound, className)
47+
for i, doc := range result.ResponseBody.Docs {
48+
t.Logf("结果 %d: %s:%s:%s", i+1, doc.GroupId, doc.ArtifactId, doc.Version)
49+
50+
// 显示高亮信息
51+
if highlightInfo, exists := result.Highlighting[doc.ID]; exists {
52+
if fchHighlights, hasFch := highlightInfo["fch"]; hasFch && len(fchHighlights) > 0 {
53+
t.Logf(" 高亮: %s", fchHighlights[0])
54+
}
55+
}
56+
}
57+
}
58+
59+
// TestExtractHighlightedClasses 测试高亮提取函数
60+
func TestExtractHighlightedClasses(t *testing.T) {
61+
// 使用真实客户端
62+
client := createRealClient(t)
63+
64+
// 设置超时
65+
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
66+
defer cancel()
67+
68+
// 测试全限定类名
69+
className := "org.junit.Assert"
70+
71+
// 设置子测试的超时时间
72+
subCtx, subCancel := context.WithTimeout(ctx, 20*time.Second)
73+
defer subCancel()
74+
75+
// 执行带高亮的搜索
76+
result, err := client.SearchClassesWithHighlighting(subCtx, className, 3)
77+
78+
// 如果API连接失败,跳过测试
79+
if err != nil {
80+
t.Logf("搜索 %s 时出错: %v", className, err)
81+
t.Skip("无法连接到Maven Central API")
82+
return
83+
}
84+
85+
// 提取高亮信息
86+
highlights := ExtractHighlightedClasses(result)
87+
88+
// 验证提取的高亮信息
89+
assert.NotNil(t, highlights, "提取的高亮信息不应为空")
90+
assert.NotEmpty(t, highlights, "提取的高亮信息应该包含数据")
91+
92+
// 输出结果信息
93+
t.Logf("从 %d 个结果中提取了 %d 个高亮类名", len(result.ResponseBody.Docs), len(highlights))
94+
for docId, highlightedClasses := range highlights {
95+
t.Logf("文档 ID: %s", docId)
96+
for i, cls := range highlightedClasses {
97+
t.Logf(" 高亮类 %d: %s", i+1, cls)
98+
}
99+
}
100+
}
101+
102+
// TestSearchFullyQualifiedClassNames 测试完全限定类名搜索
103+
func TestSearchFullyQualifiedClassNames(t *testing.T) {
104+
// 使用真实客户端
105+
client := createRealClient(t)
106+
107+
// 设置超时
108+
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
109+
defer cancel()
110+
111+
// 测试全限定类名
112+
className := "org.apache.commons.lang3.StringUtils"
113+
114+
// 设置子测试的超时时间
115+
subCtx, subCancel := context.WithTimeout(ctx, 20*time.Second)
116+
defer subCancel()
117+
118+
// 执行搜索
119+
versions, highlights, err := client.SearchFullyQualifiedClassNames(subCtx, className, 3)
120+
121+
// 如果API连接失败,跳过测试
122+
if err != nil {
123+
t.Logf("搜索 %s 时出错: %v", className, err)
124+
t.Skip("无法连接到Maven Central API")
125+
return
126+
}
127+
128+
// 验证搜索结果
129+
assert.NotNil(t, versions, "搜索结果不应为空")
130+
assert.NotEmpty(t, versions, "搜索结果应该包含数据")
131+
assert.NotNil(t, highlights, "高亮信息不应为空")
132+
133+
// 输出结果信息
134+
t.Logf("找到 %d 个包含 %s 的结果", len(versions), className)
135+
for i, v := range versions {
136+
t.Logf("结果 %d: %s:%s:%s", i+1, v.GroupId, v.ArtifactId, v.Version)
137+
138+
// 显示对应的高亮信息
139+
if hlClasses, exists := highlights[v.ID]; exists {
140+
for j, cls := range hlClasses {
141+
t.Logf(" 高亮类 %d: %s", j+1, cls)
142+
}
143+
}
144+
}
145+
}

0 commit comments

Comments
 (0)