es查询高阶语法

中间件 / 2025-04-03

主要记录项目中使用的es高阶查询语法,es版本7.x
基于以下相关依赖

<dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-client</artifactId>
</dependency>

一、首先构建一个查询请求即SearchRequest

SearchRequest构造函数包含索引名称以及SearchSourceBuilder

SearchSourceBuilder的主要方法

SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

1、fetchSource

fetchSource既可以指定返回的字段名称,也可以指定排除的字段名称,一般我们指定我们所需要返回的字段就够了

searchSourceBuilder.fetchSource("需要的字段数组",null)

2、BoolQueryBuilder 是 Elasticsearch 中用于构建 布尔查询(Boolean Query) 的核心工具类,它允许通过逻辑组合多个查询条件(如 mustshouldmust_notfilter

  • must: 文档必须匹配这些条件,且会影响相关性评分(_score)。

  • should: 文档满足这些条件会增加相关性评分,若没有 must,则至少一个 should 需匹配(可通过 minimum_should_match 控制)。

  • must_not: 文档必须不匹配这些条件,不参与评分(常用于排除特定结果)。

  • filter: 过滤文档(不计算评分),但结果可被缓存,适合性能敏感的场景。

    以电商项目背景为例,

    在Es中查询商品为 上架 且 库存充足 且 商品类别 不为 实物商品 且配送方式 为 无需快递 且价格区间在100-500区间 的商品

    BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
    boolQueryBuilder.filter(QueryBuilders.termsQuery(EsConstant.STATUS, 1)) // 1代表上架boolQueryBuilder.filter(QueryBuilders.termQuery(EsConstant.HAS_STOCK, 1)) // 1代表库存充足
    boolQueryBuilder.mustNot(QueryBuilders.termQuery(EsConstant.MOLD, 1))//1代表实物
    boolQueryBuilder.filter(QueryBuilders.termsQuery(EsConstant.DELIVERIES,1)) //1代表无需快递配送
    boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(100).lte(500))//价格区间
    
    searchSourceBuilder.query(boolQueryBuilder);
    

关于must和filter的选择,

mustfilter 它们的核心区别在于是否影响文档的 相关性评分(_score 以及是否对结果进行缓存。在电商场景中,选择使用 must 还是 filter 需要根据业务需求决定:


核心区别

特性 must filter
是否影响评分 ✅ 影响(计算相关性得分) ❌ 不影响(仅过滤,评分固定为 1.0
是否缓存结果 ❌ 不缓存 ✅ 缓存(性能更高)
适用场景 需要相关性排序的条件(如关键词匹配) 纯过滤条件(如价格、库存、分类筛选)

举个例子,如果搜索的是商品名称关键字,这种情况就需要用must。

如果搜索的是分类等这种确定性的指标,就可以用filter。

3、排序

此处采用的是脚本方式

// 构建脚本
String scriptCode = "double earthRadius = 6371; " +  // 地球半径(单位:千米)
    "double lat = doc['latitude'].value; " +
    "double lon = doc['longitude'].value; " +
    "double dLat = Math.toRadians(params.targetLat - lat); " +
    "double dLon = Math.toRadians(params.targetLon - lon); " +
    "double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + " +
    "Math.cos(Math.toRadians(lat)) * Math.cos(Math.toRadians(params.targetLat)) * " +
    "Math.sin(dLon / 2) * Math.sin(dLon / 2); " +
    "double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); " +
    "return earthRadius * c;";  // 返回距离(单位:千米)

// 创建脚本对象
Script script = new Script(ScriptType.INLINE, "painless", scriptCode, 
    Collections.singletonMap("targetLat", 39.9055),  // 用户纬度
    Collections.singletonMap("targetLon", 116.3976)); // 用户经度

// 构建 ScriptSortBuilder
ScriptSortBuilder sortBuilder = new ScriptSortBuilder(script, ScriptSortBuilder.ScriptSortType.NUMBER)
    .order(SortOrder.ASC)  // 升序排列(距离由近到远)
    .unit("km");  // 单位:千米(可选值:km, mi)
.sort(sortBuilder)
    
 searchSourceBuilder.sort(sortBuilder)

4、构建最终的SearchRequest

SearchRequest searchRequest = SearchRequest(new String[]{EsIndexEnum.PRODUCT.value()},searchSourceBuilder);
response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);