必须用elastic/go-elasticsearch/v8(或v9),olivere/elastic/v7不兼容ES 8.x,因type移除、HTTPS强制启用、_search路径变更及Content-Type要求等API差异,会导致406错误或静默失败;v7已归档,v8为官方维护主线。

选哪个客户端:go-elasticsearch/v8 还是 olivere/elastic/v7?
必须用 elastic/go-elasticsearch/v8(或 v9),olivere/elastic/v7 在 ES 8.0+ 上根本连不上——不是报错,而是直接返回 406 Not Acceptable 或空响应。ES 8.x 移除了 type、强制 HTTPS、改了 _search 路径、要求 Content-Type: application/json,而 olivere/v7 完全没适配这些变更。
如果你刚装的 ES(比如 8.15 或 9.0),olivere/elastic 一调 client.Search() 就静默失败;v8 客户端则默认校验 header、自动设 application/json,还支持 API key 认证和 explain 调试。
- v7 已归档,不再接收任何 PR 或安全更新
- v8 是官方维护主线,持续适配 8.x 新特性(如 searchable snapshot、EQL)
- v8 强制你显式处理
context.Context和res.Body.Close(),避免 goroutine 悬停和 fd 泄露
esapi.SearchRequest 必须填的三个字段
很多人写完 JSON body 就调 .Do(),结果返回空 hits 或 no search context found。这是因为 esapi.SearchRequest 不补默认值,漏一个就等于发了个废请求。
-
Index:不能为"",也不能传"*"(生产环境 ES 会拒收或打满慢日志);必须明确指定索引名,比如[]string{"blogs"} -
Body:必须是*bytes.Reader,常见错误是json.Marshal()后忘了包一层bytes.NewReader(),导致请求体为空,ES 返回400 Bad Request -
Context:必须带超时,例如context.WithTimeout(ctx, 5*time.Second);不设的话,HTTP client 可能卡在连接池里,整个 goroutine 悬停
示例:
立即学习“go语言免费学习笔记(深入)”;
req := esapi.SearchRequest{
Index: []string{"blogs"},
Body: bytes.NewReader([]byte(`{"query":{"match":{"title":"golang"}}}`)),
Context: context.WithTimeout(ctx, 3*time.Second),
}
res, err := req.Do(client)
中文搜索搜不到?检查 analyzer 是否一致
写入时用了 ik_max_word 分词器,但查询没指定 "analyzer": "ik_max_word",就会默认走标准分词器——“人工智能”被切成 “人工”“智能”,而 IK 会切出 “人工智能” 这个完整词项,自然匹配不上。
- 索引 mapping 中字段要明确定义
"analyzer": "ik_max_word" - 查询时 match/multi_match 等语句里,加
"analyzer": "ik_max_word"字段 - 别依赖默认 analyzer;ES 默认对 text 字段用
standard,对 keyword 字段不分析,二者行为完全不同
错误写法:{"query":{"match":{"title":"人工智能"}}} → 用 standard 切分
正确写法:{"query":{"match":{"title":{"query":"人工智能","analyzer":"ik_max_word"}}}}
MySQL 写完立刻搜不到?refresh 不是 bug,是机制
这不是代码问题,是 Elasticsearch 的 near real-time 特性:文档写入后默认 1 秒才可被搜索到。MySQL 的 INSERT 是立即可见的,但 ES 需要 refresh 周期触发 segment 提交。
- 开发调试可用
Refresh: "true"强制刷新,但只适合单文档,批量导入时会严重拖慢吞吐 - 生产环境应设为
Refresh: "wait_for"(等待下一次自动 refresh)或干脆不设,靠默认 1s 间隔 - 高并发写+查场景下,如果依赖“写完立刻搜”,建议加短重试(最多 2 次,间隔 500ms),而不是改 refresh
最易忽略的一点:即使你写了 Refresh: "true",也要确保调用的是 esapi.IndexRequest{}.Do(),而不是旧版 client.Index() —— 后者在 v8 中已弃用,参数不生效。


















