Implement facets using groupBy operations

This is for:

Developer

A search interface often includes facets that allow the user to narrow down a query to a specific subset of result items by selecting one or more dynamic filter values. This article provides guidelines for implementing facets on your own, assuming that you can’t use the Coveo JavaScript Search Framework or the Atomic or Headless libraries in your custom search integration with the Coveo Platform.

Notes
  • We recommend that you use facet requests instead of groupBy operations to implement facets.

  • As a reference, you may want to look at the source code of the Facet component to see how it’s implemented within the Coveo JavaScript Search Framework.

Standard facet actions

The following table lists some actions that you may want to make available in a facet implementation.

Each entry indicates the specific Coveo Usage Analytics (Coveo UA) event category, actionCause or eventValue, eventType, and customData that must be logged when the action is performed. Each entry also includes a link to the corresponding implementation guidelines.

Facet action UA event actionCause eventType eventValue customData Implementation guidelines

Select a value

Search

facetSelect / documentField

N/A

N/A

facetField, facetId, facetTitle, facetValue

Toggle facet values

Un-select a value

Search

facetDeselect / documentField / breadcrumbFacet

N/A

N/A

facetField, facetId, facetTitle, facetValue

Toggle facet values

Exclude a value

Search

facetExclude

N/A

N/A

facetField, facetId, facetTitle, facetValue

Toggle facet values

Un-exclude a value

Search

facetUnexclude / breadcrumbFacet

N/A

N/A

facetField, facetId, facetTitle, facetValue

Toggle facet values

Reset selected or excluded values

Search

facetClearAll / breadcrumbResetAll

N/A

N/A

facetField, facetId, facetTitle

Toggle facet values

Request more values

N/A

N/A

N/A

N/A

N/A

Request additional facet values

Sort values

Custom

N/A

facet

facetUpdateSort

criteria, facetField, facetId, facetTitle

Sort facet values

Search for a value

Custom

N/A

facet

facetSearch

facetField, facetId, facetTitle

Perform facet search

Note

It’s important to use the proper eventType, actionCause, eventValue, and customData when logging a UA event for a specific type of action in a search interface. Otherwise:

Request and render facets

Conceptually, a facet relies on an existing field (for example, @bookauthor) to render a sorted list of facet values (for example, Poe, Edgar Allan, Clarke, Arthur C., Gibbon, Edward), each of which is accompanied by an estimated number of occurrences in the current query result set.

You can request the necessary data to render one or more facets through the groupBy search request parameter. Each Group By operation that you include in a query populates a distinct object in the groupByResults array of that query response body.

Most of the time, when a query is sent from a faceted search interface, you’ll want to request and render or update facets. To do so:

  1. In the query, populate the groupBy search request parameter with a distinct Group By operation for each facet to render.

    Note

    Only the field Group By parameter is required. By default, when a Group By operation merely contains the field parameter, this operation requests up to ten facet values sorted by descending score order, or alphabetically in the case of ranged facets. You can use the maximumNumberOfValues and sortCriteria Group By parameters to override these settings (see Request additional facet values and Sort facet values).

    If the specified field references a field that doesn’t exist in the index, or whose facet option is set to false, the corresponding groupByResults object in the query response body will contain an empty values array.

    Likewise, the values array will be empty if no value matching the query is found for the referenced field.

    In any case, a groupByResults object with an empty values array typically means that the corresponding facet shouldn’t be rendered in the search interface.

  2. When the query is executed and the Search API returns, for each facet to render in the search interface:

    1. In the query response body, retrieve the groupByResults object whose field value matches the field on which the facet relies.

      Notes
      • In a groupByResults object, the field and globalComputedFieldResults properties, as well as all values object properties, such as lookupValue and numberOfResults, are duplicated in PascalCase for legacy reasons. For example, each item in the values array of a groupByResults item has both the numberOfResults and NumberOfResults properties.

        We recommend that you always use the camelCase version (for example, numberOfResults), and the examples in this article only include camelCase properties.

      • A Group By operation merely retrieves an array of groupByResults at query time. It has no effect whatsoever on the results array of the query response body, which contains the actual query result items. If you want to group query result items based on the value of a specific field, populate the filterField search request parameter as necessary before executing the query (see Handle folded results).

    2. Use the lookupvalue (or value) and numberOfResults attributes of each object in the values array of the groupByResults object retrieved in step 2.a to render or update the facet.

      Note

      For a given object in the values array, the lookupValue and value attributes will contain the same data, unless the object is a range value.

Example of requesting and rendering facets

Executing a query which includes Group By operations to retrieve three facets (Book Author, Book Genre, and Book Format):

