Product Projection Search

Product projections can be retrieved using full-text search, filtering and faceting functionality.

Search ProductProjections

This endpoint provides high performance search queries over ProductProjections.
The query result contains the ProductProjections for which at least one ProductVariant matches the search query. This means that variants can be included in the result also for which the search query does not match. To determine which ProductVariants match the search query, the returned ProductProjections include the additional field isMatchingVariant.

Endpoint: /{projectKey}/product-projections/search
Method: GET or POST
OAuth2 Scopes: view_products:{projectKey}
Response Representation: PagedQueryResult with the results array of ProductProjection

Mark matching variants:

If markMatchingVariants parameter is true those ProductVariants that match the search query have the additional field isMatchingVariant set to true. For the other variants in the same product projection this field is set to false.
If markMatchingVariants parameter is false the ProductVariants do not contain the field isMatchingVariant.

Parameters:

Note that currently the price selection parameters only influence the scoped price sorting, filtering and faceting. Use it with caution though since scoped price search will not follow price fallback rules. Just like with price selection, scopedPrice and scopePriceDiscounted would be selected based on the currency, country, customer group and channel.

The parameters are sent:

Example GET request

GET /{projectKey}/product-projections/search?staged=true&limit=10&text.en="simpsons"

The text.{language} query parameter performs the full-text search on the ProductProjections.

The text search includes the name, description, slug, sku and searchKeywords fields by default.
Custom attributes of the types text and ltext as well as the label of enum and lenum attributes are considered in the product search only after they have been defined as isSearchable in their AttributeDefinition.
On the localized fields ltext and lenum, the full text search will only consider the content in the language that was passed via the {language} part of the text parameter. If that language is not filled in the localized fields, nothing will be found.
The non-localized fields text and enum are always included, regardless of the language parameter.
The fields metaKeywords and metaTitle are currently not supported by the full-text search.

If no sort parameter is given, the results are sorted by the relevance of the product to the search text passed.

When the fuzzy query parameter is set to true the search endpoint will also return ProductProjections that contain slight differences to the search text.
For example, when someone searches for ‘whisky’, the fuzzy search would also return products labelled with ‘whiskey’.

To optimize the discovery of related terms, the level of fuzziness applied on the text is adapted dynamically by the platform depending on the length of the text to analyze.
The fuzziness level is quantified in terms of the Damerau-Levenshtein distance, this distance being the number of operations needed to transform one word into another.

The fuzzy level can be optionally set with the parameter fuzzyLevel, otherwise the platform selects level based on the length of the searched text:

Filters

Search results can optionally be filtered and these filters can be applied for different scopes. These scopes are represented by different query parameters:

Passing multiple filter parameters in one search request will combine them with an AND operator.
The following example search request filters products of a certain category AND of the color “black”:

GET /{projectKey}/product-projections/search?filter=categories.id:"{id}"&filter=variants.attributes.color:"black"

Specifying multiple values in one filter parameter, separated by a comma, will return products in which at least one of the specified values matches (OR-operator).
The example search request below filters products of the color “black” OR “grey”:

GET /{projectKey}/product-projections/search?filter=variants.attributes.color:"black","grey"

Not all filters can be applied in all scopes, as is described below.

Filter by category

Keep only the products that belong to the specified Category:

categories.id:"{id}"

Keep products which are not assigned to any Category:

categories:missing

Keep products which are assigned to at least one Category:

categories:exists

Category filters can be applied in the filter, filter.query and filter.facets scopes.

Filter by category subtrees

Keep only the products that belong to the specified Category or any of its descendant categories:

categories.id: subtree("{id}")

To include products that belong to different branches of the category tree other subtree functions can be added to the filter for which each specifies the Category of the other branch in the category tree:

categories.id: subtree("{id}"), subtree("{id2}")

The subtree function can also be used in combination with several regular id filters:

categories.id: subtree("{id1}"), "{id2}", "{id3}"

Note, that specifying “{id2}” and “{id3}” is not necessary in case those categories are children of “{id1}”.

The subtree function can also be applied in filter.facets.

Filter by price

Keep only the products which Price match the specified value or range (lower and upper bound included, use * to ignore either the lower or the upper bound).

variants.price.centAmount:{amount}
variants.price.centAmount:range (* to {to})
variants.price.centAmount:range ({from} to {to}), ({from} to {to}), ...

Keep only products that contain a ProductVariant without any price set:

variants.prices:missing

Keep only products that contain a ProductVariant with at least one price set:

variants.prices:exists

Price filters can be applied in the filter, filter.query and filter.facets scopes.

Please note, that only the first price would be used for filtering if a product variant has several prices. If you would like to use some other product variant price (selected based on the price scope) then you need to use scoped price filter described in the next section.

Filter by scoped price

Scoped price is a price of a product variant selected based on the price scope like currency, country, channel, etc. In contrast to price selection logic normally applied for carts and during search, scoped price filtering does not have any scope fallback behaviour and does not take the price validity date into consideration. This means that if a product variant, for instance, has 2 prices defined:

  1. country: US, value: 10 USD
  2. country: US, customerGroup: B2B, value: 8 USD

