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.