POST https://myorganizationid9sd8df7s.org.coveo.com/rest/search/v2 HTTP/1.1

Accept: application/json
Content-Type: application/json
Authorization: Bearer **********-****-****-****-************

Payload (excerpt)

{
  ...
  "groupBy": [
    ...
    {
      "field": "@bookauthor",
      "maximumNumberOfValues": 3
    },
    {
      "field": "@bookgenre"
    }
    {
      "field": "@bookformat"
    },
    ...
  ],
  "q": "fall",
  ...
}

200 OK response body (excerpt)

{
  ...
  "groupByResults": [
    ...
    {
      "field": "bookauthor",
      "globalComputedFieldResults": [],
      "values": [
        {
          "computedFieldResults": [],
          "lookupValue": "Poe, Edgar Allan",
          "numberOfResults": 6,
          "score": 631739,
          "value": "Poe, Edgar Allan",
          "valueType": "Standard"
        }
        {
          "computedFieldResults": [],
          "lookupValue": "Clarke, Arthur C.",
          "numberOfResults": 11,
          "score": 399555,
          "value": "Clarke, Arthur C.",
          "valueType": "Standard"
        },
        {
          "computedFieldResults": [],
          "lookupValue": "Gibbon, Edward",
          "numberOfResults": 29,
          "score": 380154,
          "value": "Gibbon, Edward",
          "valueType": "Standard"
        }
      ]
    },
    {
      "globalComputedFieldResults": [],
      "field": "bookgenre",
      "values": [
        {
          "computedFieldResults": [],
          "lookupValue": "Fantasy",
          "numberOfResults": 78,
          "score": 48927523,
          "value": "Fantasy",
          "valueType": "Standard"
        },
        {
          "computedFieldResults": [],
          "lookupValue": "Mystery",
          "numberOfResults": 89,
          "score": 8610185,
          "value": "Mystery",
          "valueType": "Standard"
        },
        {
          "computedFieldResults": [],
          "lookupValue": "Science-Fiction",
          "numberOfResult": 22,
          "score": 7478698,
          "value": "Science-Fiction",
          "valueType": "Standard"
        },
        ...
      ]
    },
    {
      "globalComputedFieldResults": [],
      "field": "bookformat",
      "values": [
        {
          "computedFieldResults": [],
          "score": 1752838,
          "value": "Paperback",
          "numberOfResults": 322,
          "valueType": "Standard"
        },
        {
          "computedFieldResults": [],
          "score": 820350,
          "value": "Ebook",
          "numberOfResults": 261,
          "valueType": "Standard"
        },
        {
          "computedFieldResults": [],
          "score": 70372,
          "value": "Hardcover",
          "numberOfResults": 178,
          "valueType": "Standard"
        }
      ]
    },
    ...
  ],
  ...
}

Request ranged facets

Note

As a reference, you may want to look at the source code of the FacetRange component to see how it’s implemented in the Coveo JavaScript Search Framework.

When a facet is based on a numeric or date field, its values must refer to ranges.

The easiest way to request ranged facet values is to set the generateAutomaticRanges Group By parameter to true when requesting the ranged facet. This will cause the index to automatically compute ranges based on the minimum and maximum values of the field referenced by the Group By operation.

This simple approach doesn’t work with facets based on dynamic fields created by Query functions. It also doesn’t allow retrieved ranged facet values to have custom strings as their lookupValue. One work-around is to request an explicit set of ranges using the rangeValues Group By parameter. Each object in the rangeValues array must have a start (for example, 0) and an end (for example, 100). Optionally, it may also have a label (for example, Short), which will be used as a custom lookupValue. By default, the end value isn’t included in the requested range, but you can set endInclusive to true to modify this behavior.

Note

By default, ranged facet values are sorted alphabetically based on their lookupValue (rather than by descending score like regular facet values). This may result in awkward ordering.

Example

In the values array of a groupByResults object corresponding to a ranged facet, a value whose lookupValue is 100..110 appears before one whose lookupValue is 20..30.

One way to work around this issue when using the rangeValues Group By parameter is to set the sortCriteria Group By parameter to nosort. This will return ranged facet values in the order in which they were originally specified.

Example of requesting ranged facets

Executing a query which includes Group By operations to retrieve two ranged facets (Book Price and Book Length):

POST https://myorganizationid9sd8df7s.org.coveo.com/rest/search/v2 HTTP/1.1

Accept: application/json
Content-Type: application/json
Authorization: Bearer **********-****-****-****-************

Payload (excerpt)

