一、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,深分页建议使用 Scroll 或 Search 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) |
四、关键注意事项
- 字段类型匹配 :
term查询用于 keyword 类型,match用于 text 类型 - 空值处理:查询前判断集合是否为空,避免构建无效查询
- 性能优化 :
- 使用
_source过滤减少网络传输 - 避免深分页(
from + size > 10000) - 大数据量使用 Scroll 或 Search After
- 使用
- 超时设置 :
.timeout(Time.of(t -> t.time("30s")))
记录一下