How to Implement Range Filters for Numeric and Date Fields in OpenSearch
Range filters in OpenSearch are implemented by constructing JSON query clauses using operators like gt, gte, lt, and lte, which the Lambda handler in aws-samples/sample-for-amazon-opensearch-service-tutorials-101 translates from HTTP request parameters into OpenSearch DSL.
The aws-samples/sample-for-amazon-opensearch-service-tutorials-101 repository provides a complete reference implementation showing how to build serverless search APIs that handle numeric and date range filtering. Understanding these patterns helps you construct efficient queries for price ranges, date intervals, and other bounded numeric data in OpenSearch.
Simple Range Filters for Single-Field Queries
The repository implements a straightforward range_filter search type designed for single-field numeric or date constraints. This approach extracts operator and value information directly from the incoming request payload.
How the Lambda Handler Processes Range Operators
In artifacts/search_lambda/opensearch_search.py (lines 68-78), the search_products function handles the range_filter type by validating the operator and converting values:
# From artifacts/search_lambda/opensearch_search.py lines 68-78
if search_type == "range_filter":
attribute_name = body.get("attribute_name")
attribute_value = body.get("attribute_value")
operator = body.get("operator") # gt, gte, lt, lte
# Validate operator is a string
if not isinstance(operator, str):
raise ValueError("Operator must be a string")
# Convert to integer for numeric fields
value = int(attribute_value)
# Build the range query
search_body = {
"query": {
"range": {
attribute_name: {
operator: value
}
}
}
}
The code validates that the operator is one of the supported string values (gt, gte, lt, lte), converts the attribute value to an integer for numeric comparison, and nests the operator under the field name within the OpenSearch range clause.
Example: Filtering Products by Price
When a client sends a request to filter products with prices greater than or equal to 100:
{
"type": "range_filter",
"attribute_name": "price",
"attribute_value": "150",
"operator": "gte"
}
The Lambda generates the following OpenSearch query:
{
"size": 100,
"query": {
"range": {
"price": { "gte": 150 }
}
}
}
Complex Multi-Field Range Filtering
For scenarios requiring multiple field constraints or more sophisticated date ranges, the repository implements a complex search handler that processes field definitions with explicit min and max boundaries.
Building Range Queries with Min/Max Values
In artifacts/search_lambda/opensearch_search.py (lines 31-38), the code handles fields where type equals "range":
# From artifacts/search_lambda/opensearch_search.py lines 31-38
for field in fields:
field_name = field.get("name")
field_type = field.get("type")
field_value = field.get("value")
if field_type == "range":
range_query = {"range": {field_name: {}}}
if "min" in field_value:
range_query["range"][field_name]["gte"] = field_value["min"]
if "max" in field_value:
range_query["range"][field_name]["lte"] = field_value["max"]
must_clauses.append(range_query)
This implementation checks for the presence of min and max keys in the field value object, mapping them to gte (greater than or equal) and lte (less than or equal) operators respectively. This approach works for both numeric fields and date strings.
Example: Numeric Rating Range
For filtering products with ratings between 3 and 5:
{
"type": "complex_search",
"search_type": "combined",
"fields": [
{
"name": "rating",
"type": "range",
"value": { "min": 3, "max": 5 }
}
]
}
The generated OpenSearch query fragment:
{
"range": {
"rating": {
"gte": 3,
"lte": 5
}
}
}
Example: Date Range Filtering
For date fields, the same min/max pattern applies using ISO date strings:
{
"type": "complex_search",
"search_type": "combined",
"fields": [
{
"name": "release_date",
"type": "range",
"value": {
"min": "2022-01-01",
"max": "2022-12-31"
}
}
]
}
This produces:
{
"range": {
"release_date": {
"gte": "2022-01-01",
"lte": "2022-12-31"
}
}
}
Summary
- Simple range filters use the
range_filtersearch type inartifacts/search_lambda/opensearch_search.py(lines 68-78), supportinggt,gte,lt, andlteoperators for single-field numeric queries. - Complex range filters handle multi-field scenarios using the
"range"field type (lines 31-38), mappingminvalues togteandmaxvalues toltefor both numeric and date fields. - Both implementations construct standard OpenSearch DSL
rangequeries that execute viaops_client.search(), leveraging OpenSearch's native support for numeric and date range filtering. - The repository demonstrates production-ready patterns for validating operator strings, converting data types, and building dynamic query clauses in Python Lambda functions.
Frequently Asked Questions
How do I filter documents by a numeric price range in OpenSearch using this sample?
Send a POST request with type: "range_filter" including attribute_name (the field), attribute_value (the number as a string), and operator (gte, lte, gt, or lt). The Lambda in artifacts/search_lambda/opensearch_search.py converts the value to an integer and builds the OpenSearch range query with your specified operator.
Can I use date strings with the range filter implementation?
Yes. While the simple range_filter type converts values to integers for numeric fields, the complex search implementation (lines 31-38) accepts date strings in ISO format (e.g., "2023-01-01") when using the "range" field type with min and max values. OpenSearch automatically handles date parsing based on your index mappings.
What is the difference between the simple range_filter and the complex range field type?
The simple range_filter (lines 68-78) is designed for single-field queries with explicit operators (gt, gte, lt, lte) and converts values to integers. The complex "range" field type (lines 31-38) supports multi-field search requests, uses min/max semantics instead of explicit operators, and preserves string values for dates or decimals without forced integer conversion.
Which operators are supported for range queries in this implementation?
The implementation supports the standard OpenSearch range operators: gt (greater than), gte (greater than or equal), lt (less than), and lte (less than or equal). In the simple filter, these are passed directly in the request. In the complex search, min maps to gte and max maps to lte automatically.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →