Go-Elasticsearch Typed Client查询请求的两种写法强类型 Request 与 Raw JSON

发布于:2025-07-30 ⋅ 阅读:(16) ⋅ 点赞:(0)

1 为什么需要两种写法?

在 Golang 项目中访问 Elasticsearch,一般会遇到两类需求:

需求场景 特点 最佳写法
后台服务 / 业务逻辑 查询固定、字段清晰,需要编译期保障 Request 结构体
仪表盘 / 高级搜索 / 模板 DSL 查询片段由前端或脚本动态生成,或要沿用历史 JSON 文件 Raw JSON

Typed Client 同时提供两种 API:.Request(&req).Raw([]byte),互不冲突,互有侧重。

2 使用强类型 Request —— 类型安全 + IDE 补全

2.1 代码示例:搜索 name="Foo"

package main

import (
    "context"
    "encoding/json"
    "fmt"
    "log"

    typedapi "github.com/elastic/go-elasticsearch/v8/typedapi"
    "github.com/elastic/go-elasticsearch/v8/typedapi/search"
    "github.com/elastic/go-elasticsearch/v8/typedapi/types"
)

type Product struct {
    Name  string  `json:"name"`
    Price float64 `json:"price"`
}

func main() {
    // 1) 创建 Typed Client
    es, _ := typedapi.NewTypedClient(typedapi.Config{
        Addresses: []string{"http://localhost:9200"},
    })

    // 2) 构造强类型请求体
    req := search.Request{
        Query: &types.Query{
            Term: map[string]types.TermQuery{
                "name": {Value: "Foo"},
            },
        },
    }

    // 3) 发送查询
    res, err := es.
        Search().
        Index("products").
        Size(10).
        Request(&req).          // ← 传入 Request 结构体
        Do(context.Background())
    if err != nil {
        log.Fatalf("search: %v", err)
    }
    defer res.Body.Close()

    // 4) 解析响应(Raw → 业务结构体)
    var body struct {
        Hits struct {
            Hits []struct {
                Source Product `json:"_source"`
            } `json:"hits"`
        } `json:"hits"`
    }
    _ = json.NewDecoder(res.Body).Decode(&body)

    for _, hit := range body.Hits.Hits {
        fmt.Printf("%+v\n", hit.Source)
    }
}
2.2 优缺点
优点 说明
编译期校验 DSL 字段、类型、枚举均受 Go 类型系统约束
IDE 智能提示 自动补全复杂结构(Query, Sort, Aggregation 等)
易于重构 字段改动立即触发编译错误,避免运行期踩坑
注意点 说明
依赖 spec 版本 新 API 字段需等待官方更新 elasticsearch-specification 生成代码
编码速度 初学者需花时间熟悉类型层级

3 使用 Raw JSON —— 复用模板 / 自定义编码

3.1 代码示例:用户 ID 精确匹配

package main

import (
    "context"
    "fmt"
    "log"

    typedapi "github.com/elastic/go-elasticsearch/v8/typedapi"
)

func main() {
    es, _ := typedapi.NewTypedClient(typedapi.Config{
        Addresses: []string{"http://localhost:9200"},
    })

    // Mustache / Kibana 导出的查询片段
    rawQuery := []byte(`{
      "query": {
        "term": {
          "user.id": {
            "value": "kimchy",
            "boost": 1.0
          }
        }
      }
    }`)

    res, err := es.
        Search().
        Index("twitter").
        Raw(rawQuery).          // ← 直接塞入 JSON
        Do(context.Background())
    if err != nil {
        log.Fatalf("search: %v", err)
    }
    defer res.Body.Close()

    fmt.Println(res.Status()) // 200 OK
}
3.2 优缺点
优点 说明
模板友好 可与前端、Dashboard 共用一份纯 JSON
零等待 新 API 字段、Beta 特性不依赖生成器
可替换 Encoder 想要 jsonitereasyjson?直接先序列化再 .Raw()
注意点 说明
无校验 DSL 拼写 / 字段错位不会在编译期发现
最高优先级 .Raw() 覆盖一切;之后再 .Query().Request() 都被忽略

4 优雅切换策略

经验法则

  • 80 % 固定业务查询Request 结构体(静态安全)
  • 20 % 动态或实验性查询Raw JSON(灵活兜底)

在实际工程里,可将两套方案封装成 Repository / DSL Builder

type ProductRepo struct {
    es *typedapi.TypedClient
}

func (r *ProductRepo) ByName(ctx context.Context, name string) { /* Request 结构体 */ }
func (r *ProductRepo) ByTemplate(ctx context.Context, tpl []byte) { /* Raw JSON */ }

这样调用层永远只见到 强类型方法签名,底层细节由仓库层决定,是不是很优雅?🤘

5 小结

维度 Request 结构体 Raw JSON
类型安全 ✔✔✔
IDE 补全
学习成本 低(已有模板)
新字段适配 需等生成器 立即可用
性能自定义 默认 encoding/json 自选 Encoder

Typed Client 让 Go + Elasticsearch 在保持类型安全的同时,又给出了面向未来的 Raw JSON 逃生口。只需根据「查询稳定性 & 模板复用程度」做权衡,就能兼顾 可靠性灵活性,写出更易维护、更易拓展的搜索服务。


网站公告

今日签到

点亮在社区的每一天
去签到