{
  ...
  "groupBy": [
    ...
    {
      "field": "@bookprice",
      "generateAutomaticRanges": true
    },
    {
      "field": "@booklength",
      "rangeValues": [
        {
          "start": 0,
          "end": 100,
          "label": "Short"
        },
        {
          "start": 100,
          "end": 500,
          "label": "Medium"
        },
        {
          "start": 500,
          "end": 10000,
          "label": "Long"
        }
      ],
      "sortCriteria": "nosort"
    },
    ...
  ],
  "q": "fall",
  ...
}

200 OK response body (excerpt)

{
  ...
  "groupByResults": [
    ...
    {
      "globalComputedFieldResults": [],
      "field": "bookprice",
      "values": [
        {
          "computedFieldResults": [],
          "lookupValue": "100..110",
          "numberOfResults": 20,
          "score": 0,
          "value": "100.000000..110.000000",
          "valueType": "Standard"
        }
        {
          "computedFieldResults": [],
          "lookupValue": "10..20",
          "numberOfResults": 424,
          "score": 0,
          "value": "10.000000..20.000000",
          "valueType": "Standard"
        },
        {
          "computedFieldResults": [],
          "lookupValue": "20..30",
          "numberOfResults": 217,
          "score": 0,
          "value": "20.000000..30.000000",
          "valueType": "Standard"
        },
        ...
      ]
    },
    {
      "globalComputedFieldResults": [],
      "field": "booklength",
      "values": [
        {
          "computedFieldResults": [],
          "lookupValue": "Short",
          "numberOfResults": 138,
          "score": 161,
          "value": "0..99",
          "valueType": "Standard"
        },
        {
          "computedFieldResults": [],
          "lookupValue": "Medium",
          "numberOfResults": 970,
          "score": 432,
          "value": "100..499",
          "valueType": "Standard"
        },
        {
          "computedFieldResults": [],
          "lookupValue": "Long",
          "numberOfResults": 553,
          "score": 168,
          "value": "500..9999",
          "valueType": "Standard"
        }
      ]
    },
    ...
  ],
  ...
}

Request slider facets

Note

As a reference, you may want to look at the source code of the FacetSlider component to see how it’s implemented in the Coveo JavaScript Search Framework.

You may want to allow users to specify their own custom range of values through a slider facet rather than selecting or excluding predefined values from a ranged facet.

One way to request the necessary data to render a slider facet is to perform a Group By operation in which maximumNumberOfValues is set to 1, generateAutomaticRanges is set to true, and advancedQueryOverride is set to an all-inclusive query syntax expression (for example, @uri). This forces the index to compute a single range value starting with the lowest and ending with the highest value of the field referenced by the Group By operation.

When the user interacts with the slider facet to specify a custom range, you can then alter the current advanced query expression (aq) accordingly (see Toggle facet values).

Example of requesting slider facets

Executing a query which includes a Group By operation to retrieve the Book Price slider facet:

POST https://myorganizationid9sd8df7s.org.coveo.com/rest/search/v2 HTTP/1.1

Accept: application/json
Content-Type: application/json
Authorization: Bearer **********-****-****-****-************

Payload (excerpt)

{
  ...
  "groupBy": [
    ...
    {
      "field": "@bookprice",
      "generateAutomaticRanges": true,
      "maximumNumberOfValues": 1,
      "advancedQueryOverride": "@uri"
    },
    ...
  ],
  "q": "fall",
  ...
}

200 OK response body (excerpt)

{
  ...
  "groupByResults": [
    ...
    {
      "globalComputedFieldResults": [],
      "field": "bookprice",
      "values": [
        {
          "computedFieldResults": [],
          "lookupValue": "5..110",
          "numberOfResults": 1891,
          "score": 0,
          "value": "5.000000..110.000000",
          "valueType": "Standard"
        }
      ]
    },
    ...
  ],
  ...
}

Toggle facet values

In a typical faceted search interface, the user can toggle (that is, select and un-select or exclude and un-exclude) any number of facet values to alter the current advanced query expression (aq). When the user interacts with a facet in such a way:

  1. Generate a query syntax expression that represents the updated facet state.

Example

