必须使用elastic/go-elasticsearch/v8连接es 8.x/9.x,因v7不兼容type移除、强制https等变更;初始化需配置transport超时与tls校验、请求带context超时、响应体手动关闭;中文搜索需写入与查询统一指定analyzer如ik_max_word;esapi.searchrequest必填index、body、context;结构体tag须与mapping严格一致;建索引须主动传mapping,查询须手写json body。

新项目必须用 elastic/go-elasticsearch/v8,不是“可选”,是“不换就连不上 ES 8.x/9.x”。你本地装的最新版 Elasticsearch(比如 9.0),用 olivere/elastic/v7 调 client.Search() 不会报错,而是静默返回空结果或 406 Not Acceptable —— 因为 v7 没适配 type 移除、强制 HTTPS、Content-Type: application/json 等底层变更。
怎么初始化 v8 客户端才不会卡死或泄露 goroutine
v8 默认不设超时,HTTP 连接可能无限等待;也不自动关闭响应体,fd 会慢慢耗尽。这不是理论风险,线上真会卡住整个服务。
-
elasticsearch.Config{Addresses: []string{"http://localhost:9200"}}必须显式配Transport:加&http.Transport{IdleConnTimeout: 30 * time.Second},否则连接池空闲连接永不释放 - 开发环境若用 HTTP(非 HTTPS),必须关 TLS 校验:
Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} - 每个请求都得带
context.Context,且建议用context.WithTimeout(ctx, 5*time.Second),别依赖全局 context - 每次调
req.Do(client)后,必须defer res.Body.Close()—— v8 不帮你做,漏了就是 fd 泄露
为什么中文搜不到?90% 是 analyzer 不一致
写入时用了 ik_max_word,但查询没指定同名分词器,ES 就默认走 standard 分词器。“人工智能”被切成 “人工”“智能”,而 IK 会保留 “人工智能” 整体词项,自然匹配失败。
- 索引 mapping 中字段要明确定义:
"analyzer": "ik_max_word"(例如"content": {"type": "text", "analyzer": "ik_max_word"}) - 查询时 match/multi_match 必须同步指定:
"analyzer": "ik_max_word",不能省略 - 验证方式:用 Kibana Dev Tools 执行
GET /your_index/_analyze?field=content,输入词看切分结果是否符合预期 - 别信“自动继承”,ES 不自动把 mapping 的 analyzer 带到 query 层,必须显式传
esapi.SearchRequest 三个必填字段漏一个就白发请求
esapi.SearchRequest 不补默认值,字段为空就等于发了个废请求——返回 no search context found 或空 hits,但 error 是 nil,你根本看不出哪错了。
-
Index:不能是空 slice 或"*",必须是明确存在的索引名,如[]string{"articles"} -
Body:必须是*bytes.Reader,常见错误是json.Marshal()后直接传字节切片,忘了包一层bytes.NewReader(),导致请求体为空,ES 返回400 Bad Request -
Context:必须带超时,例如context.WithTimeout(ctx, 3*time.Second);不设的话,HTTP client 可能卡在连接池里,goroutine 悬停
结构体字段和 mapping 对不齐,查出来就是 nil
Go 结构体字段 tag 和 ES mapping 类型不匹配,会导致 _source 解析失败,哪怕文档存在,res.Hits.Hits[0].Source 也是空 map 或部分字段丢失。
- 时间字段用
time.Time+json:"created_at,omitempty",mapping 中对应"type": "date",避免空值干扰 - 不想被索引的字段,加
json:"-",别只靠elastic:"-",v8 不读这个 tag - 嵌套对象字段(如
user.name)要在 mapping 中定义为object类型,Go 里用结构体嵌套,tag 写成json:"user",别写json:"user.name" - 首次建索引前,用
client.Indices.Create()主动传 mapping JSON,别依赖自动 mapping,尤其对中文分词字段
最易被忽略的是:v8 客户端没有 MatchQuery 这类链式 DSL 方法,所有查询都得手写 JSON body。map 构造比 raw string 更安全,但必须确保 key 全是 string,value 类型与 ES 期望一致——比如 range 查询的 "gte" 值不能是 int64,得是 float64 或 string 格式日期。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











