go-elasticsearch/v8是当前唯一推荐客户端,olivere/elastic已归档且不兼容ES 8.x+;esapi.SearchRequest.Body必须为*bytes.Reader,需用bytes.NewReader(jsonBytes)而非直接传字节切片。
go-elasticsearch/v8 是当前唯一推荐的客户端,olivere/elastic 已归档且不兼容 ES 8.x+,连不上就直接返回 406 Not Acceptable 或静默空响应——不是代码写错了,是协议层就断在了第一步。很多人把 json.Marshal() 的字节切片直接塞进 Body 字段,结果 ES 返回 400 Bad Request,日志里只有一句 “invalid request body”。根本原因是 esapi.SearchRequest.Body 类型是 io.ReadSeeker,不是 []byte。
Body: jsonBytes(编译可能过,但运行时请求体为空)Body: bytes.NewReader(jsonBytes)
map[string]interface{},避免手拼字符串引号嵌套出错;例如:map[string]interface{}{"query": map[string]interface{}{"match": map[string]interface{}{"title": "golang"}}}
"analyzer": "ik_max_word",否则默认走标准分词器,“人工智能”被切成“人工”“智能”,而索引时用 ik 存的是“人工智能”这个词项DSL 语法合法,ES 不报错,但 TotalHits.Value() 始终为 0——问题出在字段类型和查询语义错配。
MatchQuery("title", "golang") 适用于 text 类型字段,会触发分词和相关性打分TermQuery("status", "published") 才能匹配 keyword 类型字段;用 MatchQuery 查 keyword 字段等于白查Must:全文搜放 BoolQuery().Must(),状态/范围过滤放 .Filter()(不打分、可缓存、性能更好)RangeQuery("created_at").Gte("now-7d").Lte("now") 是 ES 原生支持的表达式,不用转成具体时间戳result, err := req.Do(ctx) 成功,但 result.Hits.Hits 是空 slice,或 hit.Source 解析后字段全是零值——不是没数据,是 JSON key 对不上。
hit._source 下,不是顶层字段;不能 json.Unmarshal(raw, &doc)
hit.Source 方法:err := json.Unmarshal(*hit.Source, &doc),注意 *hit.Source 是指针解引用user_id ≠ userId,created_at ≠ createdAt,差一个下划线就查得到但解析失败"properties": {"tags": {"type": "keyword"}},结构体就得写 Tags string `json:"tags"`,不能写成 Tags []string(除非 mapping 明确设了 "index": false 或用了 nested)90% 的 “context deadline exceeded” 和 “401 Unauthorized” 都卡在 elasticsearch.NewClient() 这一步,不是网络问题,是配置没对齐。
立即学习“go语言免费学习笔记(深入)”;
Addresses: []string{"http://elasticsearch.default.svc.cluster.local:9200"}(K8s Service 名),别信默认 localhost
Transport: &http.Transport{...} 并设 Timeout,v8 默认无超时,goroutine 会悬停Sniff: false,Docker/K8s 里 sniff 会尝试连内网 IP,直接失败Username 和 Password 字段;漏一个,错误就是一行 401 Unauthorized,没有更多上下文hits 或 406 这类非 5xx 状态码;调试时得先看 res.StatusCode 和 res.String(),而不是只盯着 err != nil。