must()只放全文检索条件,filter()处理精确过滤;时间范围用rangequery;字段名须与mapping严格一致;查询必须传带超时的ctx;解析hits用hit.source;客户端禁sniff、开healthcheck、配auth。

用 BoolQuery.Must() + Filter() 区分语义,别全塞 Must
全文检索和精确过滤混在同一个 Must() 里,查不到数据还不会报错——ES 默认静默忽略类型不匹配的条件。比如 status 是 keyword 类型,却用 MatchQuery("status", "published"),结果就是空 hits。
实操建议:
-
Must()只放参与相关性打分的全文条件,例如MatchQuery("title", keyword)或MultiMatchQuery - 状态、枚举、ID、时间范围等精确条件,一律走
Filter():它不打分、不计算 TF-IDF,性能更好,还能被缓存 - 时间范围别拼字符串,用
RangeQuery("created_at").Gte("2024-01-01").Lte("now-1d"),ES 原生支持now-7d这类表达式 - 字段名必须和 mapping 完全一致:
user_id和userId是两个字段,查错名字不会报错,只会返回 0 条
封装 SearchService 时显式传 ctx 并设超时,别依赖全局 default
没带 context.WithTimeout() 直接调 Do(ctx),一旦 ES 响应慢或网络抖动,整个 goroutine 就卡死。生产环境常见 “查询偶尔 hang 住” 都是这原因。
实操建议:
- 每个查询入口函数都接收
ctx context.Context,外部调用方负责控制生命周期 - 内部统一用
ctx, cancel := context.WithTimeout(ctx, 3*time.Second),查完立刻cancel() - 避免用
context.Background()或context.TODO()硬编码在 service 层 - 如果要重试,别在 service 内部做,交给上层用
backoff.Retry控制,保持职责清晰
解析 Hits 要用 Hit.Source,别直接 json.Unmarshal(res.Body)
直接对 SearchResult 的原始 body 做 json.Unmarshal,会漏掉高亮、排序值、_score 等关键元信息,而且字段映射错位风险极高。
实操建议:
- 遍历
res.Hits.Hits,对每个*elastic.SearchHit调hit.Source获取原始 JSON 字节 - 再用
json.Unmarshal(hit.Source, &yourStruct)解析到结构体,确保字段 tag(如json:"title")和 mapping 一致 - 需要高亮?从
hit.Highlight拿 map[string][]string,键是字段名,值是带<em></em>的片段 - 总数别信
res.Hits.TotalHits,它可能是 int64 或 *int64;安全写法是res.Hits.TotalHits.Value()
客户端初始化必须关 Sniff、开 Healthcheck、配 BasicAuth(如有)
本地跑通了,一上 K8s 就连不上?90% 是因为 SetSniff(true)(默认值)触发自动发现,客户端去连 ES 内网 Pod IP,而你的服务根本访问不到那个地址。
实操建议:
- 强制设
elastic.SetSniff(false),只认你给的SetURL("http://es-svc:9200") - 必须开健康检查:
elastic.SetHealthcheck(true)+elastic.SetHealthcheckInterval(15*time.Second) - 超时不能省:
elastic.SetTimeout(5*time.Second),否则一次慢查询拖垮整个连接池 - ES 开了 Basic Auth?漏掉
elastic.SetBasicAuth("user", "pass"),错误就只是401 Unauthorized,没有更多线索
text 字段用 TermQuery 就永远查不到,keyword 字段用 MatchQuery 就等于白配。这个错不在代码语法,而在 mapping 和 query 的语义错位,调试时得翻 _mapping API 确认字段类型,而不是只看业务逻辑。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











