# Product Projection Search
Product projections can be retrieved using full-text search, filtering, and faceting.
This endpoint provides high performance search queries over [ProductProjections](/api/projects/productProjections.md#productprojection) and is typically used to build Storefront Search functionalities that improve the discoverability of products for your customers.
To optimize the usage of this endpoint for your project, check our [performance tips](/api/performance-tips.md#product-projection-search).
This API does **not** support [filtering](/api/projects/product-projection-search.md#filters), [faceting](/api/projects/product-projection-search.md#facets), and [sorting](/api/projects/product-projection-search.md#sorting) for [Standalone Prices](/api/projects/standalone-prices.md).
Existing filters, faceting, and sorting options will still work, but will only consider [Embedded Prices](/api/pricing-and-discounts-overview.md#embedded-prices), therefore using these options on Products with `Standalone` [ProductPriceMode](/api/projects/products.md#productpricemode) might yield inconsistent results.
This API does not support Product-level [Attributes](/api/projects/products.md#attribute).
The Attributes that have the same value in all variants of the Product also apply for Variant Attributes with the `SameForAll` [constraint](/api/projects/productTypes.md#attributeconstraintenum).
Product Projection Search does **not** support [Product Tailoring](/api/projects/product-tailoring.md).
Specifically, this means you cannot:
- Receive tailored product data in returned search results.
- Use tailored data in [full text search](/api/projects/product-projection-search.md#full-text-search).
- Use tailored data in [filters](/api/projects/product-projection-search.md#filters), [facets](/api/projects/product-projection-search.md#facets) or for [sorting](/api/projects/product-projection-search.md#sorting).
If you need to retrieve tailored product data, use the [Get ProductProjection in Store](/api/projects/productProjections.md#get-productprojection-in-store) endpoint or the [Query ProductProjections](/search.md?urn=ctp:api:endpoint:/{projectKey}/product-projections:GET) endpoint with the `storeProjection` parameter instead.
## Activate the Product Projection Search API
The Product Projection Search API is not active for the Project by default.
If deactivated for your Project, the [Product Projection Search](/api/projects/product-projection-search.md#product-projection-search) endpoint returns a [SearchDeactivated](/search.md?urn=ctp:api:type:SearchDeactivatedError) error.
To activate Product Projection Search for your Project, choose one of the following options:
- using the [Merchant Center](/docs/login.md) by navigating to **Settings** > **Project settings** > **Storefront Search**.
- using the [Change Product Search Indexing Enabled](/api/projects/project.md#change-product-search-indexing-enabled) update action with `mode: ProductProjectionsSearch` on the [Update Project](/search.md?urn=ctp:api:endpoint:/{projectKey}:POST) endpoint.
```json
{
"action": "changeProductSearchIndexingEnabled",
"enabled": true,
"mode": "ProductProjectionsSearch"
}
```
Activation triggers the [indexing](/api/projects/product-projection-search.md#index-products) of your Products. The [Product Projection Search API](/api/projects/product-projection-search.md#product-projection-search) as well as the [Search Term Suggestions API](/api/projects/search-term-suggestions.md) will be fully functional after indexing is complete.
### Automatic deactivation
The Product Projection Search API and the Search Term Suggestions API are automatically deactivated for a Project if there have been no calls to them for the duration of 30 consecutive days. The APIs can be reactivated again as described above.
## Product Projection Search
Use Product Projection Search to retrieve Product Projections matching [full-text search](/api/projects/product-projection-search.md#full-text-search), or [filter](/api/projects/product-projection-search.md#filters) criteria, and to let the API calculate [facets](/api/projects/product-projection-search.md#facets) about product information. Perform API calls either with [GET](/api/projects/product-projection-search.md#with-get) or with [POST](/api/projects/product-projection-search.md#with-post).
### with GET
[endpoint definition](/search.md?urn=ctp:api:endpoint:/{projectKey}/product-projections/search:GET).
### with POST
[endpoint definition](/search.md?urn=ctp:api:endpoint:/{projectKey}/product-projections/search:POST).
## Search response
[type definition](/search.md?urn=ctp:api:type:ProductProjectionPagedSearchResponse).
### Query results
The `results` array contains Product Projections where at least one ProductVariant matches the [full-text search](/api/projects/product-projection-search.md#full-text-search) query or the provided [filter](/api/projects/product-projection-search.md#filters) expression.
The result can include Product Variants that do not match the search query.
We recommend setting the query parameter `markMatchingVariants=true` to mark the [matching variants](/api/projects/product-projection-search.md#matching-variants).
### Facet results
Each facet expression specified in the search request contains a corresponding facet result in the `facets` object of the query result. The corresponding results for [term](/api/projects/product-projection-search.md#term-facets), [range](/api/projects/product-projection-search.md#range-facets), and [filtered facets](/api/projects/product-projection-search.md#filtered-facets) are [term facet result](/api/projects/product-projection-search.md#term-facet-result), [range facet result](/api/projects/product-projection-search.md#range-facet-result), and [filtered facet result](/api/projects/product-projection-search.md#filtered-facet-result) respectively.
By default, facet results provide a count of [ProductVariants](/api/projects/products.md#productvariant). An additional count of [Products](/api/projects/products.md#product) can be requested through the [counting products](/api/projects/product-projection-search.md#counting-products) extension. A facet result can be accessed by its attribute path or, if provided in the facet expression, by its [alias](/api/projects/product-projection-search.md#alias).
```bash title="Example for two filtered facet expressions on attribute values, first with alias extension"
variants.attributes.color:"red" as red-things
variants.attributes.size:"m"
```
```json title="Facet result included in the search response"
{
"offset": 0,
"count": 0,
"results": [],
"facets": {
"red-things": {
"type": "filter",
"count": 0
},
"variants.attributes.size": {
"type": "filter",
"count": 0
}
}
}
```
#### Term facet result
[type definition](/search.md?urn=ctp:api:type:TermFacetResult).
##### TermFacetResultType
[type definition](/search.md?urn=ctp:api:type:TermFacetResultType).
##### FacetTerm
[type definition](/search.md?urn=ctp:api:type:FacetTerm).
#### Range facet result
[type definition](/search.md?urn=ctp:api:type:RangeFacetResult).
##### FacetRange
[type definition](/search.md?urn=ctp:api:type:FacetRange).
#### Filtered facet result
[type definition](/search.md?urn=ctp:api:type:FilteredFacetResult).
## Matching variants
For [filters](/api/projects/product-projection-search.md#filters) on searchable fields that refer to the Product as a whole (for example, `categories`, `key`, `slug`), all variants of the Product Projections returned in the [search response](/api/projects/product-projection-search.md#search-response) match the filter by default.
For filters on Product Variant-specific searchable fields, (for example, `variants.attributes.{name}`, `variants.prices.centAmount`), the filter may match only for certain Product Variants.
To find out which of the Product Variants match the search query, we recommend setting `markMatchingVariants` to `true` in the [Product Projection Search](/api/projects/product-projection-search.md#product-projection-search) request.
This will cause the [ProductVariants](/api/projects/products.md#productvariant) to include the `isMatchingVariant` field indicating which Product Variant matches the query.
- If `isMatchingVariant: true`, the Product Variant matches the search query.
- If `isMatchingVariant: false`, the Product Variant does not match the search query, but is returned as part of the returned Product Projection.
If `markMatchingVariants` is set to `false` in the [Product Projection Search](/api/projects/product-projection-search.md#product-projection-search) request, the Product Variants returned in the Product Projection Search response will not contain the `isMatchingVariant` field.
## Full-text search
The `text.{language}` query parameter performs a full-text search on the [ProductProjections](/api/projects/productProjections.md#productprojection) data.
For localized fields, the API only searches text matching the language specified in the `text.{language}` query parameter.
When the same request includes the `facet` query parameter, facet counts are calculated from the Product Projections that match the full-text search query.
Search results are based on the first **256** characters provided with the `text.{language}` query parameter, and characters beyond this limit are ignored. The API will not return an error when you pass longer values for this query parameter.
#### Searchable fields
By default, the API indexes the following standard fields of a [ProductProjection](/search.md?urn=ctp:api:type:ProductProjection) for full-text search:
- `name` (weighed heavier than the other searchable fields)
- `description`
- `slug`
- `sku`
- `searchKeywords`
`metaKeywords` and `metaTitle` are not indexed for full-text search.
The [Attributes](/api/projects/products.md#attribute) on Product Variants of following [AttributeTypes](/api/projects/productTypes.md#attributetype) are indexed for search if the [AttributeDefinition's](/api/projects/productTypes.md#attributedefinition) `isSearchable` is set to `true`:
- [Text type](/api/projects/productTypes.md#attributetexttype) Attributes
- [Localizable text type](/api/projects/productTypes.md#attributelocalizabletexttype) Attributes
- [Money type](/api/projects/productTypes.md#attributemoneytype) Attributes
- The `label` property of each [enum value](/api/projects/productTypes.md#attributeplainenumvalue) in an [enum type](/api/projects/productTypes.md#attributetype) Attribute.
- The `label` property of each [localized enum value](/api/projects/productTypes.md#attributeplainenumvalue) in a [localized enum type](/api/projects/productTypes.md#attributelocalizedenumtype) Attribute.
- [AttributesReferenceType](/api/projects/productTypes.md#attributereferencetype): Attributes are not full-text searchable, but are available for [filtering](/api/projects/product-projection-search.md#filters).
If no `sort` query parameter is given in the [Product Projection Search](/api/projects/product-projection-search.md#product-projection-search) request, the results are sorted by the relevance of the Product to the search text passed.
#### Fuzzy search
If you set the `fuzzy` query parameter in the [Product Projection Search](/api/projects/product-projection-search.md#product-projection-search) request to `true`, the API will return [ProductProjections](/api/projects/productProjections.md#productprojection) that contain slight differences from the search `text`.
For example, a fuzzy search for "whisky" will also return Products that are labeled with "whiskey."
To optimize the discovery of related terms, the API dynamically adapts the level of fuzziness applied on the text based on the length of the text to analyze.
- `0` - for `text` of one or two characters.
- `1` - for `text` of three, four or five characters.
- `2` - for `text` of more than five characters.
If you want to set the fuzzy level to a value different from these defaults, you can set it explicitly with the `fuzzyLevel` query parameter.
However, the value must not exceed the default value, which is selected dynamically based on the length of the `text` value.
The fuzziness level is quantified in terms of the [Damerau-Levenshtein](https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance) distance, which is the number of operations needed to transform one word into another.
## Filters
In addition to [full-text search](/api/projects/product-projection-search.md#full-text-search), [Product Projection Search](/api/projects/product-projection-search.md#product-projection-search) allows you to filter search results for exact values in specified [Product](/search.md?urn=ctp:api:type:Product) fields.
This section describes which [filter parameters](/api/projects/product-projection-search.md#filter-parameters) exist and how you formulate [filter expressions](/api/projects/product-projection-search.md#filter-expressions) for [specific searchable fields](/api/projects/product-projection-search.md#field-specific-filters).
### Filter parameters
To support multi-select faceting, the API allows for filtering products before and after calculating [facets](/api/projects/product-projection-search.md#facets).
- The `filter.query` parameter filters Products in the [query results](/api/projects/product-projection-search.md#query-results) **before** calculating facets.
- The `filter` parameter filters Products in the query results **after** calculating facets.
- The `filter.facets` parameter modifies the aggregated facet counts in the [facet results](/api/projects/product-projection-search.md#facet-results), not the Products returned with the query results.
A facet's own aggregation is not filtered by its corresponding `filter.facets` criteria.
For example, if you're requesting two facets—one for color and another for size—and you apply `filter.facets` for color, the facet aggregation for size will be filtered accordingly, but the facet counts for color itself will remain unaffected.
This allows for multi-select faceting in combination with the `filter` parameter, where storefront users can refine search results across multiple facet values without limiting the overall query results.
```mermaid
graph LR
A[all Products]
B[filtered Products]
C[query results]
D[calculated facets]
E[facet results]
A -- "filter.query" --> B
B -- "filter" --> C
B -- "facet" --> D
D -- "filter.facets" --> E
```
However, not all filter expressions can be used with all filter parameters.
For more information about which [filter expression](/api/projects/product-projection-search.md#filter-expressions) can be used with which filter parameter, see [field-specific filters](/api/projects/product-projection-search.md#field-specific-filters).
### Filter expressions
You can specify filters with filter expressions made of searchable fields, filter operators, and values to search for.
#### Matching value
Filters of the form `field:{value}` search for a specified value for the given field.
The value must match exactly.
If you want fuzzy search for text values, use [full-text search](/api/projects/product-projection-search.md#full-text-search) instead.
#### Matching values
If you want to specify that the given field can have this or that value, you can specify them in the same filter parameter, separated by a comma (`OR` operator).
For example, the below search request filters Products of the color "black" `OR` "grey":
```bash
GET /{projectKey}/product-projections/search?filter%3Dvariants.attributes.color.key%3A%22black%22%2C%22grey%22
```
#### Within range of values
If you want to specify that the values for a given field should be in a certain range, you specify a range filter with its lower- and upper-bounds.
`:range ({from} to {to})`
You can specify multiple ranges for the same filter by separating the ranges by commas:
`:range ({from} to {to}), ({from} to {to})`
Use `*` to ignore the lower- or upper-bound values: `:range (* to {to})`
#### Matching Boolean values
For Boolean fields, specify the value as `true` or `false` without quotation marks.
For example, `variants.availability.isOnStock:true` filters Products that have a [ProductVariant](/search.md?urn=ctp:api:type:ProductVariant) in stock.
For custom [Boolean type](/api/projects/productTypes.md#attributebooleantype) Attributes, the value must be quoted, for example `variants.attributes.{name}:"true"`.
#### Check if value exists
If you want to check if a specific field has a value assigned, but you don't really care about which value, you can use the filter expression of the form `{field}:exists`.
Empty strings (`""`) are recognized as existing values and are caught by this filter.
#### Check if value is missing
If you want to check if a specific field has no value assigned, you can use the filter expression of the form `{field}:missing`.
Empty strings (`""`) are not recognized as missing values and are not caught by this filter.
#### Combine filters with logical AND
You can specify that Products you search for should match filter X AND filter Y at the same time by passing multiple `filter` parameters in one search request.
This combines them with an `AND` operator.
For example, the below search request filters Products of a certain Category `AND` of the color "black":
```bash
GET /{projectKey}/product-projections/search?filter%3Dcategories.id%3A%22{category-ID}%22%26filter%3Dvariants.attributes.color%3A%22black%22
```
### Field-specific filters
This section describes the searchable fields on Products and Product Variants for which you can set filters.
#### Filter by Category
The following filters can be applied:
- `categories.id:"{id}"` filters Products that belong to the specified [Category](/search.md?urn=ctp:api:type:Category).
- `categories:missing` filters Products that are not assigned to any [Category](/search.md?urn=ctp:api:type:Category).
Empty strings (`""`) are not recognized as a missing value and are not caught by this filter.
- `categories:exists` filters Products that are assigned to at least one [Category](/search.md?urn=ctp:api:type:Category).
This filter can be used with `filter`, `filter.query`, and `filter.facets` parameters.
#### Filter by Category subtrees
The following filters can be applied:
- `categories.id: subtree("{id}")` filters Products that belong to the specified [Category](/search.md?urn=ctp:api:type:Category) or any of its descendant Categories.
- `categories.id: subtree("{id}"), subtree("{id2}")` filters Products that belong to different branches of the Category tree.
Other `subtree` functions can be added to the filter. Each subtree specifies the [Category](/search.md?urn=ctp:api:type:Category) of the other branch in the Category tree.
The `subtree` function can also be used in combination with several regular ID filters: `categories.id: subtree("{id1}"), "{id2}", "{id3}"`. If the Categories are children of `{id1}`, it is not necessary to specify `{id2}` and `{id3}`.
The `subtree` function can be applied in `filter.facets`.
#### Filter by Price
This filter only works with [Embedded Prices](/api/types.md#price).
If a Product Variant has several [Embedded Prices](/api/types.md#price), only the first Price is used for filtering.
To use another Product Variant Price (selected based on the Price scope), use the [Scoped Price filter](/api/projects/product-projection-search.md#filter-by-scoped-price).
The following filters can be applied:
- `variants.price.centAmount:{amount}` filters Products whose [Embedded Price](/api/types.md#price) matches the specified value.
- `variants.price.centAmount:range ({from} to {to}), ({from} to {to}), ...` filters Products whose [Embedded Price](/api/types.md#price) matches the specified range that includes the lower- and upper-bound values.
- `variants.price.centAmount:range (* to {to})` filters Products whose [Embedded Price](/api/types.md#price) matches the specified range that includes the lower- and upper-bound values.
Using `*` ignores the lower- or upper-bound values.
- `variants.prices:missing` filters Products that contain a [ProductVariant](/search.md?urn=ctp:api:type:ProductVariant) without any [Embedded Price](/api/types.md#price) set.
Empty strings (`""`) are not recognized as a missing value and are not caught by this filter.
- `variants.prices:exists` filters Products that contain a [ProductVariant](/search.md?urn=ctp:api:type:ProductVariant) with at least one [Embedded Price](/api/types.md#price) set.
These filters can be used with `filter`, `filter.query`, and `filter.facets` parameters.
##### Filter by Scoped Price
To filter Products by Scoped Price, use [Scoped Price Search](/api/pricing-and-discounts-overview.md#scoped-price-search).
Filter by price only works with [Embedded Prices](/api/pricing-and-discounts-overview.md#embedded-prices).
Scoped Price is a Price contained inside a [ProductVariant](/search.md?urn=ctp:api:type:ProductVariant) selected based on the Price scope like currency, country, Channel, Recurrence Policy, and Customer Group. This means that only those Product Variants with a Price matching **all** Price Selection Parameters contain the `scopedPrice` field with the [ScopedPrice](/search.md?urn=ctp:api:type:ScopedPrice). You can apply filters on the `scopedPrice` field and its subfields like `value` and `currentValue` as described in this section.
These filters can be used with `filter`, `filter.query`, and `filter.facets` parameters.
This filter only works with [Embedded Prices](/api/types.md#price).
##### Filter by ScopedPrice value
This filter only works with [Embedded Prices](/api/types.md#price).
Filters, facets, and sorts Products by the original Product Variant [Embedded Price](/api/types.md#price) value.
For example: `variants.scopedPrice.value.centAmount:range (* to 1200)`.
##### Filter by ScopedPrice currentValue
This filter only works with [Embedded Prices](/api/types.md#price).
Filters, facets, and sorts Products by the original Product Variant [Embedded Price](/api/types.md#price) value or a discounted value, if available.
For example: `variants.scopedPrice.currentValue.centAmount:1000`.
##### Filter by ScopedPrice discounted value
This filter only works with [Embedded Prices](/api/types.md#price).
Filters, facets, and sorts Products if an [Embedded Price](/api/types.md#price) `discounted` value is available.
For example: `variants.scopedPrice.discounted.value.centAmount:range (* to 1200)`.
##### Filter by scopedPriceDiscounted
This filter only works with [Embedded Prices](/api/types.md#price).
Filters, facets, and sorts Products by the [ProductVariant](/search.md?urn=ctp:api:type:ProductVariant) `scopedPriceDiscounted`. The value would be `true` if a [ProductDiscount](/search.md?urn=ctp:api:type:ProductDiscount) is applied on the [Embedded Price](/api/types.md#price).
#### Filter by SKU
The following filters can be applied:
- `variants.sku:{sku}` filters Products with a [ProductVariant](/search.md?urn=ctp:api:type:ProductVariant) that matches the specified `sku`.
- `variants.sku:missing` filters Products that have a [ProductVariant](/search.md?urn=ctp:api:type:ProductVariant) with no `sku` set.
Empty strings (`""`) are not recognized as a missing value and are not caught by this filter.
- `variants.sku:exists` filters Products that have a [ProductVariant](/search.md?urn=ctp:api:type:ProductVariant) with a `sku` set.
This filter can be used with the `filter` and `filter.query` parameters.
#### Filter by Product Key
The following filters can be applied:
- `key:{key}` filters Product that matches the specified [Product](/search.md?urn=ctp:api:type:Product) `key`.
- `key:missing` filters [Products](/api/projects/products.md#product) with no `key` set.
Empty strings (`""`) are not recognized as a missing value and are not caught by this filter.
- `key:exists` filters [Products](/api/projects/products.md#product) with a `key` set.
#### Filter by ProductVariant Key
The following filters can be applied:
- `variants.key:{key}` filters Products that have a [ProductVariant](/search.md?urn=ctp:api:type:ProductVariant) matching the specified `key`.
- `variants.key:missing` filters Products that have a [ProductVariant](/search.md?urn=ctp:api:type:ProductVariant) with no `key` set.
Empty strings (`""`) are not recognized as a missing value and are not caught by this filter.
- `variants.key:exists` filters Products that have a [ProductVariant](/search.md?urn=ctp:api:type:ProductVariant) with a `key` set.
#### Filter by ProductType
`productType.id:{id}` filters Products that matches the specified [ProductType](/search.md?urn=ctp:api:type:ProductType).
This filter can be used with the `filter` and `filter.query` parameters.
#### Filter by TaxCategory
The following filters can be applied:
- `taxCategory.id:{id}` filters Products that match the specified [TaxCategory](/search.md?urn=ctp:api:type:TaxCategory).
- `taxCategory:missing` filters Products that do not belong to any [TaxCategory](/search.md?urn=ctp:api:type:TaxCategory).
Empty strings (`""`) are not recognized as a missing value and are not caught by this filter.
- `taxCategory:exists` filters Products that have a [TaxCategory](/search.md?urn=ctp:api:type:TaxCategory) set.
This filter can be used with the `filter` and `filter.query` parameters.
#### Filter by State
The following filters can be applied:
- `state.id:{id}` filters Products that match the specified [State](/search.md?urn=ctp:api:type:State).
- `state:missing` filters Products that do not belong to any [State](/search.md?urn=ctp:api:type:State).
Empty strings (`""`) are not recognized as a missing value and are not caught by this filter.
- `state:exists` filters Products that have a [State](/search.md?urn=ctp:api:type:State) set.
This filter can be used with the `filter` and `filter.query` parameters.
#### Filter by reviewRatingStatistics
The following filters can be applied:
- `reviewRatingStatistics.averageRating:{value}` filters Products that have the average [ReviewRatingStatistics](/search.md?urn=ctp:api:type:ReviewRatingStatistics) matching the specified value.
- `reviewRatingStatistics.averageRating:range ({from} to {to}), ({from} to {to}), ...` filters Products that have the average [ReviewRatingStatistics](/search.md?urn=ctp:api:type:ReviewRatingStatistics) matching the specified range that includes the lower- and upper-bound values. Using `*` ignores the lower- or upper-bound values.
- `reviewRatingStatistics.highestRating:{value}` filters Products that have the highest [ReviewRatingStatistics](/search.md?urn=ctp:api:type:ReviewRatingStatistics) matching the specified value.
- `reviewRatingStatistics.highestRating:range ({from} to {to}), ({from} to {to}), ...` filters Products that have the highest [ReviewRatingStatistics](/search.md?urn=ctp:api:type:ReviewRatingStatistics) matching the specified range that includes the lower- and upper-bound values. Using `*` ignores the lower- or upper-bound values.
- `reviewRatingStatistics.lowestRating:{value}`: filters Products that have the lowest [ReviewRatingStatistics](/search.md?urn=ctp:api:type:ReviewRatingStatistics) matching the specified value.
- `reviewRatingStatistics.lowestRating:range ({from} to {to}), ({from} to {to}), ...`: filters Products that have the lowest [ReviewRatingStatistics](/search.md?urn=ctp:api:type:ReviewRatingStatistics) matching the specified range that includes the lower- and upper-bound values. Using `*` ignores the lower- or upper-bound values.
- `reviewRatingStatistics.count:{value}` filters Products that have a number of [ReviewRatingStatistics](/search.md?urn=ctp:api:type:ReviewRatingStatistics) matching the specified value.
- `reviewRatingStatistics.count:range ({from} to {to}), ({from} to {to}), ...` filters Products that have a number of [ReviewRatingStatistics](/search.md?urn=ctp:api:type:ReviewRatingStatistics) matching the specified range.
This filter can be used with the `filter` and `filter.query` parameters.
#### Filter by Attribute values
To filter by an Attribute value, the Attribute must have the `isSearchable` field set to `true` in its [AttributeDefinition](/search.md?urn=ctp:api:type:AttributeDefinition).
Attributes defined at the Product level are **not supported**.
The following filters can be applied:
- `variants.attributes.{name}:"{value}"` filters [ProductVariants](/api/projects/products.md#productvariant) with the [Attribute](/search.md?urn=ctp:api:type:Attribute) matching the specified value.
- `variants.attributes.{name}:range ({from} to {to}), ({from} to {to}), ...` filters [ProductVariants](/api/projects/products.md#productvariant) with the [Attribute](/search.md?urn=ctp:api:type:Attribute) matching the specified range that includes the lower- and upper-bound values. Using `*` ignores the lower- or upper-bound values.
- `variants.attributes.{name}:missing` filters Products that do not have an [Attribute](/search.md?urn=ctp:api:type:Attribute) set.
Empty strings (`""`) are not recognized as a missing value and are not caught by this filter.
- `variants.attributes.{name}:exists` filters Products that have an [Attribute](/search.md?urn=ctp:api:type:Attribute) set.
- For [AttributeEnumType](/search.md?urn=ctp:api:type:AttributeEnumType) Attributes, the filter must be applied to the `key` of the [AttributePlainEnumValue](/search.md?urn=ctp:api:type:AttributePlainEnumValue).
- `variants.attributes.{myEnumName}.key:"{value}"`
- For [AttributeMoneyType](/search.md?urn=ctp:api:type:AttributeMoneyType) Attributes, the filter must be applied to [Money](/api/types.md#money) `centAmount` or `currencyCode` property.
- `variants.attributes.{myMoneyAttribute}.centAmount:{value}`
- `variants.attributes.{myMoneyAttribute}.centAmount:range ({from} to {to}), ({from} to {to}), ...`
- `variants.attributes.{myMoneyAttribute}.currencyCode:"{value}"`
- For [AttributeReferenceType](/search.md?urn=ctp:api:type:AttributeReferenceType) Attributes, filter by the `id` of [Reference](/api/types.md#reference). The Reference `typeId` is determined by the Attribute definition, so filtering by `typeId` does not narrow results for that Attribute. To check whether the Attribute is set, use `variants.attributes.{myReferenceAttribute}:exists`.
- `variants.attributes.{myReferenceAttribute}.id:"{id}"`
- For [AttributeSetType](/search.md?urn=ctp:api:type:AttributeSetType) Attributes, the filter can be specified multiple times to search for a set that contains at least those values (`AND`). Specifying multiple values, separated by a comma, will match if one of those values is present in the set (`OR`).
For example, the below search request filters Products of the color "green" `AND` "black" `OR` "grey":
```bash
GET /{projectKey}/product-projections/search?filter%3Dvariants.attributes.colors%3A%22green%22%26filter%3Dvariants.attributes.colors%3A%22black%22%2C%22grey%22
```
These filters can be used with the `filter`, `filter.query`, and `filter.facets` parameters.
#### Filter by Product Variant availability
The following filters can be applied:
- `variants.availability.isOnStock:true` filters [ProductVariants](/api/projects/products.md#productvariant) whose [availability](/api/projects/products.md#productvariantavailability) matches the specified value based on an [InventoryEntry](/search.md?urn=ctp:api:type:InventoryEntry) that has no assigned [Channel](/search.md?urn=ctp:api:type:Channel).
- `variants.availability.availableQuantity:range (1 to *)` filters [ProductVariants](/api/projects/products.md#productvariant) with the [availability](/api/projects/products.md#productvariantavailability) matching the specified range that includes the lower- and upper-bound values. Using `*` ignores the lower- or upper-bound values.
- `variants.availability.channels..isOnStock:true` filters [ProductVariants](/api/projects/products.md#productvariant) in a specific supply [Channel](/search.md?urn=ctp:api:type:Channel) with the [availability](/api/projects/products.md#productvariantavailability) matching the specified value.
- `variants.availability.channels..availableQuantity:range (1 to *)` filters [ProductVariants](/api/projects/products.md#productvariant) in a specific supply [Channel](/search.md?urn=ctp:api:type:Channel) with the [availability](/api/projects/products.md#productvariantavailability) matching the specified range that includes the lower- and upper-bound values. Using `*` ignores the lower- or upper-bound values.
- `variants.availability.isOnStockInChannels:"channel-id-1","channel-id-2","channel-id-3"` filters [ProductVariants](/api/projects/products.md#productvariant) where [ProductVariantAvailability](/search.md?urn=ctp:api:type:ProductVariantAvailability) `isOnStock` is `true` in at least one of the specified [Channels](/api/projects/channels.md#channel).
#### Filter by Product searchKeywords
`searchKeywords.{language}.text:"{value}"` filters Products that matches the search keywords in a specified locale.
#### Filter by DateTime
Filters [ProductVariants](/api/projects/products.md#productvariant) created or updated within the specified [DateTime](/api/types.md#datetime) range.
##### Filter by createdAt
`createdAt:range ({from} to {to})` filters [ProductVariants](/api/projects/products.md#productvariant) by the `createdAt` date in a specific date range. For example:
```bash
GET /{projectKey}/product-projections/search?filter.query%3DcreatedAt%3Arange%20(%222015-06-04T12%3A27%3A55.344Z%22%20to%20%222016-06-04T12%3A27%3A55.344Z%22)
```
The search result includes the lower- and upper-bound values. Using `*` ignores the lower- or upper-bound values.
##### Filter by lastModifiedAt
`lastModifiedAt:range ({from} to {to})` filters [ProductVariants](/api/projects/products.md#productvariant) by the `lastModifiedAt` date in a specific date range.
The search result includes the lower- and upper-bound values. Using `*` ignores the lower- or upper-bound values.
## Facets
Facets calculate statistical counts to aid in faceted navigation.
It answers questions like "how many t-shirts of size XL are there?" or "how many TVs are there that cost between US$500 and $600?"
The first question is answered by a [filtered facet](/api/projects/product-projection-search.md#filtered-facets), and the second one by a [range facet](/api/projects/product-projection-search.md#range-facets).
If you want to know how many t-shirts are there for each of the sizes, request a [term facet](/api/projects/product-projection-search.md#term-facets) for the size attribute.
Facet calculation is requested by providing a facet expression through the `facet` query parameter in a [Product Projection Search](/api/projects/product-projection-search.md#product-projection-search) API call.
Facet counts are calculated from the Product Projections that match the full-text search query and any `filter.query` criteria.
Each type of facet has its specific facet expression:
- [term facet expression](/api/projects/product-projection-search.md#term-facet-expression) for term facets,
- [range facet expression](/api/projects/product-projection-search.md#range-facet-expression) for range facets, and
- [filtered facet expression](/api/projects/product-projection-search.md#filtered-facet-expression) for filtered facets.
Optionally, extend the facet expression with [alias](/api/projects/product-projection-search.md#alias) or [counting products](/api/projects/product-projection-search.md#counting-products) parameters.
Find the calculated facets in the [facet results](/api/projects/product-projection-search.md#facet-results).
To facet by an Attribute value, the Attribute must have the `isSearchable` field set to `true` in its [AttributeDefinition](/search.md?urn=ctp:api:type:AttributeDefinition).
Facets for prices only work with [Embedded Prices](/api/pricing-and-discounts-overview.md#embedded-prices).
If you need faceting for [StandalonePrices](/api/projects/standalone-prices.md), use [Product Search](/api/projects/product-search.md#prices) instead.
### Term facets
This facet provides the counts for each of the different values the query parameter has. It is useful, for example, for obtaining all possible values of an Attribute to provide filters for those values on the frontend.
#### Term facet expression
To retrieve facet counts for all occurring values of a [ProductVariant](/search.md?urn=ctp:api:type:ProductVariant) field, the following facets can be applied.
- `categories.id` counts Product Variants of all Categories.
Other built-in fields can be requested analogously.
##### Attributes
- `variants.attributes.{name}` counts Product Variants for all occurring values of custom simple value Attributes such as `text`, `date`, `time`, `datetime`, `boolean`, and `number`.
- `variants.attributes.{name}.{lang}` counts Product Variants for all occurring values of custom `ltext` Attributes in the given language.
- `variants.attributes.{name}.key` counts Product Variants for all occurring `enum` or `lenum` Attribute keys.
- `variants.attributes.{name}.label` counts Product Variants for all occurring `enum` Attribute labels.
- `variants.attributes.{name}.label.{lang}` counts Product Variants for all occurring `lenum` Attribute labels in the given language.
- `variants.attributes.{name}.centAmount` counts Product Variants for all occurring values of a custom `money` Attribute.
- `variants.attributes.{name}.currencyCode` counts Product Variants for all occurring currency codes of a custom `money` Attribute.
For [Set Type](/search.md?urn=ctp:api:type:AttributeSetType) Attributes, term facets count each element in the set as a value of the element type. For example, faceting on a set of `text` Attributes returns term facet buckets for the individual text values.
The following example requests term facets for the Attributes color and size:
```bash
GET /{projectKey}/product-projections/search?facet%3Dvariants.attributes.colors%26facet%3Dvariants.attributes.size%3A%22m%22
```
##### Availability
- `variants.availability.availableQuantity` counts Product Variants for all occurring available quantities.
- `variants.availability.channels..availableQuantity` counts Product Variants for all occurring available quantities for the supply Channel with ID ``.
##### Review ratings
- `reviewRatingStatistics.averageRating` counts Product Variants for all occurring average ratings.
- `reviewRatingStatistics.highestRating` counts Product Variants for all occurring highest ratings.
- `reviewRatingStatistics.lowestRating` counts Product Variants for all occurring lowest ratings.
- `reviewRatingStatistics.count` counts Product Variants for all occurring Review ratings counts.
### Range facets
This type counts [ProductVariants](/api/projects/products.md#productvariant) for which the query value is a range (with minimum and maximum value) specified in the [range facet expression](/api/projects/product-projection-search.md#range-facet-expression), for example, from a Product's [Embedded Prices](/api/types.md#price) to filter Products by price with a range slider.
#### Range facet expression
To aggregate facet counts across ranges of values, the `range` qualifier can be applied analogous to the [filters](/api/projects/product-projection-search.md#filters) with its lower- and upper-bounds:
`:range ({from} to {to})`
Range facet buckets include the lower bound and exclude the upper bound. For example, with adjacent ranges `1` to `10` and `10` to `20`, a value of `10` is counted only in the second bucket. This behavior differs from [range filters](/api/projects/product-projection-search.md#within-range-of-values), which include both bounds.
You can specify multiple ranges for the same facet by separating the ranges by commas:
`:range ({from} to {to}), ({from} to {to})`
Use `*` to ignore the lower- or upper-bound values: `:range (* to {to})`
The `:range` notation is applicable to the `date`, `time`, `datetime`, `number`, and `money` type fields. It is also applicable to a `set` of these types. If one matching element is contained in the `set`, all elements are used in the aggregation.
##### Ranges of Prices
- `variants.price.centAmount:range`
counts Product Variants whose [Embedded Price](/api/types.md#price) falls in one of the specified ranges.
```bash title="Counts Variants whose Price is below 50, between 50 and 100, and above 100."
variants.price.centAmount:range (* to 50), (50 to 100), (100 to *)
```
##### Ranges of Scoped Price
Can only be applied in combination with [Scoped Price search](/api/pricing-and-discounts-overview.md#scoped-price-search).
- `variants.scopedPrice.currentValue.centAmount:range`
counts Product Variants whose [ScopedPrice](/search.md?urn=ctp:api:type:ScopedPrice) `currentValue` falls in one of the specified ranges.
```bash title="Counts Product Variants whose Scoped Price is below 1000, between 1000 and 2000, and between 2000 and 3000 cents."
variants.scopedPrice.currentValue.centAmount:range (* to 1000), (1000 to 2000), (2000 to 3000)
```
##### Ranges of Money type Attributes
- `variants.attributes.{name}:range`
counts Product Variants whose values of the Attribute fall in one of the specified ranges.
### Filtered facets
This facet provides counts for specific values of [ProductVariant](/search.md?urn=ctp:api:type:ProductVariant) fields.
#### Filtered facet expression
To get facet counts for a specific field value, the requested value can be appended after a colon. This notation can be applied to any kind of field analogous to the [filters](/api/projects/product-projection-search.md#filters).
##### Categories
- `categories.id:"{id}"` counts Product Variants in the specified Category.
- `categories.id: subtree("{id}")` counts Product Variants in the subtree of the specified Category.
- `categories.id: subtree("{id1}"), subtree("{id2}")` counts Product Variants for each of the specified subtrees.
Unlike filters, facets using `subtree` cannot be mixed with regular value facets, so `categories.id: subtree("{id}"), "{id2}"` will ignore `{id2}`.
- `categories.id: subtree("*")`: The asterisk parameter is equivalent to counting the [ProductVariants](/api/projects/products.md#productvariant) that have a Category assigned.
##### Prices
- `variants.price.centAmount:{amount}` counts Product Variants that have the specified [Embedded Price](/api/types.md#price).
##### Attributes
- `variants.attributes.{name}:{value}` counts Product Variants with the Attribute that match the specified value of a simple Attribute.
String values must be set in quotation marks. Add the respective postfixes for other Attribute types analogous to when requesting facets for all values (see above).
- `variants.attributes.{name}:{value},{value2}` counts Product Variants with the Attribute that match at least one of the values in the comma-separated list.
- `variants.attributes.{myEnumName}.key:"{value}"` counts the occurrences of a specific enum key.
For [Set Type](/search.md?urn=ctp:api:type:AttributeSetType) Attributes, filtered facets match against the elements in the set. A Product Variant is counted if the set contains the requested value.
### Facet expression extensions
#### Alias
Optionally, you can give an alias to all facets that can be used instead of the Attribute path in the result. This makes it possible to calculate multiple facets on the same Attribute in the same search request.
Examples:
```bash title="Returns the facet with a meaningful name (default is the Attribute name)."
variants.attributes.color.key:"red" as red_stuff
```
```bash title="Returns the facet counts for two specific values of the same Attribute without causing a naming conflict."
variants.attributes.brand:"Coca-Cola" as brand_coke
variants.attributes.brand:"Pepsi" as brand_pepsi
```
```bash title="Allows to show a marketing hint about many Products cheaper than 100 on a page that also shows the normal price filter."
variants.price.centAmount:range(0 to 9999) as price_below_100
variants.price.centAmount
```
#### Counting Products
In addition to [ProductVariants](/api/projects/products.md#productvariant), all facets can optionally count [Products](/api/projects/products.md#product) by appending `counting products` to a facet expression.
The corresponding [facet result](/api/projects/product-projection-search.md#facet-results) then contains an additional field `productCount` that shows the number of [Products](/api/projects/products.md#product).
Examples:
- `variants.attributes.color.key counting products` returns a [term facet result](/api/projects/product-projection-search.md#term-facet-result) for each [FacetTerm](/api/projects/product-projection-search.md#facetterm) (in this case for each color) containing:
- the number of [ProductVariants](/api/projects/products.md#productvariant) with that color.
- the number of [Products](/api/projects/products.md#product) having at least one [ProductVariant](/search.md?urn=ctp:api:type:ProductVariant) with that color.
- `variants.price.centAmount:range (0 to 10000) counting products` returns a [range facet result](/api/projects/product-projection-search.md#range-facet-result) for each [FacetRange](/api/projects/product-projection-search.md#facetrange) containing:
- the number of [ProductVariants](/api/projects/products.md#productvariant) having an [Embedded Price](/api/types.md#price) in that [FacetRange](/api/projects/product-projection-search.md#facetrange).
- the number of [Products](/api/projects/products.md#product) having at least one [ProductVariant](/search.md?urn=ctp:api:type:ProductVariant) with an [Embedded Price](/api/types.md#price) in that [FacetRange](/api/projects/product-projection-search.md#facetrange).
- `variants.attributes.color.key:"red" counting products` returns a [filtered facet result](/api/projects/product-projection-search.md#filtered-facet-result) containing:
- the number of [ProductVariants](/api/projects/products.md#productvariant) that are red.
- the number of [Products](/api/projects/products.md#product) having at least one red [ProductVariant](/search.md?urn=ctp:api:type:ProductVariant).
- `categories.id:"{id}" counting products` returns a [filtered facet result](/api/projects/product-projection-search.md#filtered-facet-result) containing:
- the number of [Products](/api/projects/products.md#product) in the specified Category.
- the sum of all [ProductVariants](/api/projects/products.md#productvariant) of these [Products](/api/projects/products.md#product).
## Sorting
By default, search results are sorted in a descending order of relevance with respect to the provided `text` (that is their "score"). An alternative sorting can be specified using the `sort` parameter with the structure `{field} {direction}`.
Compound sorting (also known as multi-sort) is applied when specifying multiple sort parameters ordered by priority with each parameter having its own sorting direction, ascending (`asc`) or descending (`desc`).
If the `sort` parameters do not define an ordering, the position of some or all Products can be non-deterministic that is not predictable and repeatable across API requests.
Common cases are:
- When neither `text` nor `sort` is given, the complete order is non-deterministic.
- When full text is provided but no `sort` is given, the results are returned in a descending order of relevance.
- When Products have no value set on the fields that are the sort criteria, the position is non-deterministic among those with no value set.
- When Products have identical vales on the fields that are the sort criteria, the position among each group with identical values is non-deterministic.
To achieve a deterministic order, always have a `sort` parameter at the end of the list of `sort` parameters that sorts by a field or attribute that has distinct values across all products. For example, `createdAt` or an Attribute specific for this purpose.
The following are standard fields to sort by:
- `name.{language}`
Sorts Products by their name.
The field must include the language in the form of a [Locale](/api/types.md#locale).
- `categoryOrderHints.{category ID}`
Sorts Products by their [CategoryOrderHint](/api/projects/products.md#categoryorderhints) in combination with a [filter by category](/api/projects/product-projection-search.md#filter-by-category).
- `createdAt`
Sorts Products by their creation date.
- `lastModifiedAt`
Sorts Products by their last modification date.
- `id`
Sorts Products by their ID.
- `score`
Sorts Products by the search relevance score.
Sorting by `score` can be combined with other sorting criteria. For example, sorting by `score` and `id` ensures consistent order of Products across several search requests for Products that have the same relevance score.
```bash title="Example for sorting by the German name of the Product in ascending order"
name.de asc
```
#### Sort by Price
The sorting only works with [Embedded Prices](/api/types.md#price).
`price` sorts Products by their [Embedded Price](/api/types.md#price). Since Prices can vary across Variants of a Product, the highest Embedded Price is used for a descending sort direction, while the lowest Embedded Price is used for an ascending sort direction.
Only the `centAmount` of the **first** [Embedded Price](/api/pricing-and-discounts-overview.md#embedded-prices) from the prices of every [ProductVariant](/search.md?urn=ctp:api:type:ProductVariant) is used for sorting. If you have more than one Embedded Price defined per Product Variant, consider [sorting by Scoped Price](/api/projects/product-projection-search.md#sort-by-scoped-price).
#### Sort by Scoped Price
To sort Products by their Variant's `scopedPrice`, use [Scoped Price Search](/api/pricing-and-discounts-overview.md#scoped-price-search).
- `variants.scopedPrice.value`
Sorts Products by their [ScopedPrice](/search.md?urn=ctp:api:type:ScopedPrice) `value`.
- `variants.scopedPrice.currentValue`
Sorts Products by their [ScopedPrice](/search.md?urn=ctp:api:type:ScopedPrice) `currentValue`.
Sorting by `value` or `currentValue` might return an unexpected result if you don't specify one of their subfields. For example, to sort by the numerical value of the Price, you must specify the `.centAmount` subfield as follows: `variants.scopedPrice.currentValue.centAmount`.
Since prices can vary across Variants of a single Product, the following behavior applies: When the sort direction is descending, the highest [Embedded Price](/api/pricing-and-discounts-overview.md#embedded-prices) of a product is used for sorting. When the sort direction is ascending, the lowest [Embedded Price](/api/pricing-and-discounts-overview.md#embedded-prices) is used.
Product Projection Search compares only [Embedded Prices](/api/pricing-and-discounts-overview.md#embedded-prices) matching exactly the Price Selection Parameters given with the [Scoped Price Search](/api/pricing-and-discounts-overview.md#scoped-price-search) request and is therefore the safer option when there is more than one Price on a Product Variant compared to [sorting by Price](/api/projects/product-projection-search.md#sort-by-price).
#### Sort by Reviews
The following sorts can be applied based on [ReviewRatingStatistics](/search.md?urn=ctp:api:type:ReviewRatingStatistics):
- `reviewRatingStatistics.averageRating`
Sorts Products by their average rating.
- `reviewRatingStatistics.highestRating`
Sorts Products by their highest rating.
- `reviewRatingStatistics.lowestRating`
Sorts Products by their lowest rating.
- `reviewRatingStatistics.count`
Sorts Products by the number of ratings.
#### Sort by values found on ProductVariants
If Products have several Product Variants, each Variant can have a different value on the sorted Attribute. You must specify whether the minimum or maximum value is to be used across the Variants to compare the Product with other Products.
By default, the minimum value is used when sorting in ascending order (`asc.min`) and the maximum value is used when sorting in descending order (`desc.max`). For a different sorting behavior, the parameters can be set to `asc.max` or `desc.min` respectively.
The field names to sort Products by values found on [ProductVariants](/api/projects/products.md#productvariant) start with `variants.`.
#### Sort by SKU
`variants.sku` sorts Products sorted alphabetically by their SKUs.
#### Sort by availability
To sort products by availability:
- `variants.availability.restockableInDays`
Sorts Products by number of days it takes to restock it (with no supply Channel).
- `variants.availability.channels..restockableInDays`
Sorts Products by number of days it takes to restock it for a specific supply Channel.
- `variants.availability.availableQuantity`
Sorts Products by available quantity (with no supply Channel).
- `variants.availability.channels..availableQuantity`
Sorts Products by available quantity for a specific supply Channel.
```bash title="Example for sorting by minimal available quantity of Products in descending order"
`variants.availability.availableQuantity desc.min`
```
#### Sort by Attributes
In addition to the sorting by the standard fields listed [above](/api/projects/product-projection-search.md#sorting), Products can also be sorted by their specific custom Attributes.
The search endpoint only supports those Attributes that have been declared as searchable in the [ProductType](/search.md?urn=ctp:api:type:ProductType).
Attributes can only be sorted if the `isSearchable` field is set to `true` in its [AttributeDefinition](/search.md?urn=ctp:api:type:AttributeDefinition).
The following sort can be applied:
- `variants.attributes.{name}`
Sorts Products by simple value Attributes such as `text`, `date`, `time`, `datetime`, `boolean`, and `number`.
- `variants.attributes.{name}.{lang}`
Sorts Products by `ltext` Attribute in the specified language.
- `variants.attributes.{name}.key`
Sorts Products by `enum` or `lenum` Attribute key.
- `variants.attributes.{name}.label`
Sorts Products by `enum` Attribute label.
- `variants.attributes.{name}.label.{lang}`
Sorts Products by `lenum` Attribute label in the specified language.
- `variants.attributes.{name}.centAmount`
Sorts Products by `money` Attribute amount.
- `variants.attributes.{name}.currencyCode`
Sorts Products by `money` Attribute currency.
```bash title="Example for sorting by English labels of a 'lenum' Attribute 'color' in ascending order"
variants.attributes.color.label.en asc.max
```
## Pagination
A response to the search request contains the first 20 results by default.
Pagination allows you to retrieve the first 10000 results by requesting them page by page.
The `total` field in a query result indicates how many results match the search query in total.
### Limit
With the `limit` parameter you can set the maximum number of results returned on a page.
Any value between `0` and `500` is allowed, the default limit is `20`.
Set `limit=0` if you want to retrieve `facets` only, but not the search result.
### Offset
The `offset` parameter controls the starting point for retrieving paginated results.
With the default value `0` you retrieve the first page of query results.
Setting `offset=1` skips the first page and returns the second page of results, and so on.
Each page contains a number of results defined by the `limit` parameter.
The maximum allowed `offset` is 10000.
Setting pagination parameters exceeding this limit will result in an error with the code "SearchExecutionFailure."
## Index Products
Every update on a product in the catalog must be indexed before the change is available in the Product Projection Search endpoint.
This [indexing delay](/api/general-concepts.md#eventual-consistency) is not the same for all updates.
Products with many variants, locales, prices, or searchable attributes need more time to get indexed than others, and thus, have a longer delay.
Additionally, when changing the configuration of your Project that affects Products, for example, changing locales, currencies, countries, Customer Groups, or adding Attributes to a Product Type, it will trigger a recreation of the index for all products (full-reindex).
Any following changes are put on hold until the process is completed and then applied later.
## Related pages
- [Area overview page with navigation](/api.md)
- [Previous page: Product Search](/api/projects/product-search.md)
- [Next page: Search Term Suggestions](/api/projects/search-term-suggestions.md)
- [Search documentation and API specs](/search.md)