Reputation: 65
I am struggling creating a filter query for Elastisearch in Java Spring. I have this Elasticsearch mapping:
"mappings": {
"properties": {
"id": {"type": "keyword"},
"name": {"type": "text"},
"city": {"type": "keyword"},
"cars": {
"type": "nested",
"properties": {
"id": {"type": "long"},
"brand": {"type": "text"},
"category": {"type": "text"}
}
}
}
Data in Elasticsearch looks like this:
[
{
"id": 1,
"name": "Car seller 1",
"city": "Detroit",
"cars": [
{"id": 1, "brand": "bmw", "category": "suv"},
{"id": 2, "brand": "bmw", "category": "sedan"},
{"id": 3, "brand": "bmw", "category": "hatchback"}
]
},
{
"id": 2,
"name": "Car seller 2",
"city": "Detroit",
"cars": [
{"id": 1, "brand": "vw", "category": "suv"},
{"id": 2, "brand": "vw", "category": "sedan"},
{"id": 3, "brand": "vw", "category": "suv"}
]
},
{
"id": 3,
"name": "Car seller 3",
"city": "Las Vegas",
"cars": [
{"id": 1, "brand": "ford", "category": "suv"},
{"id": 2, "brand": "ford", "category": "sedan"},
{"id": 3, "brand": "ford", "category": "hatchback"}
]
}
]
Now, let's say that from my FE will came an request to search documents by some filters:
1. city: ["Detroit"]
2. cars.category: ["suv"]
What I want to achieve is a filter query that will return documents of sellers, but only with cars that match the filter (if there is such a filter).
For example, for mentioned filters query should return this:
[
{
"id": 1,
"name": "Car seller 1",
"city": "Detroit",
"cars": [
{"id": 1, "brand": "bmw", "category": "suv"}
]
},
{
"id": 2,
"name": "Car seller 2",
"city": "Detroit",
"cars": [
{"id": 1, "brand": "vw", "category": "suv"},
{"id": 3, "brand": "vw", "category": "suv"}
]
}
]
So far, I was able to create query with java builders provided by Elastic, which can filter documents properly, but it returning all seller´s cars.
public Flux<Seller> filterSearch(Map<String, List<String>> filters) {
List<String> listOfFields = new ArrayList<>(filters.keySet());
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
listOfFields.forEach(field -> {
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
if (field.contains("cars.")) {
filters.get(field).forEach(value -> boolQueryBuilder.should(QueryBuilders.termQuery(field, value)));
boolQueryBuilder.minimumShouldMatch(1);
NestedQueryBuilder nestedQueryBuilder = new NestedQueryBuilder("cars", boolQueryBuilder, NestedQueryBuilder.parseScoreMode("none"));
nestedQueryBuilder.innerHit(new InnerHitBuilder());
queryBuilder.must().add(nestedQueryBuilder);
} else {
filters.get(field).forEach(value -> boolQueryBuilder.should(QueryBuilders.matchPhraseQuery(field, value)));
queryBuilder.must().add(boolQueryBuilder);
}
});
return executeSearchQuery(queryBuilder);
}
Thanks for your help.
Upvotes: 0
Views: 561
Reputation: 1
I think you should try this:
if (field.contains("cars.")) {
...
} else {
filters.get(field).forEach(value -> boolQueryBuilder.should(QueryBuilders.matchPhraseQuery(field, value)).minimumShouldMatch(1));
queryBuilder.must().add(boolQueryBuilder);
}
Upvotes: 0