This is the early access documentation preview for Custom Views. This documentation might not be in sync with our official documentation.
Order Search
The Order Search feature is intended for merchants to perform search requests on a high number of Orders in a Project. It is not intended for searching through the customer's order history in a storefront application.
The Order Search API does not return the resource data of the matching Orders. Instead, it returns a list of Order IDs, which can then be used to fetch the Orders by their ID.
Since the response also contains the version of the matching Order, it allows you to:
- keep a cache of Orders
- compare version numbers to detect outdated Orders
- detect if the search index for an Order is outdated and react on that (for example by showing a warning, or trigger a rebuild of the search index).
Activation of the feature The Order Search feature is not active for the Project by default. Before first use, indexing has to be activated either
- via API using the Change Order Search Status update action on the Project.
- via contacting us on the Support Portal and provide the region, project key(s), and use case(s).
Once the Project setting has been changed, the indexing of all Orders existing in the Project will start and the Search Orders endpoint will become fully functional soon after.
Automatic deactivation The Order Search index for a Project will be deactivated automatically if there have been no calls against the Search Orders endpoint within the last 30 days. Indexing can be reactivated again with one of the options listed above.
Representations
OrderPagedSearchResponse
total Int | Total number of results matching the query. |
offset Int | Number of elements skipped. |
limit Int | Number of results requested. Maximum:100 |
hits Array of Hit | Actual results. |
Hit
id String | Unique identifier of the Order. |
version Int | Current version of the Order. |
relevance Float | The higher the value is, the more relevant the hit is for the search request. Maximum:1 |
Search Orders
view_orders:{projectKey}
region String | Region in which the Project is hosted. |
projectKey String |
|
application/json
query | The Order search query. |
sort Array of OrderSearchSorting | Controls how results to your query are sorted. If not provided, the results are sorted by relevance in descending order. |
limit Int | The maximum number of search results to be returned. Default:10 Maximum: 100 |
offset Int | The number of search results to be skipped in the response for pagination. Default:0 Maximum: 10 000 |
application/json
{"total": 3,"offset": 0,"limit": 50,"hits": [{"id": "c2d430e8-1ccf-44c8-8976-36763b572f3e","version": 12,"relevance": 0.9871},{"id": "ee8e3533-00d2-43c2-a2f4-57b1eee0f5b4","version": 2,"relevance": 0.735654},{"id": "9a9e129c-2ed1-436d-90d9-af60d83490ed","version": 44,"relevance": 0.42415}]}
Query section
This section describes the query language to be used on the Search Orders endpoint as well as for the Advanced Order Search in the Merchant Center.
A query consists of:
- A compound expression wrapper, like
and
,or
,not
,filter
- A query expression, like
exact
,fullText
. You can use more than one query expression per query. - A set of query fields. The
field
andvalue
fields are required, but there are other optional fields.
Example query:
{"query": {"and": [{"fullText": {"field": "customLineItems.name","language": "en","value": "banana"}},{"filter": [{"exact": {"field": "store.name","language": "en","value": "fruit_store"}}]}]},"sort": [{"field": "customLineItems.name","language": "en","order": "desc"}],"limit": 50,"offset": 0}
This query is searching for all orders which have banana
in their lineItems.name
and are in the fruit_store
.
This can be seen as:
(lineItems.name.en=banana AND (filter (store.name=fruit_store)))
The result will be all documents where lineItems.name.en
contains 'banana' and the store name is 'fruit_store', sorted descending (desc
) by lineItems.name.en
.
The result will be limited to the first 50 hits.
Compound expressions
The outermost layer of the query is a compound expression. This expression specifies how the composition of the sub-expressions is evaluated. Sub-expressions can be query expressions or compound expressions.
Following compound expressions are currently supported:
Expression | Behavior |
---|---|
and | only matches documents that match all sub-expressions. |
or | only matches documents where at least one of the sub-expressions is matched. |
not | only matches documents that do not match any of its sub-expressions. |
filter | Matching documents of a query are checked for their relevancy to the search. The relevancy is expressed by an internal score. All expressions except filter expressions contribute to that score. All sub-expressions of a filter are implicitly connected with an and expression. |
A compound expression can only be used one time on the same level of composition.
For example, this composition of two and
expressions is not supported: Z = (A and
B and
C),
using only one and
expression, like: X = (A and
B), is allowed.
Using a different compound expression is allowed too, like for: Y = (A and
B or
C).
Z can still be specified by (X and
C) since X is a compound expression on a different level then.
Query expressions
A query expression contains:
- a
field
- a
value
- for localized texts: a
language
field. Thelanguage
value is an IETF language tag.
The index is calculated with languages defined in the Project. It is not possible to find documents with a non-defined language.
Query fields
The field
can have any of the searchable Order fields, like orderNumber
, createdAt
, country
including fields of nested objects like LineItems (lineItems.productId
) or ShippingMethods (shippingAddress.firstName
).
Example of a query finding orders with a specific price:
{"query": {"and": [{"exact": {"field": "totalPrice.currencyCode","value": "EUR"}},{"exact": {"field": "totalPrice.centAmount","value": 2222}}]}}
Searchable Order fields
These are the Order fields that are currently supported by the Order Search feature. Next to each field you will find a type that determines what query expression type can be used on that field:
all
- textFieldbillingAddress.city
- textFieldbillingAddress.company
- textFieldbillingAddress.country
- textFieldbillingAddress.firstName
- textFieldbillingAddress.lastName
- textFieldbillingAddress.mobile
- phoneFieldbillingAddress.phone
- phoneFieldbillingAddress.postalCode
- textFieldcompletedAt
- dateTimeFieldcountry
- textFieldcreatedAt
- dateTimeFieldcreatedBy.anonymousId
- keywordFieldcreatedBy.clientId
- textFieldcreatedBy.externalUserId
- keywordFieldcustom.<field-name>
customLineItems.name
- localizedTextFieldcustomLineItems.state.quantity
- longFieldcustomLineItems.state.state.key
- keywordFieldcustomLineItems.state.state.name
- localizedTextFieldcustomerEmail
- textFieldcustomerGroup.id
- keywordFieldcustomerGroup.key
- textFieldcustomerGroup.name
- textFieldid
- keywordFielditemShippingAddresses.city
- textFielditemShippingAddresses.company
- textFielditemShippingAddresses.country
- textFielditemShippingAddresses.firstName
- textFielditemShippingAddresses.lastName
- textFielditemShippingAddresses.mobile
- phoneFielditemShippingAddresses.phone
- phoneFielditemShippingAddresses.postalCode
- textFieldlastModifiedAt
- dateTimeFieldlastModifiedBy.anonymousId
- keywordFieldlastModifiedBy.clientId
- textFieldlastModifiedBy.externalUserId
- keywordFieldlineItems.name
- localizedTextFieldlineItems.productId
- textFieldlineItems.state.quantity
- longFieldlineItems.state.state.key
- keywordFieldlineItems.state.state.name
- localizedTextFieldlineItems.variant.sku
- textFieldorderNumber
- keywordFieldorderState
- textFieldorigin
- keywordFieldpaymentInfo.payments.interfaceId
- keywordFieldpaymentInfo.payments.paymentMethodInfo.method
- textFieldpaymentInfo.payments.transactions.id
- keywordFieldpaymentInfo.payments.transactions.interactionId
- keywordFieldpaymentState
- textFieldreturnInfo.returnDate
- dateTimeFieldreturnInfo.returnTrackingId
- keywordFieldshipmentState
- textFieldshippingAddress.city
- textFieldshippingAddress.company
- textFieldshippingAddress.country
- textFieldshippingAddress.firstName
- textFieldshippingAddress.lastName
- textFieldshippingAddress.mobile
- phoneFieldshippingAddress.phone
- phoneFieldshippingAddress.postalCode
- textFieldshippingInfo.deliveries.parcels.trackingData.trackingId
- keywordFieldshippingInfo.shippingMethodName
- textFieldstate.key
- keywordFieldstate.name
- localizedTextFieldstore.key
- keywordFieldstore.name
- localizedTextFieldtotalPrice.centAmount
- longFieldtotalPrice.currencyCode
- keywordFieldversion
- longField
Expanded fields
Following fields in the Order model are Reference Expansions that are not updated regularly. They are kept in the initial order state, even if the referred resource is changed. A manual re-indexing is required to get the most recent values of the following fields
state
-key
,name
lineItems.state.state
-key
,name
,quantity
customLineItems.state.state
-key
,name
customerGroup
-key
,name
store
-key
,name
paymentInfo.payments
interfaceId
,paymentMethodInfo.method
,transactions.id
,transactions.interactionId
Custom Fields
It is possible to search in the order's custom fields.
Therefore, the customType
needs to be specified in the query.
Currently, the following types are supported.
BooleanType
StringType
LocalizedStringType
EnumType
LocalizedEnumType
NumberType
DateType
TimeType
DateTimeType
SetType.StringType
SetType.LocalizedStringType
SetType.EnumType
SetType.LocalizedEnumType
SetType.NumberType
SetType.DateType
SetType.TimeType
SetType.DateTimeType
{"query": {"exact": {"field": "custom.myOrderField","value": "special order","customType": "StringType"}}}
If you want to search for a date in a custom field SetType with many dates, a query might look like this:
{"query": {"prefix": {"field": "custom.myDateField","value": "2021-05-10","customType": "SetType.DateType"}}}
The customTypes
support the following query expressions, where SetType
support the same expressions as the type inside the Set
:
customType | fullText | exact | prefix | range | wildcard | exists |
---|---|---|---|---|---|---|
NumberType | ✓ | ✓ | ✓ | |||
StringType, EnumType, DateType, TimeType, DateTimeType | ✓ | ✓ | ✓ | ✓ | ✓ | |
LocalizedStringType, LocalizedEnumType | ✓ | ✓ | ✓ | ✓ | ✓ | |
BooleanType | ✓ | ✓ |
Query expression types
fullText
Performs a full text search of the field.
fullText
queries have the option to pass mustMatch
on how multiple terms should be combined.
mustMatch
can either be any
or all
(the default is all
).
If you search for 'yellow car' the search will find documents that contain both 'yellow' and 'car' by default.
When you now pass any
as mustMatch
the search will find documents that contain either 'yellow' or 'car'.
Note that fullText
search will only return Orders for which the terms match completely.
If you need partial matches, the prefix search would be the better approach.
A fullText
query performs a full-text search:
{"query": {"fullText": {"field": "customLineItems.name","language": "en","value": "yellow car","mustMatch": "any"}}}
exact
Searches for exact values only. If you provide a value of yellow car
, a field with the best yellow car
will not match.
This means the value you provide for searching must exactly match the value in the order search.
exact
queries have the option to treat the search term caseInsensitive
.
An exact
query only matches exact values:
{"query": {"exact": {"field": "lineItems.variant.sku","value": "chiquita_yellow_123","caseInsensitive": true}}}
prefix
Searches the field specified for ones that begin with the prefix specified.
prefix
queries have the option to treat the search term caseInsensitive
.
A prefix
query only matches values that start with the given one:
{"query": {"prefix": {"field": "customerEmail","value": "commerceto","caseInsensitive": true}}}
Note that searching for yell ca
in the value yellow car
will not lead to a match, all terms need to form one prefix, you would need to search for yellow c
to match yellow car
.
range
A range
query only matches values between the specified boundaries and is useful for restricting the search query to certain time frames or ranges of numerical values. The interval may also be open by omitting the upper or lower boundary value.
Example query for Orders last modified between two specific dates:
{"query": {"range": {"field": "lastModifiedAt","gte": "2018-08-25T12:00:00.000Z","lte": "2018-08-26T12:00:00.000Z"}}}
wildcard
A wildcard
query matches documents that have fields matching the specified wildcard expression.
In wildcard expression, the following characters have a special meaning:
*
for one or more characters?
for exactly one character.
wildcard
queries have the option to treat the search term caseInsensitive
.
{"query": {"wildcard": {"field": "customerEmail","value": "ab*@commercetools.com"}}}
{"query": {"wildcard": {"field": "customerEmail","value": "ab?@commercetools.com","caseInsensitive": true}}}
It is even possible to use more than one wildcard in one term.
{"query": {"wildcard": {"field": "customerEmail","value": "ab?@*.com","caseInsensitive": true}}}
Like the prefix
and exact
, wildcard
queries as well have the option to treat the search term caseInsensitive
.
Wildcard expression are quite expensive to calculate. Prefer a prefix expression if this is suitable for your use-case.
exists
An exists
query matches documents that have a field with a non-null value:
{"query": {"exists": {"field": "customerEmail"}}}
boost (optional)
When you provide more than one query expression to a query, the boost
field makes the results that match one particular query more relevant than the results that match the others.
Example query: Documents that have butter
in the lineItems.name
field are scored as more relevant than those that have butter
in their customLineItems.name
field.
{"query": {"or": [{"fullText": {"field": "lineItems.name","language": "en","value": "butter","boost": 2}},{"fullText": {"field": "customLineItems.name","language": "en","value": "butter"}}]}}
Types of fields
- booleanField: for Boolean fields.
- longField: for Number fields.
- dateTimeField: for DateTime fields.
- keywordField: for fields holding unique identifiers.
- textField: for String fields.
- localizedTextField: for LocalizedString fields.
- phoneField: for phone number-formatted fields.
What query expression type can be used on what type of field?
The different query expression types (fullText, exact, prefix, range, wildcard and exists) can't all be used on all order fields, depending on the type of the order field.
Types | fullText | exact | prefix | range | wildcard | exists |
---|---|---|---|---|---|---|
booleanField | ✓ | ✓ | ||||
longField | ✓ | ✓ | ✓ | |||
dateTimeField | ✓ | ✓ | ✓ | |||
keywordField | ✓ | ✓ | ✓ | ✓ | ||
textField | ✓ | ✓ | ✓ | ✓ | ✓ | |
localizedTextField | ✓ | ✓ | ✓ | ✓ | ✓ | |
phoneField | ✓ | ✓ | ✓ | ✓ |
Specifics for phoneField
When searching on a phoneField
type of field all non numeric characters are ignored.
For example, a query of type fullText for (783) 627-3740
will also match (783) 6273740
.
The same applies to prefix queries. Special characters are only taken into account when performing queries of type exact.
Sorting
Sorting allows you to control how results to your query are sorted.
If no sorting is specified, the results are sorted by relevance in descending (desc
) order.
Example query (sorts the results ascending by createdAt
):
{"sort": [{"field": "createdAt","order": "asc"}]}
Sort mode
If you sort by a field in an array (like customLineItems.name
) you can optionally pass a sort mode.
This is relevant because a single order can have multiple lineItems and thus multiple name
fields.
That means that there might not be a single value to sort on, but multiple.
Using the sort mode we can choose which of the values in the array to use for sorting or how to aggregate them.
The default sorting mode is min
Following four sort modes are provided:
min
- Use the minimum of all available valuesmax
- Use the maximum of all available valuesavg
- Use the average of all available valuessum
- Use the sum of all available values.
Example query (Using the min
sort mode, will sort using the min customLineItems.name
in descending order):
{"sort": [{"field": "customLineItems.name","language": "en","order": "desc","mode": "min"}]}
Sort filter
In case the above sort modes are not enough to specify exactly which document or aggregation you want to sort on, you can use a sort filter
.
{"sort": [{"field": "lineItems.name","language": "en","order": "asc","filter": {"exact": {"field": "lineItems.productId","value": "4054a159-7f3e-4fe9-a30c-8db80ca7d665"}}}]}
Note that you can only filter on the same parent field you sort by. In this case on the root of the order.
Reindex Orders
If you realize the Order Search API does not contain the latest Orders, you can trigger a rebuild of the search index. This service is provided on a GraphQL endpoint by submitting a specific reindex mutation.
Reindex mutation
OAuth 2.0 Scopes: manage_project_settings:{projectKey}
, view_orders:{projectKey}
mutation ReindexAllOrders {reIndexAllOrders {indexingJobIdexistingIndexingJobId}}
This mutation returns following indexing job IDs:
indexingJobId
is the ID of a new indexing job.existingIndexingJobId
is the ID of an already running job making progress.
Check indexing progress
For each indexing job ID you can check the status of the respective jobs like so:
OAuth 2.0 Scopes: manage_project_settings:{projectKey}
, view_orders:{projectKey}
query GetReindexingStatus ($jobId: String!) {getReindexingStatus(id: $jobId) {nbrOfIndexedDocumentsnbrOfFailedDocumentstotalNbrOfDocumentspercentCompletedcompleted}}
Stop indexing jobs
You can stop the indexing of all Orders with following mutation:
OAuth 2.0 Scopes: manage_project_settings:{projectKey}
mutation StopIndexingOrders {stopOrdersIndexing {status}}
The response contains the status
of the indexing job.
GraphQL endpoint
You can access the GraphQL endpoint with following URL:
https://api.{region}.commercetools.com/{projectKey}/orders/indexer/graphql
The endpoint accepts HTTP POST requests with following fields in a JSON body:
query
- String - GraphQL query as a stringvariables
- Object - Optional - containing JSON object that defines variables for your queryoperationName
- String - Optional - the name of the operation, in case you defined several of them in the query
GraphiQL
To explore the GraphQL endpoint, you can use an interactive GraphiQL environment with a web browser, accessible through the following URL:
https://api.{region}.commercetools.com/{projectKey}/orders/indexer/graphiql?token={access_token}
Check search index
To check whether a search index exists for the Project's Orders the following endpoint can be used.
Endpoint: /{projectKey}/orders/search
Method: HEAD
OAuth 2.0 Scopes: view_project_settings:{projectKey}
Response status codes:
200
- the index exists and the Search Orders endpoint can be used.404
- the index does not exist and the Search Orders endpoint returnsError 404
only.
On the GraphQL endpoint you can use following query:
query FetchIndicesExists {ordersIndicesExist {searchableIndexExistsnewInProgress}}
The query response contains the following fields indicating the status of the search index:
searchableIndexExists
- the index exists and Orders can be searched.newInProgress
- the index is being created by a job. Use thereIndexAllOrders
mutation to get the job ID.
Find below an example for a FetchIndicesExists
query to be executed as cURL command:
$ curl -X POST https://api.{region}.commercetools.com/{projectKey}/orders/indexer/graphql \-H "Content-Type:application/json" \-H "Authorization:Bearer ..." \-d '{"query": "{ ordersIndicesExist { searchableIndexExists } }" }'