elasticsearch多条件查询需用boolquery.must()处理全文检索(如matchquery),filter()处理精确匹配(如termquery、rangequery),字段名须与mapping严格一致,且客户端版本必须匹配es大版本。

用 BoolQuery.Must().Filter() 组合全文搜和精确过滤
ES 多条件查不是拼 JSON,而是靠 query 类型分工:全文搜走 MatchQuery,状态/ID/范围这类精确匹配必须用 TermQuery 或 RangeQuery。Gin 路由里接收参数后直接塞进 BoolQuery,别混用。
常见错误是把 status 字段(mapping 是 keyword)用 MatchQuery 查,结果永远空——MatchQuery 会分词,keyword 不分词,根本对不上。
-
Must()放全文检索类条件,比如MatchQuery("title", keyword)、MatchPhraseQuery("content", phrase) -
Filter()放精确匹配或范围,比如TermQuery("status", "published")、RangeQuery("created_at").Gte("2024-01-01");它不参与算分,性能更好 - 多个
Filter()条件之间是 AND 关系,不用再套一层BoolQuery - 字段名必须和 mapping 完全一致:
user_id≠userId,错一个就查不到,也不报错
解析 Hits 时别直接 Unmarshal 到结构体
SearchResult.Hits.Hits 里的每个 hit,真实数据藏在 hit.Source 字段里,它是 *json.RawMessage 类型。直接 json.Unmarshal(hit, &MyStruct{}) 会 panic 或字段为空。
正确做法是先判空,再解指针:
for _, hit := range result.Hits.Hits {
if hit.Source == nil {
continue
}
var doc MyDoc
if err := json.Unmarshal(*hit.Source, &doc); err != nil {
// log error
continue
}
// use doc
}
另外 result.Hits.TotalHits.Value() 才是真实命中数,result.Hits.TotalHits 是个接口,直接打印是地址值。
客户端版本必须匹配 ES 大版本
你现在装的是 ES 8.x 或 9.x?那 olivere/elastic/v7 就不能用——不是写法错,是协议断层:ES 移除了 _type、强制 HTTPS、改了 /_search 路径、要求 Content-Type: application/json,v7 客户端发的请求 ES 直接拒收,返回 406 Not Acceptable 或静默空结果。
实操判断方式:
- ES 7.10–7.17 → 可用
github.com/olivere/elastic/v7 - ES 8.0+(包括刚装的 8.15 / 9.0)→ 必须用
github.com/elastic/go-elasticsearch/v9 - 别混用两个客户端:它们的
http.RoundTripper和context.Context实现冲突,容易 goroutine 悬停
Gin 接收参数后要校验再传给 ES
Gin 的 c.Query("q") 或 c.ShouldBindJSON(&req) 拿到参数后,别直接喂给 MatchQuery。空字符串、超长关键词、SQL 注入式输入(如 *) OR (*:)都可能触发 ES 解析失败或慢查询。
建议加一层轻量校验:
- 关键词长度限制在 1–100 字符,过短易误匹配,过长影响分词效率
- 过滤掉控制字符和常见注入 pattern,比如
~ * ? :在非 wildcard 场景下应拒绝 - 时间范围参数用
time.Parse验证格式,再转成 ES 支持的字符串(如"now-7d") - 状态字段值做白名单检查:
if !slices.Contains([]string{"draft", "published", "archived"}, status) { return }
ES 查询本身不校验业务逻辑,参数污染会直接变成线上慢查询或 400 错误,而 Gin 这层是最容易拦截的位置。











