1

I have three fields:

  • date_start (type: date)
  • date_end (type: date)
  • permanent (type: bool)

I would like to return all documents with theses conditions:

date_start <= now AND date_end >= now
OR
date_start <= now AND permanent == true

What is the best way to do that ?

I thought it would be to use a script like this :

{
 "query": {
   "bool": {
     "filter": [
       {
         "script": {
           "script": {
             "source": "((doc['date_start'].value <= params.now) && (doc['date_end'].value >= params.now)) || ((doc['date_start'].value <= params.now) && (doc['permanent'].value == params.permanent))"
           },
           "lang": "painless",
           "params": {
             "now": "1594390526",
             "permanent": true
           }
         }
       }
     ]
   }
 }
}

But there is an issue with date types comparison and I don't know how to solve this. Thank you

  • 1
    How do you get that epoch in now? Is it hard coded for sample? Do you really mean `now` in date of elasticsearch? – Gibbs Jul 10 '20 at 16:32

2 Answers2

1

Thanks joe for your answer. I did not manage to implement it correctly but I think this is a good starting point for a script solution.

However, I find another way to do what I wanted using combination of range and bool/should.

Here it is :

{
  "query": {
    "bool": {
      "filter": [
        {
          "range": {
            "date_start": {
              "lte": "now"
            }
          }
        },
        {
          "bool": {
            "should": [
              {
                "range": {
                  "date_end": {
                    "gte": "now"
                  }
                }
              },
              {
                "term": {
                  "permanent": true
                }
              }
            ]
          }
        }
      ]
    }
  }
}
0

Dynamic now is purposefully disabled in painless. From the docs:

There are two primary reasons for this. The first is that scripts are often run once per document, so each time the script is run a different now is returned. The second is that scripts are often run in a distributed fashion without a way to appropriately synchronize now. Instead, pass in a user-defined parameter with either a string datetime or numeric datetime for now. A numeric datetime is preferred as there is no need to parse it for comparison.


Tips on why your script may not be working:

params.now should be an epoch-second integer, not a string. Plus, you'll need to access your dates' timestamp values using .millis and then convert to epoch seconds.

Exempli gratia:

{
  "query": {
    "bool": {
      "filter": [
        {
          "script": {
            "script": {
              "source": """
                def now = params.now;
                def start = doc['date_start'].value.millis/1000;
                def end = doc['date_end'].value.millis/1000;
                
                def is_permanent = doc['permanent'].value; 
                
                return (
                  (start <= now) && (end >= now)) ||
                  ((start <= now) && (is_permanent == params.permanent)
                )
              """,
              "lang": "painless",
              "params": {
                "now": 1594399256,
                "permanent": true
              }
            }
          }
        }
      ]
    }
  }
}

The datetime math is nicely described here.

Joe - GMapsBook.com
  • 15,787
  • 4
  • 23
  • 68