Elasticsearch - Filter where (one of nested array) and (all of nested array) Elasticsearch - Filter where (one of nested array) and (all of nested array) elasticsearch elasticsearch

Elasticsearch - Filter where (one of nested array) and (all of nested array)


One of Nested Array

Having one of a nested array matching some criteria turns out to be very simple. A nested filter evaluates to matching/true if any of the array of nested objects match the specified inner filters. For example, given an array of outer objects where one of those objects has a field match with the value "matching" the following would be considered true.

"nested": {   "path": "outer",   "filter": {       "term" : { "match" : "matching" }    }}

The above will be considered true/matching if one of the nested outer objects has a field called match with the value "matching".

All of Nested Array

Having a nested filter only be considered matching if all of the nested objects in an array match is more interesting. In fact, it's impossible. But given that it is considered matching if only one of the nested objects match a filter we can reverse the logic and say "If none of the nested objects don't match" to achieve what we need. For example, given an array of nested outer.inner objects where all of those objects has a field match with the value "matching" the following would be considered true.

"not" : {   "nested": {      "path": "outer.inner",      "filter": {          "not" : {              "term" : { "match" : "matching" }           }      }   }}

The above will be considered true/matching because none of the nested outer.inner objects don't (double negative) have a field called match with the value "matching". This, of course, is the same as all of the nested inner objects having a field match with the value "matching".

Missing Any Nested Objects

You can't check whether a field containing nested objects is missing using the traditional missing filter. This is because nested objects aren't actually in the document at all, they are stored somewhere else. As such missing filters will always be considered true. What you can do however, is check that a match_all filter returns no results like so;

"not": {   "nested": {      "path": "outer",      "filter": {          "match_all": {}       }    } }

This is considered true/matching if match_all finds no results.


Well, it's a doozy, but this query seems to do what you want:

POST /test_index/_search{   "query": {      "filtered": {         "filter": {            "nested": {               "path": "outer",               "filter": {                  "bool": {                     "must": [                        {                           "nested": {                              "path": "outer.inner",                              "filter": {                                 "bool": {                                    "must": [                                       { "term": { "outer.inner.type": "Market" } },                                       { "term": { "outer.inner.match": "1st Class" } }                                    ]                                 }                              }                           }                        },                        {                           "nested": {                              "path": "outer.inner",                              "filter": {                                 "bool": {                                    "must": [                                       { "term": { "outer.inner.type": "Country" } },                                       { "term": { "outer.inner.match": "GBR" } }                                    ]                                 }                              }                           }                        }                     ]                  }               }            }         }      }   }}

Here is some code I used to test it:

http://sense.qbox.io/gist/f554c2ad2ef2c7e6f5b94b1ddb907813370f4edc

Let me know if you need some explanation of the logic; it is kind of involved.