Elasticsearch SearchRequest 构建备忘录

一、SearchRequest 构建基础

1. 最简单的查询 - 查询所有文档

java 复制代码
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;

// 方式1:使用 Lambda Builder(推荐)
SearchRequest request = SearchRequest.of(req -> req
        .index("salesdata_idx")  // 索引名
        .size(100)                // 返回条数
);

SearchResponse<Map> response = elasticsearchClient.search(request, Map.class);

2. 精确匹配查询(Term Query)

java 复制代码
import co.elastic.clients.elasticsearch._types.query_dsl.Query;

SearchRequest request = SearchRequest.of(req -> req
        .index("salesdata_idx")
        .query(q -> q
                .term(t -> t
                        .field("status")
                        .value("active")
                )
        )
        .size(50)
);

对应 DSL:

json 复制代码
{
  "query": {
    "term": {
      "status": "active"
    }
  },
  "size": 50
}

3. 多条件组合查询(Bool Query) - 最常用

java 复制代码
SearchRequest request = SearchRequest.of(req -> req
        .index("salesdata_idx")
        .query(q -> q
                .bool(b -> b
                        // MUST: 必须满足(AND)
                        .must(m -> m.term(t -> t.field("geo").value("CN")))
                        .must(m -> m.term(t -> t.field("fiscal_year").value("FY25")))
                        
                        // SHOULD: 满足其一即可(OR)
                        .should(s -> s.term(t -> t.field("status").value("active")))
                        .should(s -> s.term(t -> t.field("status").value("pending")))
                        
                        // MUST_NOT: 必须不满足
                        .mustNot(mn -> mn.term(t -> t.field("deleted").value(true)))
                        
                        // 最少满足几个 SHOULD
                        .minimumShouldMatch("1")
                )
        )
        .size(100)
);

对应 DSL:

json 复制代码
{
  "query": {
    "bool": {
      "must": [
        {"term": {"geo": "CN"}},
        {"term": {"fiscal_year": "FY25"}}
      ],
      "should": [
        {"term": {"status": "active"}},
        {"term": {"status": "pending"}}
      ],
      "must_not": [
        {"term": {"deleted": true}}
      ],
      "minimum_should_match": "1"
    }
  }
}

4. 范围查询(Range Query)

java 复制代码
SearchRequest request = SearchRequest.of(req -> req
        .index("salesdata_idx")
        .query(q -> q
                .range(r -> r
                        .field("amount")
                        .gte(co.elastic.clients.json.JsonData.of(1000))  // >= 1000
                        .lte(co.elastic.clients.json.JsonData.of(50000)) // <= 50000
                )
        )
);

日期范围查询:

java 复制代码
SearchRequest request = SearchRequest.of(req -> req
        .index("salesdata_idx")
        .query(q -> q
                .range(r -> r
                        .field("create_time")
                        .gte(co.elastic.clients.json.JsonData.of("2025-01-01"))
                        .lt(co.elastic.clients.json.JsonData.of("2025-12-31"))
                )
        )
);

5. 模糊查询/全文检索(Match Query)

java 复制代码
SearchRequest request = SearchRequest.of(req -> req
        .index("salesdata_idx")
        .query(q -> q
                .match(m -> m
                        .field("customer_name")
                        .query("联想")
                        .fuzziness("AUTO")  // 模糊匹配
                )
        )
);

6. IN 查询(Terms Query)

java 复制代码
List<String> recordIds = Arrays.asList("id1", "id2", "id3");

SearchRequest request = SearchRequest.of(req -> req
        .index("salesdata_idx")
        .query(q -> q
                .terms(t -> t
                        .field("record_id")
                        .terms(tv -> tv.value(recordIds.stream()
                                .map(id -> co.elastic.clients.elasticsearch._types.FieldValue.of(id))
                                .collect(Collectors.toList())
                        ))
                )
        )
);

更简洁的写法(使用快捷方法):

java 复制代码
SearchRequest request = SearchRequest.of(req -> req
        .index("salesdata_idx")
        .query(q -> q
                .terms(t -> t
                        .field("record_id")
                        .terms(tv -> tv.value(
                                recordIds.stream()
                                        .map(co.elastic.clients.elasticsearch._types.FieldValue::of)
                                        .collect(Collectors.toList())
                        ))
                )
        )
);

7. 指定返回字段(Source Filtering)

java 复制代码
SearchRequest request = SearchRequest.of(req -> req
        .index("salesdata_idx")
        // 只返回指定字段
        .source(s -> s.filter(f -> f
                .includes("record_id", "customer_name", "amount")
                .excludes("internal_notes")  // 排除某些字段
        ))
        .query(q -> q.matchAll(m -> m))  // 查询所有
        .size(100)
);

8. 排序(Sort)

java 复制代码
import co.elastic.clients.elasticsearch._types.SortOrder;

SearchRequest request = SearchRequest.of(req -> req
        .index("salesdata_idx")
        .query(q -> q.matchAll(m -> m))
        .sort(s -> s
                .field(f -> f
                        .field("amount")
                        .order(SortOrder.Desc)  // 降序
                )
        )
        .sort(s -> s
                .field(f -> f
                        .field("create_time")
                        .order(SortOrder.Asc)   // 升序
                )
        )
        .size(50)
);

9. 分页查询

java 复制代码
int pageNum = 2;
int pageSize = 20;

SearchRequest request = SearchRequest.of(req -> req
        .index("salesdata_idx")
        .query(q -> q.matchAll(m -> m))
        .from((pageNum - 1) * pageSize)  // 起始位置
        .size(pageSize)                   // 每页大小
);

