Reputation: 581
I have a mapping in which I have a nested type, with the nested object usually comprising more than one object, so it is an array if the whole main object is retrieved. Using the "fields" part of a search request I can get fields of the main object, and fields from the nested objects (as an array except when there is only one), but not, apparently, the whole nested object array. Is there any way to do this other than getting the whole object (omitting fields)?
For example:
{
"properties: {
"f1": {"type": "string"},
"f2": {"type": "string"},
...
"n": {
"type": "nested",
"properties": {
"n1": {"type": "string"},
"n2": {"type": "string"},
...
}
}
}
}
Typically, n will be an array, but n1 may not be set for all entries
This query works, but because of missing n1's in some of the objects in the array, isn't helpful:
{"query": {"nested": {"path": "n", "query": {"match": {"n.n1","something"}}}},
"fields": ["f1", "n.n1"]}
This doesn't ("field [n] isn't a leaf field"), but is what I really want:
{"query": {"nested": {"path": "n", "query": {"match": {"n.n1","something"}}}},
"fields": ["f1", "n"]}
But this does, at the expense of retrieving the whole object including (array of) n:
{"query": {"nested": {"path": "n", "query": {"match": {"n.n1","something"}}}}}
Is there some query syntax I'm missing that would give me the whole array n (and also at least one of the string fields in the main object) when a field in one of the n's matches, without getting the whole matching object?
Upvotes: 1
Views: 4112
Reputation: 198
I think that what you want is probably Inner_Hits
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-inner-hits.html#top-level-inner-hits
Basically you can do a query that will give you back only nested objects that match your query rather than all the nested objects for each "parent" object that matched your query.
Combine that with the _source: false
flag and I think you get more or less what you are looking for.
Here is an example:
"_source": "false",
"query": {
"match": {
"n.n1": "something"
}
},
"inner_hits": {
"myname": {
"path": {
"n": {
"query": {
"match": {
"n.n1": "something"
}
}
}
}
}
}
The top level query will give you all the parent documents that contain any inner document where n.n1 contains "something"
. The inner query will then also filter that array to only the inner documents that have n.n1 contains "something"
. If you actually don't want to filter the Inner_hits
at all, you just want all of them even where n1 is null, then just change the inner hits query to match_all
.
This will give you back a response something like:
{
"_index": "myindex",
"_type": "mytype",
"_id": "theid",
"_score": 1,
"inner_hits": {
"myname": {
"hits": {
"total": 2,
"max_score": 2.4890606,
"hits": [
{
"_index": "myindex",
"_type": "mytype",
"_id": "2",
"_nested": {
"field": "n",
"offset": 1
},
"_score": 2.4890606,
"_source": {
"n1": "something",
"n2": "whatever"
}
},
{
"_index": "myindex",
"_type": "mytype",
"_id": "3",
"_nested": {
"field": "n",
"offset": 0
},
"_score": 2.4890606,
"_source": {
"n1": "something",
"n2": "great"
}
}
]
}
}
}
}
Let me know if anything is unclear.
EDIT:
As per my comment here is an example:
{
"fields": ["f1"],
"query": {
"match": {
"n.n1": "something"
}
},
"inner_hits": {
"myname": {
"path": {
"n": {
"query": {
"match_all" : {}
}
}
}
}
}
}
That query will give you all the parent documents which have ANY child where n.n1 contains "something". The Inner hits query will give you ALL children of the parents, including those where n.n1 does not contain "something". The fields flag will mean that only the chosen fields are returned for the parent document.
Upvotes: 3