Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Storing nested objects in elastic search

I have a 3 level nested object something like below, I want to index these objects in elastic search. Requirement here is users will write a search query keywords like "keyword1 keyword2 ..." and I want to return objects that has all of these keywords in it (at any level i.e. AND operation).

   [  
   {  
      "country":[  
         {  
            "name":"India",
            "ext_code":"91",
            "states":[  
               {  
                  "name":"Karnataka",
                  "ext_code":"04",
                  "cities":[  
                     {  
                        "name":"Bangalore",
                        "ext_code":"080"
                     }
                  ]
               }
            ]
         }
      ]
   }
]

Currently, I store them in nested format using the following mapping:

{
    "mappings":{
        "doc":{
            "properties":{
                "name": {"type":"text"},
                "ext_code": {"type":"text"}
                "state" {
                    "type": "nested",
                    "properties": {
                        "name": {"type":"text"},
                        "ext_code": {"type":"text"}
                        "city" {
                            "type": "nested",
                            "properties": {
                                "name": {"type":"text"}
                                "ext_code": {"type":"text"}
                            }
                        }
                    }
                }
            }
        }
    }
}

And while searching, I pass elastic search a nested query to search on all levels like below:

{
    "query": {
        "bool": {
            "should": [
                {
                    "multi_match": {
                        "query": "keyword1 keyword2 ...",
                        "fields": ['name'] 
                    }
                },
                {
                    "nested": {
                        "path": 'state',
                        "query": {
                            "multi_match": {
                                "query": "keyword1 keyword2 ...",
                                "fields": ['state.name']
                            }
                        }
                    }
                },
                {
                    "nested": {
                        "path": 'state.city',
                        "query": {
                            "multi_match": {
                                "query": "keyword1 keyword2 ...",
                                "fields": ['state.city.name']
                            }
                        }
                    }
                }
            ]
        }
    }
}

When sending multiple tokens to search, it applies an OR operation returning documents containing any of the search tokens.

Is there a way to configure Elastic Search to perform an AND operation for multiple tokens in search query?

like image 410
Lokesh Agrawal Avatar asked Sep 27 '18 06:09

Lokesh Agrawal


1 Answers

One solution would be to index all the values of the name fields inside a custom all field. First define your index and mappings like this:

PUT index
{
  "mappings": {
    "doc": {
      "properties": {
        "all": {                 <-- all field that will contain all values
          "type": "text"
        },
        "name": {
          "type": "text",
          "copy_to": "all"       <-- copy value to all field
        },
        "ext_code": {
          "type": "text"
        },
        "state": {
          "type": "nested",
          "properties": {
            "name": {
              "type": "text",
              "copy_to": "all"       <-- copy value to all field
            },
            "ext_code": {
              "type": "text"
            },
            "city": {
              "type": "nested",
              "properties": {
                "name": {
                  "type": "text",
                  "copy_to": "all"       <-- copy value to all field
                },
                "ext_code": {
                  "type": "text"
                }
              }
            }
          }
        }
      }
    }
  }
}

Then index your documents:

POST index/doc
{
  "name": "India",
  "ext_code": "91",
  "state": [
    {
      "name": "Karnataka",
      "ext_code": "04",
      "city": [
        {
          "name": "Bangalore",
          "ext_code": "080"
        }
      ]
    }
  ]
}

Finally, using a simple match query, you can search for any value anywhere in the document:

POST index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "all": {
              "query": "bangalore india",
              "operator": "and"
            }
          }
        }
      ]
    }
  }
}
like image 159
Val Avatar answered Sep 19 '22 04:09

Val