⚠️ 注意 :ES 默认限制 from + size <= 10000,深分页建议使用 ScrollSearch After


10. 聚合查询(Aggregation)

java 复制代码
SearchRequest request = SearchRequest.of(req -> req
        .index("salesdata_idx")
        .size(0)  // 不返回文档,只返回聚合结果
        .aggregations("geo_stats", a -> a
                .terms(t -> t
                        .field("geo")
                        .size(10)  // 返回前10个分组
                )
        )
        .aggregations("total_amount", a -> a
                .sum(s -> s
                        .field("amount")
                )
        )
);

// 获取聚合结果
Map<String, Aggregate> aggs = response.aggregations();

二、结合你的项目实际场景

根据你项目中的 SelectEsDataHandler,这里提供一个完整的示例:

java 复制代码
public EsResult handle(EsDataQuery qry) throws Exception {
    // 构建查询条件
    SearchRequest searchRequest = SearchRequest.of(req -> {
        var builder = req.index(qry.getIndexName());
        
        // 构建 Bool 查询
        if (!qry.isKibanaQuery() && qry.getFieldQuery() != null) {
            builder.query(q -> q.bool(b -> {
                // 添加 record_id IN 查询
                if (CollectionUtils.isNotEmpty(qry.getFieldQuery().getRecordIdList())) {
                    List<FieldValue> values = qry.getFieldQuery().getRecordIdList().stream()
                            .map(FieldValue::of)
                            .collect(Collectors.toList());
                    b.must(m -> m.terms(t -> t
                            .field("record_id")
                            .terms(tv -> tv.value(values))
                    ));
                }
                
                // 添加 invoice_number IN 查询
                if (CollectionUtils.isNotEmpty(qry.getFieldQuery().getInvoiceNumberList())) {
                    List<FieldValue> values = qry.getFieldQuery().getInvoiceNumberList().stream()
                            .map(FieldValue::of)
                            .collect(Collectors.toList());
                    b.must(m -> m.terms(t -> t
                            .field("invoice_number")
                            .terms(tv -> tv.value(values))
                    ));
                }
                
                // 添加 material IN 查询
                if (CollectionUtils.isNotEmpty(qry.getFieldQuery().getMaterialList())) {
                    List<FieldValue> values = qry.getFieldQuery().getMaterialList().stream()
                            .map(FieldValue::of)
                            .collect(Collectors.toList());
                    b.must(m -> m.terms(t -> t
                            .field("material")
                            .terms(tv -> tv.value(values))
                    ));
                }
                
                return b;
            }));
        }
        
        // 设置返回字段
        if (qry.get_source() != null && qry.get_source().length > 0) {
            builder.source(s -> s.filter(f -> f.includes(Arrays.asList(qry.get_source()))));
        }
        
        // 设置分页大小
        builder.size(200);
        
        return builder;
    });
    
    // 执行查询
    SearchResponse<Map> response = elasticsearchClient.search(searchRequest, Map.class);
    
    // 处理结果...
}

三、常用查询对照表

SQL Elasticsearch DSL Java Client API
WHERE status = 'active' term q.term(t -> t.field("status").value("active"))
WHERE id IN (1,2,3) terms q.terms(t -> t.field("id").terms(...))
WHERE amount > 1000 range q.range(r -> r.field("amount").gte(...))
WHERE name LIKE '%联想%' match q.match(m -> m.field("name").query("联想"))
AND bool.must b.must(...)
OR bool.should b.should(...)
NOT bool.must_not b.mustNot(...)
ORDER BY amount DESC sort .sort(s -> s.field(f -> f.field("amount").order(Desc)))
LIMIT 20 OFFSET 40 from/size .from(40).size(20)

四、关键注意事项

  1. 字段类型匹配term 查询用于 keyword 类型,match 用于 text 类型
  2. 空值处理:查询前判断集合是否为空,避免构建无效查询
  3. 性能优化
    • 使用 _source 过滤减少网络传输
    • 避免深分页(from + size > 10000
    • 大数据量使用 Scroll 或 Search After
  4. 超时设置.timeout(Time.of(t -> t.time("30s")))

记录一下

相关推荐
llilian_162 小时前
国产铷原子钟 铷频标挑选指南 铷原子频标 国产铷钟
大数据·网络·功能测试·单片机·嵌入式硬件·测试工具
智慧景区与市集主理人2 小时前
巨有科技:文旅二消的增收密码,数智化让“一次游览”变“多次消费”
大数据·人工智能·科技
cy_cy0022 小时前
解析数字科技展厅的多元展示手段
大数据·科技·人机交互·交互·软件构建
听你说322 小时前
智慧照明赋能城市升级|中节能晶和科技EMC模式破解路灯节能改造长效难题
大数据·人工智能·科技
KKKlucifer2 小时前
混合云纳管能力:国内堡垒机适配多云与异构资源的技术方案
大数据·网络·分类
财迅通Ai2 小时前
天立国际控股:AI赋能再造新增长 中期净利大增21%
大数据·人工智能·天立国际控股
老纪的技术唠嗑局3 小时前
4.15 bubseek —— 让 Agent 的足迹,变成团队的洞察
大数据·数据库·sql·游戏·ai·oceanbase·sql优化
Magic-Yuan3 小时前
如何提高AI落地的成功率 - 成功率函数
大数据·人工智能
永霖光电_UVLED3 小时前
AIXTRON(爱思强)于2026年的业务指引实现上调
大数据·人工智能