In a disjunctive facet based on the @bookauthor field, the currently selected values are Poe, Edgar Allan and Clarke, Arthur C. The user interacts with the facet to exclude the Gibbon, Edward value. The updated { } state could be represented by a query syntax expression such as (@bookauthor==("Poe, Edgar Allan", "Clarke, Arthur C.")) (NOT @author==("Gibbon, Edward")).

  1. Prepare a new query. Ensure that the aq search request parameter includes the expression generated in step 1.

  2. Call the Search API to execute the query prepared in step 2. When the Search API returns:

    1. Call the UA Write API to log the corresponding search event. In the request body:

      • Set the actionCause property to:

        • facetSelect if the user has selected a single facet value.

        • facetUnselect if the user has un-selected a single facet value.

        • facetExclude if the user has excluded a single facet value.

        • facetUnexclude if the user has un-excluded a single facet value.

        • facetClearAll if the user has cleared all previously selected or excluded values.

        • facetRangeSlider or facetRangeGraph (whichever is more appropriate) if the user has selected a custom range of numeric or date values through a slider facet.

      • Include the following key-value pairs in the customData property:

        • "facetField": <facetField>

        • "facetId": <facetId>

        • "facetTitle": <facetTitle> (don’t include if actionCause is facetRangeSlider or facetRangeGraph)

        • "facetValue": <facetValue> (don’t include if actionCause is facetClearAll, facetRangeSlider, or facetRangeGraph)

        • "facetRangeStart": <facetRangeStart> (only include if actionCause is facetRangeSlider or facetRangeGraph)

        • "facetRangeEnd": <facetRangeEnd> (only include if actionCause is facetRangeSlider or facetRangeGraph)

        where:

        • <facetField> (string) is the @-prefixed name of the field this facet is based on (for example, @booklength).

        • <facetId> (string) is the unique identifier of the facet in which one or more values were toggled. Typically, this is the name of the field this facet is based on (for example, @booklength).

        • <facetTitle> (string) is the display name of the facet in which one or more values were toggled. Typically, this is the human-readable name of the field this facet is based on (for example, Book Length).

        • <facetValue> (string) is the name of the toggled facet value (for example, Short).

        • <facetRangeStart> (string) is the first value of the user-specified range.

        • <facetRangeEnd> (string) is the last value of the user-specified range.

        Note

        You have to use the appropriate actionCause and customData values if you’re interacting with a facet indirectly in your own custom component.

      • Set other required or optional properties as needed, such as language, originLevel1, or originLevel2.

    2. Update all of the facets in the search interface.

    3. Update the result list.

Interact with a facet indirectly

The Coveo JavaScript Search Framework includes components that allow users to interact with facets indirectly, such as the Breadcrumb and FieldValue components.

If you implement your own components, be sure to use the appropriate actionCause and customData values when logging a search event after the user has indirectly toggled one or more facet values:

Action actionCause customData

Select or un-select a single facet value by interacting with a query result

documentField

facetId, facetTitle, facetValue

Un-select or un-exclude a single facet value by interacting with breadcrumbs

breadcrumbFacet

facetId, facetTitle, facetValue

Clear all selected or excluded facet values by interacting with breadcrumbs

breadcrumbResetAll

N/A