And following price selector is used for filtering:

Then no product will be returned: price will not be found and filtered because normal price fallback logic does not apply for scoped prices.

Price selector is used to specify which price should be filtered, faceted and sorted. If price selector is not provided and scoped price filter is used in the search request, then search will yield no results.

Scoped price filters can be applied as filter, filter.query and filter.facets parameters.

Filter by scopedPrice value

Filters, sorts and facets products by the original product variant price value. Here is an example:

variants.scopedPrice.value.centAmount:range (* to 1200)

Filter by scopedPrice currentValue

Filters, sorts and facets products by either the original product variant price value or a discount price value, if it’s available. Here is an example:

variants.scopedPrice.currentValue.centAmount:1000

Filter by scopedPrice discounted value

Filters, sorts and facets products by a discount price value if it’s available. Here is an example:

variants.scopedPrice.discounted.value.centAmount:range (* to 1200)

Filter by scopedPriceDiscounted

You can filter, facet and sort by the Boolean property variants.scopedPriceDiscounted that would be true if a ProductDiscount had been applied on the price.

Filter by SKU

Keep only the product which matches the specified SKU:

variants.sku:{sku}

Keep only products that have a ProductVariant with no sku set:

variants.sku:missing

Keep only products that have a ProductVariant with a sku set:

variants.sku:exists

SKU filters can be applied in the filter as well as in the filter.query scope.

Filter by Product Key

Keep only the product which matches the specified Key:

key:{key}

Keep only products with no key set:

key:missing

Keep only products with a key set:

key:exists

Filter by ProductVariant Key

Keep only the product that have a ProductVariant which matches the specified Key:

variants.key:{key}

Keep only products that have a ProductVariant with no key set:

variants.key:missing

Keep only products that have a ProductVariant with a key set:

variants.key:exists

Filter by productType

Keep only the product which matches the specified ProductType.

productType.id:{id}

ProductType filters can be applied in the filter as well as in the filter.query scope.

Filter by taxCategory

Keep only the products which match the specified TaxCategory.

taxCategory.id:{id}

Keep only products that do not belong to any TaxCategory:

taxCategory:missing

Keep only products that have a TaxCategory set:

taxCategory:exists

TaxCategory filters can be applied in the filter as well as in the filter.query scope.

Filter by state

Keep only the products which match the specified State.

state.id:{id}

Keep only products that do not belong to any State:

state:missing

Keep only products that have a State set:

state:exists

State filters can be applied in the filter as well as in the filter.query scope.

Filter by reviewRatingStatistics

Keep only the products which match the specified ReviewRatingStatistics.

reviewRatingStatistics.averageRating:{value}
reviewRatingStatistics.averageRating:range ({from} to {to}), ({from} to {to}), ...
reviewRatingStatistics.highestRating:{value}
reviewRatingStatistics.highestRating:range ({from} to {to}), ({from} to {to}), ...
reviewRatingStatistics.lowestRating:{value}
reviewRatingStatistics.lowestRating:range ({from} to {to}), ({from} to {to}), ...

Keep only products that have an average/highest/lowest rating matching the specified value or range (lower and upper bound included, use * to ignore either the lower or the upper bound).

reviewRatingStatistics.count:{value}
reviewRatingStatistics.count:range ({from} to {to}), ({from} to {to}), ...

Keep only products that have a number of review ratings matching the specified value or range.

ReviewRatingStatistics filters can be applied in the filter as well as in the filter.query scope.

Filter by custom attribute values

Keep only the ProductVariants with the custom attribute matching the specified value or range (lower and upper bound included, use * to ignore either the lower or the upper bound).

variants.attributes.{name}:{value}
variants.attributes.{name}:range ({from} to {to}), ({from} to {to}), ...

To keep the products that do not have a custom attribute set, use:

variants.attributes.{name}:missing

To keep the products that contain a custom attribute, use:

variants.attributes.{name}:exists

If the custom attribute is of EnumType, the filter must be applied on the key of the PlainEnumValue:

variants.attributes.{myEnumName}.key:"{value}"

In case of MoneyType attributes, the filter must be applied on the Money object’s 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}"

In case of ReferenceType attributes, the filter must be applied on the id or the typeId of the Reference:

variants.attributes.{myReferenceAttribute}.id:"{id}"
variants.attributes.{myReferenceAttribute}.typeId:"{typeId}"

In case of SetType 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).

GET /{projectKey}/product-projections/search?filter=variants.attributes.colors:"green"&filter=variants.attributes.colors:"black","grey"

Custom attribute filters can applied in the filter, filter.query and filter.facets scopes.

Filter by product variant availability

Keep only the ProductVariants with the availability matching the specified value or range (lower and upper bound included, use * to ignore either the lower or the upper bound).

variants.availability.isOnStock:true
variants.availability.availableQuantity:range (1 to *)

