A.Cabb
A.Cabb

Reputation: 65

How to programmatically create a filter query in Elasticsearch with filtring in a nested object

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

Answers (1)

Tds
Tds

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

Related Questions