Examples of toggling facet values

  1. Executing a query after the user has excluded the Medium value from the Book Length facet by interacting with the facet directly:

    POST https://myorganizationid9sd8df7s.org.coveo.com/rest/search/v2 HTTP/1.1
    
    Accept: application/json
    Content-Type: application/json
    Authorization: Bearer **********-****-****-****-************

    Payload (excerpt)

    {
      ...
      "aq": "(NOT @booklength==(100..499))",
      "locale": "en-US",
      "groupBy": [
        ...
      ],
      "q": "fall",
      "searchHub": "BookstoreSearch",
      "tab": "All",
      ...
    }

    200 OK response body (excerpt)

    {
      ...
      "duration": 86,
      "groupByResults": [
        ...
      ],
      "results": [
        ...
      ],
      "searchUid": "a1d15d1b-5802-47ac-959f-35a0f2ca72de",
      ...
    }
  2. Executing a query after the user has selected the Hardcover value from the Book Format facet by interacting with a query result:

    POST https://myorganizationid9sd8df7s.org.coveo.com/rest/search/v2 HTTP/1.1
    
    Accept: application/json
    Content-Type: application/json
    Authorization: Bearer **********-****-****-****-************

    Payload (excerpt)

    {
      ...
      "aq": "(NOT @booklength==(100..499)) (@bookformat==(\"Hardcover\"))",
      "locale": "en-US",
      "groupBy": [
        ...
      ],
      "q": "fall",
      "searchHub": "BookstoreSearch",
      "tab": "All",
      ...
    }

    200 OK response body (excerpt)

    {
      ...
      "duration": 75,
      "groupByResults": [
        ...
      ],
      "results": [
        ...
      ],
      "searchUid": "38e17ca1-c362-44a0-90bf-09669d908af3",
      ...
    }
  3. Executing a query after the user has un-excluded the Medium value from the Book Length facet by interacting with the breadcrumbs:

    POST https://myorganizationid9sd8df7s.org.coveo.com/rest/search/v2 HTTP/1.1
    
    Accept: application/json
    Content-Type: application/json
    Authorization: Bearer **********-****-****-****-************

    Payload (excerpt)

    {
      ...
      "aq": "(@bookformat==(\"Hardcover\"))",
      "locale": "en-US",
      "groupBy": [
        ...
      ],
      "q": "fall",
      "searchHub": "BookstoreSearch",
      "tab": "All",
      ...
    }

    200 OK response body (excerpt)

    {
      ...
      "duration": 67,
      "groupByResults": [
        ...
      ],
      "results": [
        ...
      ],
      "searchUid": "7b83db54-ce56-4837-8c76-9f63b39b54a6",
      ...
    }
  4. Logging a batch of search events for the three previous actions:

    POST https://myorganizationid9sd8df7s.analytics.org.coveo.com/rest/ua/v15/analytics/searches?visitor=28s6g49d-f81s-1435-2r5x153dle72 HTTP/1.1
    
    Accept: application/json
    Content-Type: application/json
    Authorization: Bearer **********-****-****-****-************

    Payload (excerpt)

    [
      {
        ...
        "actionCause": "facetExclude",
        "advancedQuery": "(NOT @booklength==(100..499))",
        "customData": {
          ...
          "facetField": "@booklength",
          "facetId": "@booklength",
          "facetTitle": "Book Length",
          "facetValue": "Medium",
          ...
        },
        "language": "en",
        "originLevel1": "BookstoreSearch",
        "originLevel2": "All",
        "queryText": "fall",
        "responseTime": 86,
        "searchQueryUid": "a1d15d1b-5802-47ac-959f-35a0f2ca72de",
        ...
      },
      {
        ...
        "actionCause": "documentField",
        "advancedQuery": "(NOT @booklength==(100..499)) (@bookformat==(\"Hardcover\"))",
        "customData": {
          ...
          "facetField": "@bookformat",
          "facetId": "@bookformat",
          "facetTitle": "Book Format",
          "facetValue": "Hardcover",
          ...
        },
        "language": "en",
        "originLevel1": "BookstoreSearch",
        "originLevel2": "All",
        "queryText": "fall",
        "responseTime": 75,
        "searchQueryUid": "38e17ca1-c362-44a0-90bf-09669d908af3",
        ...
      },
      {
        ...
        "actionCause": "breadcrumbFacet",
        "advancedQuery": "(@bookformat==(\"Hardcover\"))",
        "customData": {
          ...
          "facetField": "@booklength",
          "facetId": "@booklength",
          "facetTitle": "Book Length",
          "facetValue": "Medium",
          ...
        },
        "language": "en",
        "originLevel1": "BookstoreSearch",
        "originLevel2": "All",
        "queryText": "fall",
        "responseTime": 67,
        "searchQueryUid": "7b83db54-ce56-4837-8c76-9f63b39b54a6",
        ...
      }
    ]

Request additional facet values

The maximumNumberOfValues Group By parameter allows you to specify up to how many facet values the index may retrieve to populate the values array of a given groupByResults object (the default value is 10). When completeFacetWithStandardValues is set to true and the cardinality of allowedValues is lower than the maximumNumberOfValues, the index will try to retrieve standard values until the values array contains maximumNumberOfValues.

You may also want to allow the user to autonomously request more values to be displayed in a facet. When the user interacts with a facet in such a way:

  1. Prepare a new query. Ensure that the numberOfResults search request parameter is set to 0. In the groupBy search request parameter, include a Group By operation to update the facet for which the user has requested additional values. In this Group By operation:

    1. Set field to the name of the field that the facet is based on (for example, @bookauthor).

    2. In the allowedValues array, include all of the values currently displayed by the facet (for example, Poe, Edgar Allan, Clarke, Arthur C., Gibbon, Edward).

    3. Set maximumNumberOfValues to a higher value than the one that was specified the last time the facet was requested (for example, 6).

    4. Set completeFacetWithStandardValues to true.

    5. Set all other Group By parameters to the value they were the last time the facet was requested.

    Note

    Although this query isn’t meant to return actual result items, you should include the other search request parameters as usual. This way, it will represent the current state of the search interface, aside from numberOfResults being forcefully set to 0 and groupBy not necessarily containing all of the operations required to update facets. Otherwise, the retrieved groupByResults object may not contain accurate or consistent facet values or occurrence counts.

  2. Call the Search API to execute the query prepared in step 1. When the Search API returns, re-render the updated facet as appropriate.

Note

When the user requests additional facet values to be displayed:

  • No specific UA event needs to be logged.

  • The result list shouldn’t be updated.

Example of requesting additional facet values

Executing a query after the user has requested additional values to be displayed in the Book Author facet:

