Search code examples
elasticsearchnestedmulti-levelelasticsearch-6

In Elasticsearch, how do I search string on multiple fields from multi-level nested objects


In Elasticsearch 6, I have data with nested objects like this:

{
    "brands" : 
    [
        {
            "brand_name" : "xyz",
            "products" : 
            [
               {
                   "title" : "test",
                   "mrp" : 100,
                   "sp" : 90,
                   "status" : 1
               },
               {
                   "title" : "test1",
                   "mrp" : 50,
                   "sp" : 45,
                   "status" : 1
                }
            ]
        },
        {
            "brand_name" : "aaa",
            "products" : 
            [
                {
                    "title" : "xyz",
                    "mrp" : 100,
                    "sp" : 90,
                    "status" : 1
                },
                {
                    "title" : "abc",
                    "mrp" : 50,
                    "sp" : 45,
                    "status" : 1
                }
            ]
        }
    ]
}

I want to search from either from the field brand_name or from the field title. And I want return all results in same inner_hits.

For example : If I input the search string as "xyz" it should return both brands object with correspondent product object. If I input the search string as "test" it should return only first brand array with only first product object.

How can I achieve this. Any ideas?

I have tried with the nested path query like this:

{
  "query": {
    "bool": {
      "must": [
        {
          "nested": {
            "path": "brands",
            "query": {
              "bool": {
                "should": [
                  {
                    "term": {
                      "brands.brand_name": "xyz"
                    }
                  },
                  {
                    "term": {
                      "brands.brand_name.keyword": "aaa"
                    }
                  },
                  {
                    "nested": {
                      "path": "brands.products",
                      "query": {
                        "bool": {
                          "should": [
                            {
                              "match": {
                                "brands.products.title": "xyz"
                              }
                            }
                          ]
                        }
                      },
                      "inner_hits": {}
                    }
                  }
                ]
              }
            },
            "inner_hits": {}
          }
        }
      ]
    }
  }
}

But this query returning with multiple inner_hits response with multiple array objects for each brands and for each products.

I want the response like all brand names which is matching with the string should list under one array and all the products should list under another array under same inner_hits.


Solution

  • Since you want the inner hits to be different based on where the match has happened i.e. brands.brand_name or brands.products.title, you can have two queries one for brand name and other for product title as independent nested queries. These queries then should be inside should clause of a bool query. Each of the nested query should have its own inner_hits as below:

    {
      "query": {
        "bool": {
          "should": [
            {
              "nested": {
                "path": "brands",
                "inner_hits": {},
                "query": {
                  "term": {
                    "brands.brand_name.keyword": "test"
                  }
                }
              }
            },
            {
              "nested": {
                "path": "brands.products",
                "inner_hits": {},
                "query": {
                  "term": {
                    "brands.products.title": "test"
                  }
                }
              }
            }
          ]
        }
      },
      "_source": false
    }