Mikhail Geyer
Mikhail Geyer

Reputation: 1171

How to sort In Elasticsearch by parent and nested fields at the same time?

I need to sort in Elasticsearch by parent and nested fields at the same time. My data are like this:

[
    {
        "id": "1",
        "rank": 8,
        "price": 12.45,
        "offers": [
            {
                "id": "777",
                "rank": 12,
                "price": 45.75
            }
        ]
    },
    {
        "id": "2",
        "rank": 35,
        "price": 5.95,
        "offers": null
    }
]

I need to sort the results on rank in such a manner that when the offers are not null I should take nested offers.rank value, otherwise I should take the parent rank value. I tried this script, but it did not work:

    "sort": [
        {
            "_script": {
                "script": {
                    "source": "doc['offers'].size()==0 ? doc['rank'].value : doc['offers.rank'].value",
                    "lang": "painless"
                },
                "type": "number",
                "order": "asc"
            }
        }
    ]

It did not work probably because offers.rank is from a nested offers object, which is not accessible. But I do not understand how to handle it - if I add a nested condition for the whole script, then my parent value doc['rank'].value will not be accessible anymore. It is possible to sort here by parent and nested fields at the same time?

Upvotes: 3

Views: 1611

Answers (1)

Joe - Check out my books
Joe - Check out my books

Reputation: 16925

You're right in assuming that the parent rank would not be accessible. Now, you could either

  1. create 2 separate sort 'objects', one for the parent, one for the nested offers, and then sum the results up using the sort mode or
  2. iterate on the _source instead:
{
  "sort": [
    {
      "_script": {
        "script": {
          "source": """
          if (params._source.offers instanceof ArrayList
              && params._source.offers.length > 0) {
            return params._source['offers'][0].rank;
          }
          return params._source.rank
          """,
          "lang": "painless"
        },
        "type": "number",
        "order": "asc"
      }
    }
  ]
}

Note that since we're working with an 'offers' ArrayList here, you'll need some sort of mechanism to pick a rank. That's up to you -- I've simply accessed the first offer's rank and you may want to sort the array list & pick the highest one...

Here's a one-liner if that's what you prefer:

params._source.offers instanceof ArrayList && params._source.offers.length > 0 ? params._source['offers'][0].rank : params._source.rank

Upvotes: 3

Related Questions