POST https://myorganizationid9sd8df7s.org.coveo.com/rest/search/v2 HTTP/1.1

Accept: application/json
Content-Type: application/json
Authorization: Bearer **********-****-****-****-************

Payload (excerpt)

{
  ...
  "aq": "(@bookformat==(\"Hardcover\"))",
  "groupBy": [
    {
      "allowedValues": [
        "Poe, Edgar Allan",
        "Clarke, Arthur C.",
        "Gibbon, Edward"
      ],
      "completeFacetsWithStandardValues": true,
      "field": "@bookauthor",
      "maximumNumberOfValues": 6
    }
  ],
  "locale": "en-US",
  "numberOfResults": 0,
  "q": "fall",
  "searchHub": "BookstoreSearch",
  "tab": "All",
  ...
}

200 OK response body (excerpt)

{
  ...
  "groupByResults": [
    {
      "field": "bookauthor",
      "globalComputedFieldResults": [],
      "values": [
        {
          "computedFieldResults": [],
          "lookupValue": "Poe, Edgar Allan",
          "numberOfResults": 1,
          "score": 631739,
          "value": "Poe, Edgar Allan",
          "valueType": "Standard"
        }
        {
          "computedFieldResults": [],
          "lookupValue": "Clarke, Arthur C.",
          "numberOfResults": 3,
          "score": 399555,
          "value": "Clarke, Arthur C.",
          "valueType": "Standard"
        },
        {
          "computedFieldResults": [],
          "lookupValue": "Gibbon, Edward",
          "numberOfResults": 7,
          "score": 380154,
          "value": "Gibbon, Edward",
          "valueType": "Standard"
        },
        {
          "computedFieldResults": [],
          "lookupValue": "Beckett, Samuel",
          "numberOfResults": 1,
          "score": 271647,
          "value": "Beckett, Samuel",
          "valueType": "Standard"
        },
        {
          "computedFieldResults": [],
          "lookupValue": "Delany, Samuel R.",
          "numberOfResults": 2,
          "score": 240684,
          "value": "Delany, Samuel R.",
          "valueType": "Standard"
        },
        {
          "computedFieldResults": [],
          "lookupValue": "Camus, Albert",
          "numberOfResults": 1,
          "score": 165923,
          "value": "Camus, Albert",
          "valueType": "Standard"
        }
      ]
    }
  ],
  "results": [],
  ...
}

Sort facet values

The sortCriteria Group By parameter allows you to specify in what order the index should return the values of a groupByResults object (the default value being Score for regular values, or AlphaAscending for range values).

You may also want to allow the user to autonomously change facet sort criteria. When the user interacts with a facet in such a way:

  1. Prepare a new query. Ensure that the numberOfResults search request parameter is set to 0. In the groupBy search request parameter, include a Group By operation to update the facet for which the user has selected a new sort criteria. In this Group By operation:

    1. Set field to the name of the field that the facet is based on (for example, @bookformat).

    2. Set sortCriteria to the value that corresponds to the user’s selection.

    3. Set all other Group By parameters to the value they were the last time the facet was requested.

    Note

    Although this query isn’t meant to return actual result items, you should include the other search request parameters as usual. This way, it will represent the current state of the search interface, aside from numberOfResults being forcefully set to 0 and groupBy not necessarily containing all of the operations required to update facets. Otherwise, the retrieved groupByResults object may not contain accurate or consistent facet values or occurrence counts.

  2. Call the Search API to execute the query prepared in step 1. When the Search API returns:

    1. Call the UA Write API to log the corresponding custom event. In the request body:

      • Set the eventType property to facet.

      • Set the eventValue property to facetUpdateSort.

      • Include the following key-value pairs in the customData property:

        • "criteria": <criteria>

        • "facetField": <facetField>

        • "facetId": <facetId>

        • "facetTitle": <facetTitle>

        where:

        • <criteria> (string) is the new selected sort criteria (for example, AlphaAscending).

        • <facetField> (string) is the @-prefixed name of the field this facet is based on (for example, @bookformat).

        • <facetId> (string) is the unique identifier of the facet in which the sort criteria was changed. Typically, this is the name of the field this facet is based on (for example, @bookformat).

        • <facetTitle> (string) is the display name of the facet in which the sort criteria was changed. Typically, this is the human-readable name of the field this facet is based on (for example, Book Format).

      • Set other required or optional properties as needed, such as language, originLevel1, or originLevel2.

    2. Re-render the facet as appropriate.

