elasticsearch/elasticsearch — Issue #1383: Expose matching nested docs to the query and sort
Author: clintongormley
Created: 2011-10-08 10:43:58+00:00
State: open

Hiya

I have a use case for nested docs which, currently, can not be supported easily.

For instance, have a look at this doc:

{
"location" : { "lat" : "8.31667", "lon" : "52.38333" },
"parent_ids" : [ "2862926", "2921044" ],
"contexts" : [ {
"context" : "/en",
"rank" : 2,
"tokens" : [
{ "tokens" : [ "steinbrink" ] },
{ "tokens" : [ "steinbrink", "germany" ] }
],
"label" : {
"short" : "Steinbrink",
"long" : "Steinbrink, Germany"
}
},
{
"context" : "/de",
"rank" : 3,
"tokens" : [
{ "tokens" : [ "steinbrink" ] },
{ "tokens" : [ "steinbrink", "deutschland" ] }
],
"label" : {
"short" : "Steinbrink",
"long" : "Steinbrink, Deutschland"
}
}
]
}

`contexts` and `tokens` are both nested docs.

I would like to run query like:

* filter by `contexts.context == '/de'`
* sort by:
* `contexts.context.rank desc`
* `contexts.label.short asc`

but of course I have no way of pulling out the single matching context and using the values in that sub-doc.

I could use a script sort like the horribly inefficient:

{
"sort" : [
{
"params" : {
"name" : "/de"
},
"_script" : {
"script" : "for (context: contexts) { if (context.context == name) { return context.rank }}",
"order" : "desc",
"type" : "number"
}
},
{
"_script" : {
"params" : {
"name" : "/de"
},
"script" : "for (context: contexts) { if (context.context == name) { return context.label.short }}",
"order" : "asc",
"type" : "string"
}
}
]
}

Or I may want to do:
* filter by `context=='/de'`
* find doc with tokens that best match `stein*`
* return the `tokens` that match best as a field

Or have a slightly different doc structure:

"tokens" : [
{
"label": "Steinbrink",
"tokens" : [ "steinbrink" ]
},
{
"label": "Steinbrink, Germany",
"tokens" : [ "steinbrink, germany" ]
},
],

and return the `label` associated with the `tokens` that best match.

Even with a script, I couldn't find the best matching `tokens`.

In this example, I only have two contexts, and so could have changed the doc structure to something like:

contexts: {
en: {....},
de: {....}
}

But I have other use cases where I have hundreds of contexts, which would result in thousands of fields.

Would it be possible to add functionality to:
* expose the best matching child (or children, with the best matching first in an array) in a `_scope` so we could do something like:

"sort": [
{ "_child.myscope[0].rank": "desc" },
{ "_child.myscope[0].label.short": "asc"}
],
"fields": [ "_child.otherscope[0],tokens.label" ]

* return the scores for matching children in a `_scope` eg:

{ "children": {
myscope: [5,3,0,2,0],
}}

Not sure how feasible this is, and if it is feasible, there is probably a much more elegant way of doing it.

thanks

clint