🔍 搜索
按关键词、标签或类型搜索 Packagist 上的包。三个搜索方法共享同一套返回结构 SearchResponse,区别仅在查询参数。
何时使用
- 🔍 用户输入关键词,你想在 UI 里展示匹配的 Packagist 包。
- 🧩 按
tags找同类库(如所有带psr-3标签的日志库)。 - 🏷️ 按
type过滤(如只看composer-plugin类型包)。 - 📊 分页拉取搜索结果做批量分析。
数据模型
SearchResponse
pkg/domain/search.go,对应 https://packagist.org/search.json 响应。
go
type SearchResponse struct {
Results []SearchResult `json:"results"`
Total int `json:"total"`
Next string `json:"next,omitempty"`
}| 字段 | 类型 | 说明 |
|---|---|---|
Results | []SearchResult | 当前页搜索结果列表 |
Total | int | 满足条件的总结果数 |
Next | string | 下一页的 URL(无下一页时为空) |
SearchResult
go
type SearchResult struct {
Name string `json:"name"`
Description string `json:"description"`
URL string `json:"url"`
Repository string `json:"repository"`
Downloads int `json:"downloads"`
Favers int `json:"favers"`
}| 字段 | 类型 | 说明 |
|---|---|---|
Name | string | 包名(vendor/package) |
Description | string | 包描述 |
URL | string | Packagist 上该包的页面 URL |
Repository | string | 源代码仓库 URL |
Downloads | int | 下载次数 |
Favers | int | 收藏数 |
SearchPackages
🔍 通过关键词搜索包。对应 GET https://packagist.org/search.json?q={query}。
签名
go
func (c *ComposerClient) SearchPackages(query string, perPage, page int) (*domain.SearchResponse, error)参数
| 参数 | 类型 | 说明 |
|---|---|---|
query | string | 搜索关键词 |
perPage | int | 每页条数;传 0 或负数表示不发送该参数,由 Packagist 用默认值 |
page | int | 页码(从 1 开始);传 0 或负数表示不发送该参数 |
返回值
| 值 | 类型 | 说明 |
|---|---|---|
| 结果 | *domain.SearchResponse | 搜索结果,含当前页 Results、总 Total、Next 链接 |
| 错误 | error | HTTP 失败、非 200、JSON 解析失败时返回 |
示例
go
package main
import (
"fmt"
"log"
"time"
"github.com/scagogogo/composer-skills/pkg/client"
)
func main() {
c := client.NewComposerClient(30 * time.Second)
res, err := c.SearchPackages("logger", 15, 1)
if err != nil {
log.Fatalf("搜索失败: %v", err)
}
fmt.Printf("共 %d 条结果\n", res.Total)
for _, r := range res.Results {
fmt.Printf("- %s : %s (下载 %d, 收藏 %d)\n",
r.Name, r.Description, r.Downloads, r.Favers)
}
}分页
perPage 与 page 仅在大于 0 时才会作为 per_page / page 查询参数发出。利用 Next 字段可逐页翻:
go
res, _ := c.SearchPackages("logger", 50, 1)
for {
for _, r := range res.Results {
handle(r)
}
if res.Next == "" {
break
}
// 解析 Next URL 取 page,或直接递增 page
page++
res, err = c.SearchPackages("logger", 50, page)
if err != nil {
break
}
}分页参数约定
Packagist 搜索默认每页 15 条。per_page 上限约为 100,超过可能被服务端截断。如需大批量数据,建议改用 ListPackages 直接拉全量包名列表(不走搜索)。
SearchPackagesByTags
🔍 按标签(tags)搜索包。对应 GET https://packagist.org/search.json?tags[]={tag}。
签名
go
func (c *ComposerClient) SearchPackagesByTags(tags []string, perPage, page int) (*domain.SearchResponse, error)参数
| 参数 | 类型 | 说明 |
|---|---|---|
tags | []string | 标签列表,每个标签作为一个 tags 查询参数发出(逻辑与,结果需同时含所有标签) |
perPage | int | 每页条数;<=0 则不发送 |
page | int | 页码;<=0 则不发送 |
返回值
| 值 | 类型 | 说明 |
|---|---|---|
| 结果 | *domain.SearchResponse | 同 SearchPackages |
| 错误 | error | HTTP 失败、非 200、JSON 解析失败时返回 |
示例
go
res, err := c.SearchPackagesByTags([]string{"psr-3", "log"}, 20, 1)
if err != nil {
log.Fatal(err)
}
fmt.Printf("匹配标签的包共 %d 个\n", res.Total)
for _, r := range res.Results {
fmt.Println(r.Name, "-", r.Description)
}多标签语义
每个标签都作为独立的 tags[] 查询参数重复发出,Packagist 会返回同时包含这些标签的包。
SearchPackagesByType
🔍 同时按关键词与包类型搜索。对应 GET https://packagist.org/search.json?q={query}&type={type}。
签名
go
func (c *ComposerClient) SearchPackagesByType(query, packageType string, perPage, page int) (*domain.SearchResponse, error)参数
| 参数 | 类型 | 说明 |
|---|---|---|
query | string | 搜索关键词 |
packageType | string | 包类型,如 library、composer-plugin、project、metapackage |
perPage | int | 每页条数;<=0 则不发送 |
page | int | 页码;<=0 则不发送 |
返回值
| 值 | 类型 | 说明 |
|---|---|---|
| 结果 | *domain.SearchResponse | 同 SearchPackages |
| 错误 | error | HTTP 失败、非 200、JSON 解析失败时返回 |
示例
go
// 只看 composer-plugin 类型的包
res, err := c.SearchPackagesByType("installer", "composer-plugin", 20, 1)
if err != nil {
log.Fatal(err)
}
for _, r := range res.Results {
fmt.Println(r.Name, "-", r.Repository)
}进阶
三个搜索方法的差异
| 方法 | 查询参数 | 适用场景 |
|---|---|---|
SearchPackages | q | 通用关键词模糊搜索 |
SearchPackagesByTags | tags[](可多个) | 按功能标签精确过滤 |
SearchPackagesByType | q + type | 关键词 + 包类型双维度 |
🔗 相关
- 📋 想拿全量包名列表(不分页、不搜索)见 包列表。
- 📦 拿到包名后想看详情见 GetPackage。