For a specific supply Channel:

variants.availability.channels.<channel-id>.isOnStock:true
variants.availability.channels.<channel-id>.availableQuantity:range (1 to *)

For variants with ProductVariantAvailability isOnStock equal to true in at least one of the specified Channels:

variants.availability.isOnStockInChannels:"channel-id-1","channel-id-2","channel-id-3"

Filter by product searchKeywords

searchKeywords.{language}.text:{value}

Facets

Facets calculate statistical counts to aid in faceted navigation.
If one or more valid FacetExpressions are specified in the Product Projection Search the PagedQueryResult contains a facets object additionally to the ProductProjections. For each FacetExpression specified in the search request the facet object contains a FacetResult.
By default FacetResults provide a count of ProductVariants. An additional count of Products can be requested via the extension counting products.

Facet calculation is requested by providing FacetExpressions via the facet query parameter.

GET /{projectKey}/product-projections/search?facet=variants.attributes.colors&facet=variants.attributes.size:"m"

This API endpoint supports three types of facets: TermFacets, RangeFacets, and FilteredFacets
Below each type is explained in more detail.

FacetExpression

FacetResult

A PagedQueryResult contains a facets object with all FacetResults.
A FacetResult can be accessed by its alias if one was given in the FacetExpression or by its attributePath otherwise.

Consider for example the following two facets:

Their FacetResults would be included in the PagedQueryResult like seen here:

{
  "offset": 0,
  "count": 0,
  "results": [],
  "facets": {
    "red-things": {
      "type": "filter",
      "count": 0
    },
    "variants.attributes.size": {
      "type": "filter",
      "count": 0
    }
  }
}

TermFacet

TermFacetExpression

To retrieve facet counts for all occurring values of a ProductVariant field the following notations can be applied:

TermFacetResult

The term type facets provide the counts for each of the different values the query parameter happens to have. This is useful for, e.g., obtaining all possible values of a product attribute to provide filters for those values on the frontend.

FacetTerm

Note: FacetTerms in TermFacetResults are returned in descending order of their count by default.
If the TermFacetExpression specifies to count Products via the counting products extention then FacetTerms are instead returned in descending order of their productCount.

RangeFacet

RangeFacetExpression

To aggregate facet counts across ranges of values, the range qualifier can be applied analogous to the filter parameters. 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, in this case if one matching element is contained in the set, all elements will be used in the aggregation.

Examples:

RangeFacetResult

The range facet type counts the ProductVariants for which the query value is a range specified in the RangeFacetExpression. RangeFacets are typically used to determine the minimum and maximum value for e.g. product prices to filter products by price with a range slider.

Range

Range facets provide statistical data over values for date, time, datetime, number and money type fields in a RangeFacetResult.

FilteredFacet

FilteredFacetExpression

To get facet counts just 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 filter parameters.
Examples:

FilteredFacetResult

This facet type provides counts for specific values of ProductVariant fields.

FacetExpression Extensions

Alias

All facets can optionally be given an alias which will 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:

Counting Products

All facets can optionally count Products additionally to ProductVariants.
Appending counting products to a FacetExpression enables the counting of Products.
The FacetResult will then contain an additional field productCount that shows the number of Products.

Examples:

Sorting

By default, search results are sorted descending by their relevancy with respect to the provided text (i.e. their “score”). An alternative sorting can be specified via the sort query parameter which has the structure {field} {direction}. Compound sorting (also known as multisort) is applied when specifying multiple sort query parameters ordered by priority in which each parameter has its own sorting direction.
Direction can be asc (ascending) or desc (descending).

If the sort parameters do not sufficiently define an ordering, the position of some or all products can be nondeterministic, i.e. not predictable and not repeatable across API requests. Common cases include:

To achieve 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, e.g. createdAt or a custom attribute specific for this purpose.

The following are valid standard fields to sort by:

Example for ascending sort parameter: name.de asc

Sorting by Reviews

To sort products by reviews:

Sorting by values found on ProductVariants

If products have several ProductVariants each variant can have a different value on the sorted attribute.
For those products it needs to be specified whether the minimum or the maximum value across the variants is used to compare this 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).
If a different sorting behavior is desired the parameters can be set to asc.max or desc.min respectively.

The field names to sort products by values found on ProductVariants start with variants..

Sorting by Sku

To sort by sku:

Sorting by Availability

To sort products by availability:

Example sort parameter for availableQuantity:
variants.availability.availableQuantity desc.min

Sorting by Attributes

Besides sorting by the standard fields listed before, products can also be sorted by their specific custom attributes. Please note that the search endpoint only supports those custom attributes that have been declared as searchable in the ProductType.
To make a custom attribute searchable the isSearchable flag must be set to true in the AttributeDefinition.

The following are valid attribute fields to sort by:

Example sort parameter for lenum attribute color:
variants.attributes.color.label.en asc.max

Reference Expansion

Product projection search supports reference expansion. At the moment only references to the following resources can be expanded:

comments powered by Disqus