Elasticsearch DSL query with specific Output

Viewed 780

I have a few objects in the database: object 1, object 2, object 3, .., object n

Now I am doing filter like this

MyDocument.search().filter("match", is_active=True).sort('id').execute()

Output:

searchDocumentobject 1, searchDocumentobject 2,searchDocumentobject 3, ....

Now I need to searchDocumentobject 2 in last from the list.

Need the output like this:

searchDocumentobject 1,searchDocumentobject 3, .... , searchDocumentobject 2

Thank you

2 Answers

You can achieve this behavior in your search request using function score, the idea is that each other documents you'll have score of 1 (default) and with document 2 you give it lower score then sort by "_score", "id". Here is the DSL, try construct the query from your python API:

{
  "_source": ["id"], 
  "query": {
    "function_score": {
      "query": {
        "bool": {
           //add your query here
           "must": [
             {
               "terms": {
                 "id": [1, 2, 3, 70]
               }
             }
           ]
         }
      },
      "functions": [
        {
          "filter": {
            "term": {
              "id": 2
            }
          },
          "weight": 0.5
        }
      ]
    }
  }, 

  "sort": [
    "_score",
    {
      "id": {
        "order": "asc"
      }
    }
  ]
}

Also as Yifeng stated in comment section, you can re-sort the results after you query from ES.

In MyModel, add a new method, which returns 0 if you want to keep that document at last, else it'll return 1.

class MyModel(models.Model):

    # Add new method here
    def get_rank(self):
        if self.id == 2:  # your condition here
            return 0      # return 0, if you want to keep it at last
        return 1

Now, you can utilize this method in MyDocument. Add a new field in MyDocument, which we'll use for sorting.

class MyDocument(Document):
    # Add new field here
    rank = fields.IntegerField(attr='get_rank')

Now, you can query like this,

MyDocument.search().filter("match", is_active=True).sort('-rank', 'id').execute()
Related