Examples of sorting facet values

  1. Executing a query after the user has selected an alphabetical sort criteria for the Book Format facet:

    POST https://myorganizationid9sd8df7s.org.coveo.com/rest/search/v2 HTTP/1.1
    
    Accept: application/json
    Content-Type: application/json
    Authorization: Bearer **********-****-****-****-************

    Payload (excerpt)

    {
      ...
      "groupBy": [
        {
          "field": "@bookformat",
          "sortCriteria": "AlphaAscending"
        }
      ],
      "locale": "en-US",
      "numberOfResults": 0,
      "q": "fall",
      "searchHub": "BookstoreSearch",
      "tab": "All",
      ...
    }

    200 OK response body (excerpt)

    {
      ...
      "groupByResults": [
        {
          "globalComputedFieldResults": [],
          "field": "bookformat",
          "values": [
            {
              "computedFieldResults": [],
              "score": 0,
              "value": "Ebook",
              "numberOfResults": 261
            },
            {
              "computedFieldResults": [],
              "score": 0,
              "value": "Hardcover",
              "numberOfResults": 178
            },
            {
              "computedFieldResults": [],
              "score": 0,
              "value": "Paperback",
              "numberOfResults": 322
            }
          ]
        }
      ],
      "results": [],
      ...
    }
  2. Logging a custom event for the previous action:

    POST https://myorganizationid9sd8df7s.analytics.org.coveo.com/rest/ua/v15/analytics/custom?visitor=28s6g49d-f81s-1435-2r5x153dle72 HTTP/1.1
    
    Accept: application/json
    Content-Type: application/json
    Authorization: Bearer **********-****-****-****-************

    Payload (excerpt)

    {
      ...
      "customData": {
        ...
        "criteria": "AlphaAscending",
        "facetField": "@bookformat",
        "facetId": "@bookformat",
        "facetTitle": "Book Format",
        ...
      },
      "eventType": "facet",
      "eventValue": "facetUpdateSort",
      "language": "en",
      "originLevel1": "BookstoreSearch",
      "originLevel2": "All",
      ...
    }

Use custom facet sort criteria

If you want to define a custom facet sort criteria based on an aggregate operation performed on the values of a numeric field (that is, average, maximum, minimum, or sum), use the computedFields Group By parameter along with either the ComputedFieldAscending or ComputedFieldDescending sortCriteria value (see Computed fields).

Notes
  • You can include many aggregate operations in the computedFields array of a Group By operation. In the query response body, the computedFieldResults array property of each values element in the corresponding groupByResults object sequentially stores the results of these operations. When using the ComputedFieldAscending or ComputedFieldDescending sort criteria for a given Group By operation, only the results of the first specified aggregate operation are taken into account. If two facet values have the same first computedFieldResults values, the AlphaDescending sort criteria is used as a fallback.

  • The groupByResults object itself has a globalComputedFieldResults property which sequentially stores the results of the aggregate operation applied to all corresponding computedFieldResults (that is, the average of all averages, the sum of all sums, and so on).

Example of using custom facet sort criteria

Executing a query which includes a Group By operation to sort the Book Author facet values based on average book ratings:

POST https://myorganizationid9sd8df7s.org.coveo.com/rest/search/v2 HTTP/1.1

Accept: application/json
Content-Type: application/json
Authorization: Bearer **********-****-****-****-************

Payload (excerpt)

{
  ...
  "groupBy": [
    ...
    {
      "field": "@bookauthor",
      "maximumNumberOfValues": 3,
      "computedFields": [
        {
          "field": "@bookrating",
          "operation": "average"
        }
      ],
      "sortCriteria": "ComputedFieldDescending"
    },
    ...
  ],
  ...
}

200 OK response body (excerpt)

{
  ...
  "groupByResults": [
    ...
    {
      "field": "bookauthor",
      "globalComputedFieldResults": [ 6.7389380531 ],
      "values": [
        {
          "computedFieldResults": [ 8.9146341463 ],
          "lookupValue": "Follett, Ken",
          "numberOfResults": 3,
          "score": 0,
          "value": "Follett, Ken",
          "valueType": "Standard"
        }
        {
          "computedFieldResults": [ 8.307142857 ],
          "lookupValue": "Simmons, Dan",
          "numberOfResults": 3,
          "score": 0,
          "value": "Simmons, Dan",
          "valueType": "Standard"
        },
        {
          "computedFieldResults": [ 8.058823529 ],
          "lookupValue": "Golding, William",
          "numberOfResults": 2,
          "score": 0,
          "value": "Golding, William",
          "valueType": "Standard"
        }
      ]
    },
    ...
  ],
  ...
}

