--- title: Product grouping slug: l78i2152 canonical_url: https://docs.coveo.com/en/l78i2152/ collection: coveo-for-commerce source_format: adoc --- # Product grouping In an ecommerce interface, you'll often want to group similar products together. For example, if a certain type of bicycle helmet comes in various colors, you could display those variations as smaller images below the main product image. ![Product grouping sample image](https://docs.coveo.com/en/assets/images/coveo-for-commerce/images/l78i2152_images/commerce-product-grouping-sample.png) The mechanism required for product grouping to apply is [filtering](https://docs.coveo.com/en/1440/) (that is, the [`filterField`](https://docs.coveo.com/en/1466#filterfield-string) and [`filterFieldRange`](https://docs.coveo.com/en/1466#filterfieldrange-unsigned-integer) [query](https://docs.coveo.com/en/231/) parameters). In addition, ranking within the group of products is controlled by [query ranking expressions (QREs)](https://docs.coveo.com/en/2777/) and can dramatically improve the user experience, but product grouping will work without it. ## Grouping field configuration Grouping is done between products that share the same value for a specific [field](https://docs.coveo.com/en/200/). > **Leading practice** > > Use `ec_item_group_id` as a grouping [field](https://docs.coveo.com/en/200/) with every product and populate it with a value. > When a product isn't part of a group, the `ec_item_group_id` [field](https://docs.coveo.com/en/200/) must be populated with the product's unique identifier (for example, `ec_product_id`). > This is required for [Coveo Machine Learning (Coveo ML)](https://docs.coveo.com/en/188/) [models](https://docs.coveo.com/en/1012/) to recommend product groups. Suppose that in your [catalog data](https://docs.coveo.com/en/obcf0333/), whenever a product comes in different colors, all color variations of that product share the same unique "root" substring in their product IDs. In such a scenario, you could [index](https://docs.coveo.com/en/204/) these root IDs into your grouping field for each product: [%header,cols="1,1,1"] |=== |Product description |Product identifier value |Desired `ec_item_group_id` value |Bicycle helmet model 001 (gray) |bhelmet001-gray |bhelmet001 |Bicycle helmet model 001 (red) |bhelmet001-red |bhelmet001 |Bicycle helmet model 001 (blue) |bhelmet001-blue |bhelmet001 |Bicycle helmet model 002 (gray) |bhelmet002-gray |bhelmet002 |Bicycle helmet model 002 (red) |bhelmet002-red |bhelmet002 |Bicycle helmet model 002 (blue) |bhelmet002-blue |bhelmet002 |=== You could then use that field to request grouped products (as explained in the [Set up the pipelines](#set-up-the-pipelines) section). Your grouping field must be [configured](https://docs.coveo.com/en/1833#add-a-field) as follows: * Must have the [**Facet** option enabled](https://docs.coveo.com/en/1833#facet-and-multi-value-facet). * Must be of type `String`. * If you want pagination, [facet](https://docs.coveo.com/en/198/) counts, and result counts on search requests to reflect the number of groups rather than individual items, you must enable the [**Use cache for nested queries**](https://docs.coveo.com/en/1833#use-cache-for-nested-queries) field setting. By default, this setting is disabled. This means that [facet](https://docs.coveo.com/en/198/) counts, pagination, and result counts reflect the number of individual [items](https://docs.coveo.com/en/210/), not the number of groups. **Example** You use the `ec_brand` [field](https://docs.coveo.com/en/200/) as a [facet](https://docs.coveo.com/en/198/) in your [search interface](https://docs.coveo.com/en/2741/). A user enters the `Kayak` query in the search box. The query returns 10 results, which are kayaks from two different brands: **Barca** and **Aqua Marina**. * 7 results are from **Barca**. 5 of them belong to the same product group, differing only in color, while the remaining 2 come from a different group. * 3 results are from **Aqua Marina**. All 3 are different kayak types, so they're all part of different product groups. With the setting disabled (default): * Facet counts show **Barca (7)** and **Aqua Marina (3)**. * The total result count is 10. * Pagination is based on 10 individual items. With the setting enabled: * Facet counts show **Barca (2)** and **Aqua Marina (3)**. * The total result count is 5 (groups). * Pagination is based on 5 groups. ## Set up the pipelines To request grouped products, you must apply the following [filters](https://docs.coveo.com/en/2736/) in your [**Query Pipelines**](https://platform.cloud.coveo.com/admin/#/orgid/search/pipelines/) ([platform-ca](https://platform-ca.cloud.coveo.com/admin/#/orgid/search/pipelines/) | [platform-eu](https://platform-eu.cloud.coveo.com/admin/#/orgid/search/pipelines/) | [platform-au](https://platform-au.cloud.coveo.com/admin/#/orgid/search/pipelines/)), under the [**Advanced**](https://docs.coveo.com/en/3409/) tab. ### Filter field grouping The [`filterField`](https://docs.coveo.com/en/1466#filterfield-string) query parameter is used to set the field to execute the grouping and is entered in the [Query parameters](https://docs.coveo.com/en/3411/), directly in the [query pipeline](https://docs.coveo.com/en/180/). ![Product Grouping Sample Image](https://docs.coveo.com/en/assets/images/coveo-for-commerce/images/l78i2152_images/product-grouping-filter-field-example.png) ### Filter field range The [`filterFieldRange`](https://docs.coveo.com/en/1466#filterfieldrange-unsigned-integer) parameter controls the number of [items](https://docs.coveo.com/en/pa8f6515/) returned in the group. This is used to define how many results are returned to the group. In the following example, we want to return `10` results. To show all of the items in your group, ensure the number entered is equal to the number of items you have in your biggest product group. ![Product Grouping Sample Image](https://docs.coveo.com/en/assets/images/coveo-for-commerce/images/l78i2152_images/product-grouping-filter-field-range-example.png) ### Products from the same group Products from the same group are retrieved with a [disjunction query expression (`dq`)](https://docs.coveo.com/en/190/) that fetches all the products using the same `ec_item_group_id` as the one returned by the query. To create this expression, in the **Filters** section, click **Add filter rule**. In the **Query Parameter** dropdown menu, always select `dq`, then enter the expression. **Examples** **To be used with [Dynamic facets](https://docs.coveo.com/en/2917/)** ```js (@source=="exampleCatalog")([[@ec_item_group_id]($query) ($originalQuery)($facetsFilter)]) ``` **Where:** * `@source` is the name of your Catalog source. * `[@ec_item_group_id]` is another item with the same `ec_item_group_id`. * [`$originalQuery`](https://docs.coveo.com/en/1449#qpl-objects) is the manual query (q parameter) that initially gets sent before modifications. * [`$facetsFilter`](https://docs.coveo.com/en/1449#qpl-objects) query expression that represents the filters introduced by the selection of dynamic facets. **To be used with Legacy facets** ```js (@source=="exampleCatalog")([[@ec_item_group_id]($query) ($originalAdvancedQuery)]) ``` **Where:** * `@source` is the name of your Catalog source. * `[@ec_item_group_id]` is another item with the same `ec_item_group_id`. * [`$originalAdvancedQuery`](https://docs.coveo.com/en/1449#qpl-objects) is the advanced query that initially gets sent to the platform before any modifications are made by filters or query parameters. > **Note** > > Only Dynamic facets are used within a [Coveo Headless](https://docs.coveo.com/en/lcdf0493/)-based [search interface](https://docs.coveo.com/en/2741/). ## Product grouping main item Grouped items aren't displayed in any particular order. However, you can improve the experience with [ranking expressions](https://docs.coveo.com/en/3375/). You can boost a color field if that value is present in the query. This will move the appropriate product to the top as the main item. To apply these expressions, select a pipeline from the [**Query Pipelines**](https://platform.cloud.coveo.com/admin/#/orgid/search/pipelines/) ([platform-ca](https://platform-ca.cloud.coveo.com/admin/#/orgid/search/pipelines/) | [platform-eu](https://platform-eu.cloud.coveo.com/admin/#/orgid/search/pipelines/) | [platform-au](https://platform-au.cloud.coveo.com/admin/#/orgid/search/pipelines/)) page. Under the [**Result Ranking**](https://docs.coveo.com/en/3234/) tab, click **Add Rule**, and then select **Ranking expression**. For example, performing a search for "shoes" will return shoes as a result with their grouped items in no particular order. Performing a search for "red shoes" will return any red shoes as the main item, and place all other colors as grouped items underneath. Using the `@color` field would result in: ```js @color=$removeMatchingValues(regex:'AND|OR', values:$removeEmptyValues(values: $splitValues(text:$query, separator:'\s|\W'))) ``` Then, use the slider to increase the boost by **100** ![Product Grouping Query Sample Image](https://docs.coveo.com/en/assets/images/coveo-for-commerce/images/l78i2152_images/prod-grouping-color-query-example.png) In case of a facet selection, this expression can extract the right value from the advanced query and add it to the expression: ```js @color=$valuesOfField(field: '@color', resultSet: $advancedQuery) ``` ![Product Grouping Facet Sample Image](https://docs.coveo.com/en/assets/images/coveo-for-commerce/images/l78i2152_images/prod-grouping-color-facet-example.png) > **Note** > > When using Dynamic facets, you would need to replace `$advancedQuery` with `$facetsFilter` in the preceding expression. Refer to the product grouping template based on your use case, [With JSUI](#with-jsui) or [With Headless](#with-headless). ## Product grouping and Coveo Machine Learning [Coveo ML](https://docs.coveo.com/en/188/) [ART](https://docs.coveo.com/en/1013/) and [PR](https://docs.coveo.com/en/3132/) [models](https://docs.coveo.com/en/1012/) require additional configurations to recommend groups of products. ### Product grouping with an ART model When [creating an ART model](https://docs.coveo.com/en/3397/) that aims at boosting groups of products on a commerce interface, you must activate the [`recommendProductGroup`](https://docs.coveo.com/en/3397#recommendproductgroup-boolean) advanced model parameter. By doing so, this will indicate that the model: * Boost groups of products rather than individual products. * Use [cart](https://docs.coveo.com/en/n39h1594/) and [purchase](https://docs.coveo.com/en/l39m0327/) usage analytics events to boost products. * Automatically assign the required weight to each event. ### Product grouping within a recommendation component Recommended product grouping uses the same principles as product grouping, but with additional modifications to the ML model configuration. The group shown is based on the [product recommendation (PR) strategies](https://docs.coveo.com/en/3382/) set by the user. ![Recommended products grouped by category | Coveo](https://docs.coveo.com/en/assets/images/coveo-for-commerce/images/l78i2152_images/recommended-product-grouping-sample.png) This method should be applied when you're already returning recommendations, but instead of showing individual products, you want to show the product within a group defined by the `ec_item_group_id` field. The base configurations are the same as product grouping, set the [Grouping field configuration](#grouping-field-configuration) and the [Filter field grouping](#filter-field-grouping) query parameters. In addition, you then need to [create a PR model](https://docs.coveo.com/en/3395/) and [associate the PR model to your pipeline](https://docs.coveo.com/en/l2fa0038/). #### Model configuration When [creating a PR model](https://docs.coveo.com/en/3395/) that recommends groups of products, you must activate the [`recommendProductGroup`](https://docs.coveo.com/en/3395#recommendproductgroup-boolean) advanced model parameter. By doing so, this will indicate that the model must return groups of products rather than individual products. #### Model association configuration Once you've [associated the PR model to your query pipeline](https://docs.coveo.com/en/l2fa0038/), you must [edit the PR model association via a JSON configuration](https://docs.coveo.com/en/l2fa0038#reference) to modify the ML model's configuration by adding the following parameter: `"maxRecommendations":__` **Example** Your `ec_item_group_id` group contains 7 products and you want there to be enough products to populate a full carousel of items frequently viewed together. You must request more items to return less results, as the recommendations are folded under those groups. The value greatly depends on the number of products per group. ```js { "id": "", "position": "1", "modelId": "", "modelDisplayName": "", "modelEngine": "ecommerce", "modelStatus": "Updating", "rankingModifier": 1000, "maxRecommendations": 50, "exclusive": true, "customQueryParameters": { "strategy": "frequentlyViewed" }, "useAdvancedConfiguration": true, } ``` By grouping products, you filter down the output of the model at the group level, therefore you must request more recommendations to ensure that your component is populated accordingly. > **Note** > > You can request up to a maximum of `50` recommendations. > Attempting to use a higher value will have no effect. ## Product grouping template In the result templates, use a loop to access the child results. This will show the related items in the same group. When using the `filterField` parameter, the items with the same value for that field appear under `childResults` in the search response. > **Note** > > Whether you use a full search interface with a result list or you implement a product recommendation component, you get a results list in both cases. > The template usage is identical but the behaviors are slightly different. The following example iterates over the child to show the related products. ![Product grouping template example showing grouped product cards | Coveo](https://docs.coveo.com/en/assets/images/coveo-for-commerce/images/l78i2152_images/product-grouping-template-example.png) ### With JSUI Here's the code to produce the previous image: ```js import { Component, ComponentOptions, IComponentBindings, Initialization, } from 'coveo-search-ui'; export interface IRelatedProductsOptions { } export class RelatedProducts extends Component { static ID = 'RelatedProducts'; static options: IRelatedProductsOptions = {}; constructor(public element: HTMLElement, public options: IRelatedProductsOptions, public bindings: IComponentBindings, public result: any) { super(element, RelatedProducts.ID, bindings); this.options = ComponentOptions.initComponentOptions(element, RelatedProducts, options); const relatedProducts = result.childResults || []; if (relatedProducts.length) { // for each related product, create an array of Html
to display it. const imagesHtml = relatedProducts.map(product => `
 
`); // Show the related products in the HTML for this result. element.innerHTML = ` Also available in:
${imagesHtml.join('\n')}
`; } } } Initialization.registerAutoCreateComponent(RelatedProducts); ``` > **Note** > > This is a [custom component](https://docs.coveo.com/en/297/), meant to be used as a `
` element in the result template for Products. ### With Headless When using the [Coveo Headless library](https://docs.coveo.com/en/headless/latest/), you'll need to create a special-purpose [result template](https://docs.coveo.com/en/headless/latest/usage/result-templates/). The following example code shows a basic use case of the `ResultTemplatesManager` and will produce the previous image. You can access products from the same group by using the `childResults` property on the result object. ```javascript import { buildResultTemplatesManager, ResultTemplatesManager, Result, ResultTemplatesHelpers } from "@coveo/headless"; import { headlessEngine } from "../Engine"; // ... export default class ResultList { // ... private headlessResultTemplatesManager: ResultTemplatesManager; // ... constructor(props: any) { // ... this.headlessResultTemplatesManager = buildResultTemplatesManager(headlessEngine); this.headlessResultTemplatesManager.registerTemplates( { conditions: [], content: (result: Result) => (
  • {result.title}

    {result.excerpt}

    Also available in:
    { // Use the `childResults` property to get the related products, which we map to their HTML representation in order to display them. result.childResults.map(product => (
     
    ) ) }
  • ) }, ); // ... } // ... render() { return (
      {this.state.results.map((result: Result) => { const template = this.headlessResultTemplatesManager.selectTemplate(result); return template(result); })}
    ); } } ``` ## Limitations Grouped products are treated as regular items by the Commerce and Search APIs. For example, if 5 items share the same `ec_item_group_id` value, the APIs will return 5 items, even though only 1 appears as a product card in the results list. This behavior can impact query performance because the per-page limit applies to the number of products shown in the results list, not the total items returned. For example, if the per-page limit is 20 and each product includes 5 grouped items, the API will return 100 items, even though only 20 are displayed in the results list. For optimal performance, Coveo recommends keeping the total number of items (parents and children combined) returned by a query to 100 or fewer. ## Debugging product grouping You may want to ensure that the product grouping you've set up is working as intended. Before proceeding with the following steps, you'll first need to complete the preceding [Grouping field configuration](#grouping-field-configuration) and [Set up the pipelines](#set-up-the-pipelines) sections. . In your [Coveo organization](https://docs.coveo.com/en/185/), open the [**Content Browser**](https://platform.cloud.coveo.com/admin/#/orgid/content/browser/) ([platform-ca](https://platform-ca.cloud.coveo.com/admin/#/orgid/content/browser/) | [platform-eu](https://platform-eu.cloud.coveo.com/admin/#/orgid/content/browser/) | [platform-au](https://platform-au.cloud.coveo.com/admin/#/orgid/content/browser/)). . Open your web browser's developer tools. > **Note** > > The examples in this article use the **Google Chrome** developer tools. > For browser-specific information, see: > > * [Google Chrome](https://developers.google.com/web/tools/chrome-devtools/open) > > * [Mozilla Firefox](https://developer.mozilla.org/en-US/docs/Tools) > > * [Safari](https://support.apple.com/en-ca/guide/safari/sfri20948/mac) . Select the **Network** tab. . Back in the **Content Browser**, in the **Source** facet, select the source you're attempting to validate. ![Dev tools product grouping results](https://docs.coveo.com/en/assets/images/coveo-for-commerce/images/l78i2152_images/comm-prd-grp-debug-content-browser-example.png) . In the **Pipeline** dropdown menu, select the pipeline where you [set up your filter fields for grouping](https://docs.coveo.com/en/l78i2152#filter-field-grouping). . Back in your browser's developer tools, under the **Name** column, select the latest request to the Search API. The request path should contain `v2`. . Select the **Preview** tab. You should now see the query response body. . In the query response body, you should see an expandable `results` property. You can expand it to see information about the search results for this query. . Expand one of the results. (for example, the `0` index). . Expand its child properties, also known as `childResults`. This should hold a list of results that belong to the same group as the main result. ![Dev tools product grouping results](https://docs.coveo.com/en/assets/images/coveo-for-commerce/images/l78i2152_images/comm-prd-grp-debug-example.png) You can also test whether the keywords give priority to the right item. These adjustments to the ranking expressions can be done to the [Product grouping main item](#product-grouping-main-item). **Example** If you search for "red" in the **Content Browser**, and the "white" product is returned as the main result, you might want to boost the `color` field so that it matches the search box keyword, since it should be given a higher priority compared to its siblings.