When dealing with numbers in this chapter, we have so far only searched for exact numbers. In practice, filtering on ranges is often more useful. For example, find all products with a price greater than $20 and less than $40.
In SQL terms, a range can be expressed as:
SELECT document
FROM products
WHERE price BETWEEN 20 AND 40
Elasticsearch has a range filter, which, unsurprisingly, allows you to filter ranges:
"range" : {
"price" : {
"gt" : 20,
"lt" : 40
}
}
The range filter supports both inclusive and exclusive ranges, through combinations of the following options:
gt |
|
lt |
|
gte |
|
lte |
|
GET /my_store/products/_search
{
"query" : {
"filtered" : {
"filter" : {
"range" : {
"price" : {
"gte" : 20,
"lt" : 40
}
}
}
}
}
}
If you need an unbounded range (e.g. just >20), just omit one of the boundaries:
"range" : {
"price" : {
"gt" : 20
}
}
The range filter can be used on date fields too:
"range" : {
"timestamp" : {
"gt" : "2014-01-01 00:00:00",
"lt" : "2014-01-07 00:00:00"
}
}
When used on date fields, the range filter supports date math operations. For example, if we want to find all documents that have a timestamp sometime in the last hour:
"range" : {
"timestamp" : {
"gt" : "now-1h"
}
}
This filter will now constantly find all documents with a timestamp greater than the current time minus 1 hour, making the filter a ``sliding window'' across your documents.
Date math can also be applied to actual dates, rather than a placeholder like now. Just add a double pipe || after the date and follow it with a date math expression:
"range" : {
"timestamp" : {
"gt" : "2014-01-01 00:00:00",
"lt" : "2014-01-01 00:00:00||+1M" (1)
}
}
-
Less than January 1 2014 plus one month.
Date math is "calendar aware", so it knows how many days are in each month, days in a year, etc. More details about working with dates can be found in the {ref}/mapping-date-format.html[date format reference documentation].
The range filter can also operate on string fields. String ranges are calculated lexicographically or alphabetically. For example, these values are sorted in lexicographic order:
-
5, 50, 6, B, C, a, ab, abb, abc, b
Terms in the inverted index are sorted in lexicographical order, which is why string ranges use this order.
If we want a range from a
up to but not including b
, we can use the same
range filter syntax:
"range" : {
"title" : {
"gte" : "a",
"lt" : "b"
}
}
Numeric and date fields are indexed in such a way that ranges are very efficient to calculate. This is not the case for string fields, however. To perform a range on a string field, Elasticsearch is effectively performing a term filter for every term that falls in the range. This is much slower than a date or numeric range.
String ranges are fine on a field with low cardinality — a small number of unique terms. But the more unique terms you have, the slower the string range will be.