Filtering and Paging
Fulltext search and simple filtering
Use the fulltext_search field for fulltext search and simple filtering.
It supports simple_query_string capabilities of OpenSearch/Elasticsearch and the following operators:
+signifies AND operation|signifies OR operation-negates a single term"wraps a number of term to signify a phrase for searching*at the end of a term signifies a prefix query(and)signify precedence~nused after a term (for example,wnid~3), sets fuzziness, when used after a phrase, sets slop.
Details see
QueryStringQuery for Advanced Filtering (recommended for filtering)
Use the query_string_query field for advanced filtering.
It supports query_string capabilities and syntax of OpenSearch/Elasticsearch which allows advanced filtering for
specific fields.
Details see
Query Filters (legacy filtering mechanism)
Additionally, querying can be done using a limited and simplified subset of the syntax described here.
Supported Logic Operators
- $not
- $or
- $and
Examples
filter={"system.modificationDate" : "1729685234"}
... SQL equivalent ...
where `modificationDate` = '1729685234'
filter=[{"system.creationDate" : "1557394706"}, {"system.modificationDate" : "1729685234"}]
...
where `creationDate` = '1557394706' AND `modificationDate` = '1557394706'
filter={"$and" : [{"system.subtype" : "Car"}, {"$or": [{"system.id": "14"}, {"system.key": "E-Type"}]}]}
...
where `subtype` = 'Car' AND (`id` = '14' OR `key` = 'E-Type')
filter={"$and" : [{"system.subtype": "Car"}, {"data.Manufacturer" : "Jaguar"}, {"$or": [{"system.id": "14"}, {"system.key": "E-Type"}]}]}
...
where `subtype` = 'Car' AND Manufacturer = 'Jaguar' AND (`id` = '14' OR `key` = 'E-Type')
filter={"system.type": {"$not": "folder"}}
...
where NOT `type` = 'folder'
Sorting
Use the order_by parameter to control result ordering.
Format
Simple string - field name only (defaults to ascending):
"fieldPath"
JSON object - field paths as keys and sort direction as values:
{"fieldPath": "asc|desc"}
JSON object with options (needed for Elasticsearch only) - extended format with sort direction and unmapped_type:
{"fieldPath": {"order": "asc|desc", "unmapped_type": "keyword|double"}}
JSON array - multiple fields (first is primary sort, subsequent are tiebreakers):
{"data.priority": "desc", "system.creationDate": "asc"}
Examples
Sort by modification date (simple string, ascending):
system.modificationDate
Sort by modification date (newest first):
{"system.modificationDate": "desc"}
Sort by price (lowest first):
{"data.price": "asc"}
Sort by price with explicit unmapped_type (for numeric fields in Elasticsearch):
{"data.price": {"order": "asc", "unmapped_type": "double"}}
Multiple sort fields:
{"data.category": "asc", "data.price": "desc", "system.id": "asc"}
Mixed format (simple and extended):
{"data.price": {"order": "asc", "unmapped_type": "double"}, "system.id": "asc"}
The unmapped_type Parameter
When querying across multiple indices where a sort field may not exist in all indices, you can specify unmapped_type
to define how missing fields should be treated.
When to use:
- Elasticsearch: Required when sorting by numeric fields (e.g.,
data.price,data.number) to prevent "incompatible sort types" errors - OpenSearch: Optional
Supported types:
keyword- For text/string fields (default)double- For numeric fields
Why it matters:
When a field exists in some indices but not others (e.g., data.price exists in product objects but not in
asset or folder indices), Elasticsearch needs to know how to handle the missing field during sorting.
Without unmapped_type, queries may fail with "incompatible sort types" errors when the same field name has different
types across shards.
Examples:
// Numeric field sorting (Elasticsearch)
{"data.number": {"order": "asc", "unmapped_type": "double"}}
// Text field sorting
{"data.name": {"order": "asc", "unmapped_type": "keyword"}}
// Multiple fields with mixed types
{
"data.priority": {"order": "desc", "unmapped_type": "double"},
"data.name": {"order": "asc", "unmapped_type": "keyword"},
"system.id": "asc"
}
Default Behavior
Every result is sorted, and every sort ends in a system.id, system.type tiebreaker that is added automatically. If
order_by is omitted, results are ordered by relevance score when the request has a fulltext_search, and by
system.id ascending otherwise (query_string_query and filter do not score). A deterministic order is what
keeps paging from skipping or repeating records between requests.
Paging
The paging can be done via the page_cursor parameter in the query. When loading the next page of a result, use the
value provided by the previous response for the page_cursor parameter. The value is available in the link header
or via the data attribute page_cursor in the response content. A page_cursor of null means the response was the
last page: the server asks the index for one item beyond the page, so the signal reflects the result set as it is now
(not a count taken earlier), and the link header's rel="next" is only sent when there is a next page.
Technical Details
The page_cursor has two operation modes:
- for pages below the configured max results window, it uses a numeric value and OpenSearch
fromquery. - from the max results window on, it is an opaque token that uses the
search_afteroption of OpenSearch or Elasticsearch respectively, and paging continues to the end of the result set with or withoutorder_by.
Always pass the cursor back exactly as received. A numeric page_cursor that, together with the page size, reaches
beyond the max results window is rejected with HTTP 400 (the cursor a response returns never addresses such an
offset; it has already switched to the opaque form by then).
The max results window can be configured via symfony configuration:
pimcore_data_hub_simple_rest:
# Limit of page size and offset when paging only works via page cursor (and not page numbers anymore).
max_results_window: 10000