You may want to allow users to look for specific facet values by typing into a dedicated search input within a facet. When a user interacts with a facet in such a way:

  1. On each valid keystroke in the corresponding facet search input:

    1. Retrieve the current value from the facet search input (for example, a).

    2. Prepare a new query. Ensure that the numberOfResults search request parameter is set to 0. In the groupBy search request parameter, include a Group By operation for the facet in which the user is searching. In this Group By operation:

      1. Set field to the name of the field that the searched facet is based on (for example, @bookgenre).

      2. In the allowedValues array, include the original as well as the -prefixed and -suffixed values retrieved in step 1.a (for example, a and *a*).

      3. Set maximumNumberOfValues to the maximum number of matching values to retrieve through facet search (for example, 4). The default value is 10.

      4. Set all other Group By parameters to the value they were the last time the facet was requested.

      Note

      Although this query isn’t meant to return actual result items, you should include the other search request parameters as usual. This way, it will represent the current state of the search interface, aside from numberOfResults being forcefully set to 0 and groupBy not necessarily containing all of the operations required to update facets. Otherwise, the retrieved groupByResults object may not contain accurate or consistent facet values or occurrence counts.

    3. Call the Search API to execute the query prepared in step 1.b. When the Search API returns:

      1. Call the UA Write API to log the corresponding custom event. In the request body:

        • Set the eventType property to facet.

        • Set the eventValue property to facetSearch.

        • Include the following key-value pairs in the customData property:

          • "facetField": <facetField>

          • "facetId": <facetId>

          • "facetTitle": <facetTitle>

          where:

          • <facetField> (string) is the @-prefixed name of the field this facet is based on (for example, @bookgenre).

          • <facetId> (string) is the unique identifier of the facet in which facet search was performed. Typically, this is the name of the field this facet is based on (for example, @bookgenre).

          • <facetTitle> (string) is the display name of the facet in which facet search was performed. Typically, this is the human-readable name of the field this facet is based on (for example, Book Genre).

        • Set other required or optional properties as needed, such as language`, originLevel1, or originLevel2.

      2. Render the retrieved facet values as appropriate (for example, in a dedicated result list below the facet search input).

Note

These guidelines suggest a facet search implementation which is based on that of the Coveo JavaScript Search Framework (that is, wildcard pattern matching as the user types). You may opt to use an entirely different approach for your own implementation (to consume less queries per month (QPM), for example).

  1. Executing a query after the user has typed a in the dedicated search input of the Book Genre facet:

    POST https://myorganizationid9sd8df7s.org.coveo.com/rest/search/v2 HTTP/1.1
    
    Accept: application/json
    Content-Type: application/json
    Authorization: Bearer **********-****-****-****-************

    Payload (excerpt)

    {
      ...
      "groupBy": [
        {
          "allowedValues": [
            "a",
            "*a*"
          ],
          "field": "@bookgenre",
          "maximumNumberOfValues": 4
        }
      ],
      "locale": "en-US",
      "numberOfResults": 0,
      "q": "fall",
      "searchHub": "BookstoreSearch",
      "tab": "All",
      ...
    }

    200 OK response body (excerpt)

    {
      ...
      "groupByResults": [
        {
          "values": [
            {
              "computedFieldResults": [],
              "lookupValue": "Fantasy",
              "numberOfResults": 78,
              "score": 48927523,
              "value": "Fantasy",
              "valueType": "Standard"
            },
            {
              "computedFieldResults": [],
              "lookupValue": "Romance",
              "numberOfResults": 106,
              "score": 1125782,
              "value": "Romance",
              "valueType": "Standard"
            },
            {
              "computedFieldResults": [],
              "lookupValue": "Adventure",
              "numberOfResults": 47,
              "score": 1063015,
              "value": "Adventure",
              "valueType": "Standard"
            },
            {
              "computedFieldResults": [],
              "lookupValue": "Biography",
              "numberOfResults": 94,
              "score": 831924,
              "value": "Biography",
              "valueType": "Standard"
            }
          ]
        }
      ],
      "results": [],
      ...
    }
  2. Logging a custom event for the previous action:

    POST https://myorganizationid9sd8df7s.analytics.org.coveo.com/rest/ua/v15/analytics/custom?visitor=28s6g49d-f81s-1435-2r5x153dle72 HTTP/1.1
    
    Accept: application/json
    Content-Type: application/json
    Authorization: Bearer **********-****-****-****-************

    Payload (excerpt)

    {
      ...
      "customData": {
        ...
        "facetField": "@bookgenre",
        "facetId": "@bookgenre",
        "facetTitle": "Book Genre",
        ...
      },
      "eventType": "facet",
      "eventValue": "facetSearch",
      "language": "en",
      "originLevel1": "BookstoreSearch",
      "originLevel2": "All",
      ...
    }