背景
一个配方由多种原材料组成,需求是根据各种原材料的用量搜索出对应的配方
配方实体类
class Formula {
private long id;
private String name;
private List<Material> materials;
}
class Material {
@JsonProperty("material_id")
private long materialId;
private float amount;
}
1、定义索引映射 Mapping
PUT /formula
{
"mappings": {
"properties": {
"id": {
"type": "long"
},
"name": {
"type": "text"
},
"materials": {
"type": "nested",
"properties": {
"material_id": {
"type": "long"
},
"amount": {
"type": "float"
}
}
}
}
}
}
2、添加测试数据
POST /formula/_doc/1
{
"id": "1",
"name": "formula A",
"materials": [
{ "material_id": "material_1", "amount": 100 },
{ "material_id": "material_2", "amount": 50 }
]
}
POST /formula/_doc/2
{
"id": "2",
"name": "formula B",
"materials": [
{ "material_id": "material_2", "amount": 30 },
{ "material_id": "material_3", "amount": 20 }
]
}
3、查询
GET /formula/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "formula A"
}
},
{
"nested": {
"path": "materials",
"query": {
"bool": {
"must": [
{ "match": { "materials.material_id": "2" } },
{ "range": { "materials.amount": { "gte": 40, "lte": 60 } } }
]
}
}
}
}
]
}
}
}
RestHighLevelClient
// nested
BoolQueryBuilder nestedBoolQueryBuilder = new BoolQueryBuilder();
nestedBoolQueryBuilder.must().add(QueryBuilders.matchPhraseQuery("materials.material_id", 1));
nestedBoolQueryBuilder.must().add(QueryBuilders.rangeQuery("materials.amount").from(40).to(60));
NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("materials", nestedBoolQueryBuilder, ScoreMode.Avg);
// query
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
boolQueryBuilder.must(nestedQueryBuilder);
// simple
boolQueryBuilder.must(QueryBuilders.matchQuery("name", "formula A"));
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(boolQueryBuilder);
SearchRequest searchRequest = new SearchRequest(new String[]{index}, searchSourceBuilder);
// response
SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
ElasticsearchClient
SearchResponse<Formula> response = elasticsearchClient.search(s -> s
.index("formula")
.query(q -> q.match(t -> t.field("name").query("formula A")))
.query(q -> q.nested(nq -> nq.path("materials")
.query(nq1 -> nq1.match(t -> t.field("materials.material_id").query("2")))
.query(nq2 -> nq2.range(r -> r.field("materials.amount").from("40").to("60")))
)
),
Formula.class
);