Elasticsearch filter by nested fields

Viewed 514

I have a problem with creating a query to Elasticsearch with many conditions. My model looks like:

data class Product(
        @Id
        val id: String? = null,
        val category: String,
        val imagesUrls: List<String>,
        @Field(type = FieldType.Double)
        val price: Double?,
        @Field(type = FieldType.Nested)
        val parameters: List<Parameter>?
)

data class Parameter(
        val key: String,
        val values: List<String>
)

I would like to query products by:

  • category (for example cars)
  • price (between 20k $ and 50k $)
  • and parameters -> For example products with many parameters, like key capacity values 4L, 5L and second parameter gear transmission values manual

My current query looks like this:

GET data/_search
{

  "size": 10,
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "category.keyword": {
              "value": "cars"
            }
          }
        },
        {
          "nested": {
            "path": "parameters",
            "query": {
              "bool": {
                "must": [
                  {"term": {
                    "parameters.key.keyword": {
                      "value": "Capacity"
                    }
                  }},
                  {
                    "term": {
                      "parameters.key": {
                        "value": "4L, 5L"
                      }
                    }
                  }
                ]
              }
            }
          }
        }
      ]
    }
  }
  • Could you tell me how to filter the product when parameter key is equal to Capacity and check that the values list contains one of the values?
  • How to combine many this kind operations in one query?

Example data:

{
   "category":"cars",
   "name":"Ferrari",
   "price":50000,
   "parameters":[
      {
         "key":"capacity",
         "values":"4L"
      },
      {
         "key":"gear transmission",
         "values":"automcatic"
      }
   ]
}
2 Answers

The search query shown below queries the data based on:

  1. category (for example cars)
  2. And parameters -> For example products with many parameters, like key capacity values 4L, 5L and second parameter gear transmission values manual

Adding a working example with index data, mapping, search query, and search result

Index Mapping:

{
  "mappings": {
    "properties": {
      "parameters": {
        "type": "nested"
      }
    }
  }
}

Index Data:

{
   "category":"cars",
   "name":"Ferrari",
   "price":50000,
   "parameters":[
      {
         "key":"gear transmission",
         "values":["4L","5L"]
      },
      {
         "key":"capacity",
         "values":"automcatic"
      }
   ]
}

{
   "category":"cars",
   "name":"Ferrari",
   "price":50000,
   "parameters":[
      {
         "key":"capacity",
         "values":["4L","5L"]
      },
      {
         "key":"gear transmission",
         "values":"automcatic"
      }
   ]
}

{
   "category":"cars",
   "name":"Ferrari",
   "price":50000,
   "parameters":[
      {
         "key":"capacity",
         "values":"4L"
      },
      {
         "key":"gear transmission",
         "values":"automcatic"
      }
   ]
}

Search Query:

{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "category.keyword": {
              "value": "cars"
            }
          }
        },
        {
          "nested": {
            "path": "parameters",
            "query": {
              "bool": {
                "must": [
                  {
                    "match": {
                      "parameters.key": "capacity"
                    }
                  },
                  {
                    "terms": {
                      "parameters.values": [
                        "4l",
                        "5l"
                      ]
                    }
                  }
                ]
              }
            }
          }
        },
        {
          "nested": {
            "path": "parameters",
            "query": {
              "bool": {
                "must": [
                  {
                    "match": {
                      "parameters.key": "gear transmission"
                    }
                  },
                  {
                    "match": {
                      "parameters.values": "automcatic"
                    }
                  }
                ]
              }
            }
          }
        }
      ]
    }
  }
}

Search Result:

"hits": [
         {
            "_index": "bstof",
            "_type": "_doc",
            "_id": "1",
            "_score": 3.9281754,
            "_source": {
               "category": "cars",
               "name": "Ferrari",
               "price": 50000,
               "parameters": [
                  {
                     "key": "capacity",
                     "values": "4L"
                  },
                  {
                     "key": "gear transmission",
                     "values": "automcatic"
                  }
               ]
            }
         },
         {
            "_index": "bstof",
            "_type": "_doc",
            "_id": "2",
            "_score": 3.9281754,
            "_source": {
               "category": "cars",
               "name": "Ferrari",
               "price": 50000,
               "parameters": [
                  {
                     "key": "capacity",
                     "values": [
                        "4L",
                        "5L"
                     ]
                  },
                  {
                     "key": "gear transmission",
                     "values": "automcatic"
                  }
               ]
            }
         }
      ]

When you need to match any one from a list then you can use terms query instead of term. Update the part in query from:

{
  "term": {
    "parameters.key": {
      "value": "4L, 5L"
    }
  }
}

to below:

{
  "terms": {
    "parameters.values": {
      "value": [
        "4L",
        "5L"
      ]
    }
  }
}

Note that if parameters.key is analysed field and there exist a keyword sub-field for the same, then use it instead. e.g parameters.values.keyword

You can read more on terms query here.

Related