Implementing Facets

A search interface often allows the end-user to select dynamic filter values to narrow down a query to a specific subset of result items. This topic provides guidelines for properly implementing facets on your own, assuming that you cannot use the Coveo JavaScript Search Framework in your custom search integration with the Coveo Cloud platform.

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

Standard Facet Actions

The following table lists some of the most frequent actions you may want to make available in a facet implementation.

Each entry indicates the specific Coveo Cloud usage analytics event type, actionCause/eventValue, eventType, and customData that must be logged when the action is performed (see Logging Usage Analytics Events), as well as a link to the corresponding implementation guidelines.

Facet action Usage analytics event actionCause eventType eventValue customData Implementation guidelines
Selecting a value Search facetSelect / documentField N/A N/A facetField, facetId, facetTitle, facetValue Toggling Facet Values
Un-selecting a value Search facetDeselect / documentField / breadcrumbFacet N/A N/A facetField, facetId, facetTitle, facetValue Toggling Facet Values
Excluding a value Search facetExclude N/A N/A facetField, facetId, facetTitle, facetValue Toggling Facet Values
Un-excluding a value Search facetUnexclude / breadcrumbFacet N/A N/A facetField, facetId, facetTitle, facetValue Toggling Facet Values
Resetting selected/excluded values Search facetClearAll / breadcrumbResetAll N/A N/A facetField, facetId, facetTitle Toggling Facet Values
Requesting more values N/A N/A N/A N/A N/A Requesting Additional Facet Values
Sorting values Custom N/A facet facetUpdateSort criteria, facetField, facetId, facetTitle Sorting Facet Values
Searching for a value Custom N/A facet facetSearch facetField, facetId, facetTitle Performing Facet Search
  • Using the proper eventType, actionCause/eventValue, and customData when logging a usage analytics event for a specific type of action in a search interface is important. Otherwise:

Requesting and Rendering Facets

Conceptually, a facet relies on an existing field (e.g., @bookauthor) to render a sorted list of facet values (e.g., 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 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 will want to request and render/update facets. In order to do so:

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

    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; see Requesting Ranged Facets). You can use the maximumNumberOfValues and sortCriteria Group By parameters to override those settings (see Requesting Additional Facet Values and Sorting Facet Values).

    If the specified field references a field that does not 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 should not 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.

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

      For a given object in the values array, the lookupValue and value attributes will contain identical data except when the object is a range value (see Requesting Ranged Facets).

  • In a groupByResults object, the field and globalComputedFieldResults properties, as well as all values object properties (lookupValue, numberOfResults, etc.) are duplicated in PascalCase for legacy reasons (e.g., each item in the values array of a groupByResults item has both the numberOfResults and NumberOfResults properties).

    Always use the camelCase version (e.g., numberOfResults).

    For the sake of brevity, examples in this topic 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 Implementing a Result List - Handling Folded Results).

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

POST https://plaform.cloud.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"
        }
      ]
    },
    ...
  ],
  ...
}

Requesting Ranged Facets

As a reference, you may want to look at the source code of the FacetRange component to see how it is 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 does not work with facets based on dynamic fields created by query functions (see Query Functions). It also does not allow retrieved ranged facet values to have custom strings as their lookupValue. To achieve such goals, the alternative is to request an explicit set of ranges using the rangeValues Group By parameter. Each object in the rangeValues array must have a start (e.g., 0), and an end (e.g., 100). Optionally, it may also have a label (e.g., Short), which will be used as a custom lookupValue. By default, the end value is not included in the requested range, but you can set endInclusive to true to modify this behavior.

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

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

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

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

POST https://plaform.cloud.coveo.com/rest/search/v2 HTTP/1.1
 
Accept: application/json
Content-Type: application/json
Authorization: Bearer **********-****-****-****-************
{
  ...
  "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"
        }
      ]
    },
    ...
  ],
  ...
}

Requesting Slider Facets

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

You may want to allow the end-user to specify their own custom ranges of values through some sort of slider facet rather than selecting and/or excluding pre-defined ranged facet values from a ranged facet (see Requesting Ranged Facets).

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 (e.g., @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 end-user interacts with the slider facet to specify a custom range, you can then alter the current advanced query expression (i.e., the aq search request parameter) accordingly (see Toggling Facet Values).

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

POST https://plaform.cloud.coveo.com/rest/search/v2 HTTP/1.1
 
Accept: application/json
Content-Type: application/json
Authorization: Bearer **********-****-****-****-************
{
  ...
  "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"
        }
      ]
    },
    ...
  ],
  ...
}

Toggling Facet Values

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

  1. Generate a query syntax expression that represents the updated facet state (see Coveo Cloud Query Syntax Reference).

    In a disjunctive facet based on the @bookauthor field, the Poe, Edgar Allan and Clarke, Arthur C. values are currently selected. The end-user interacts with the facet to exclude the Gibbon, Edward value. A query syntax expression such as what follows could now represent the updated facet state: (@bookauthor==("Poe, Edgar Allan", "Clarke, Arthur C.")) (NOT @author==("Gibbon, Edward"))

  2. Prepare a new query. Ensure that the aq search request parameter includes the expression generated at Step 1.

  3. Call the Search API to execute the query prepared at Step 2 (see Performing a Query). When the Search API returns:

    1. Call the Usage Analytics Write API to log the corresponding Search event (see Logging Search Events). In the request body:

      • Set the actionCause property to:

        • facetSelect if the end-user has selected a single facet value.
        • facetUnselect if the end-user has un-selected a single facet value.
        • facetExclude if the end-user has excluded a single facet value.
        • facetUnexclude if the end-user has un-excluded a single facet value.
        • facetClearAll if the end-user has cleared all previously selected and/or excluded values.
        • facetRangeSlider or facetRangeGraph (whichever is more appropriate) if the end-user has selected a custom range of numeric or date values through a slider facet (see Requesting Slider Facets).
      • Include the following key-value pairs in the customData property:

        • "facetField": <facetField>
        • "facetId": <facetId>
        • "facetTitle": <facetTitle> (do not include if actionCause is facetRangeSlider or facetRangeGraph)
        • "facetValue": <facetValue> (do not include if actionCause is facetClearAll, facetRangeSlider, or facetRangeGraph)
        • "facetRangeStart": <facetRangeStart> (only inlucde 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 (e.g., @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 (e.g., @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 (e.g., Book Length).
        • <facetValue> (string) is the name of the toggled facet value (e.g., Short).
        • <facetRangeStart> (string) is the first value of the end-user specified range.
        • <facetRangeEnd> (string) is the last value of the end-user specified range.

      The Coveo JavaScript Search Framework includes components that allow the end-user to interact with facets indirectly (see the Breadcrumb and FieldValue components).

      If you implement similar widgets, be sure to use the appropriate actionCause and customData when logging a Search event after the end-user has indirectly toggled one or more facet values:

      Action actionCause customData
      Select/un-select a single facet value by interacting with a query result documentField facetId, facetTitle, facetValue
      Un-select/un-exclude a single facet value by interacting with breadcrumbs breadcrumbFacet facetId, facetTitle, facetValue
      Clear all selected/excluded facet values by interacting with breadcrumbs breadcrumbResetAll N/A
    2. Update all facets in the search interface (see Requesting and Rendering Facets).

    3. Update the result list (see Implementing a Result List - Rendering Query Results).

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

    POST https://plaform.cloud.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 end-user has selected the Hardcover value from the Book Format facet by interacting with a query result

    POST https://plaform.cloud.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 end-user has un-excluded the Medium value from the Book Length facet by interacting with the breadcrumbs

    {
      ...
      "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://platform.cloud.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",
        ...
      }
    ]
    

Requesting 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 being 10). When completeFacetWithStandardValues is set to true and the cardinality of allowedValues is lower than the maximumNumberOfValues, the index will attempt to retrieve standard values until the values array contains maximumNumberOfValues.

You may also want to allow the end-user to autonomously request more values to be displayed in a facet. When the end-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 that the end-user has requested additional values for (see Group By Operations). In this Group By operation:

    1. Set field to the name of the field the facet is based on (e.g., @bookauthor).
    2. In the allowedValues array, include all values currently displayed by the facet (e.g., Poe, Edgar Allan, Clarke, Arthur C., Gibbon, Edward).
    3. Set maximumNumberOfValues to a higher value than the one that was specified last time the facet was requested (e.g., 6).
    4. Set completeFacetWithStandardValues to true.
    5. Set all other Group By parameters to the value they were last time the facet was requested.

    Although the query prepared at Step 1 is not meant to return actual result items, be sure to include other search request parameters as usual so that the query represents the current state of the search interface (aside from numberOfResults being forcefully set to 0 and groupBy not necessarily containing all necessary operations to update facets). Otherwise, the retrieved groupByResults object may not contain accurate/consistent facet values/occurrence counts.

  2. Call the Search API to execute the query prepared at Step 1 (see Performing a Query). When the Search API returns, re-render the updated facet as appropriate (see Requesting and Rendering Facets - Steps 2.a and 2.b).

When the end-user requests additional facet values to be displayed:

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

POST https://plaform.cloud.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": [],
  ...
}

Sorting 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 end-user to autonomously change facet sort criteria. When the end-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 end-user has selected a new sort criteria (see Group By Operations). In this Group By operation:

    1. Set field to the name of the field the facet is based on (e.g., @bookformat).
    2. Set sortCriteria to the value corresponding to what the end-user has selected.
    3. Set all other Group By parameters to the value they were last time the facet was requested.

    Although the query prepared at Step 1 is not meant to return actual result items, be sure to include other search request parameters as usual so that the query represents the current state of the search interface (aside from numberOfResults being forcefully set to 0 and groupBy not necessarily containing all necessary operations to update facets). Otherwise, the retrieved groupByResults object may not contain accurate/consistent facet values/occurrence counts.

  2. Call the Search API to execute the query prepared at Step 1 (see Performing a Query). When the Search API returns:

    1. Call the Usage Analytics Write API to log the corresponding Custom event (see Logging Custom Events). 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 (e.g., AlphaAscending).
        • <facetField> (string) is the @-prefixed name of the field this facet is based on (e.g., @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 (e.g., @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 (e.g., Book Format).
      • Set other required/optional properties as appropriate (language, originLevel1, originLevel2, etc.).
    2. Re-render the facet as appropriate (see Requesting and Rendering Facets - Step 2.a).

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

    POST https://plaform.cloud.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://platform.cloud.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",
      ...
    }
    

Using Custom Facet Sort Criteria

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

  • You can include multiple 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 those 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 identical 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 (i.e., the average of the all averages, the sum of all sums, etc.).

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

POST https://plaform.cloud.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 the end-user to look for specific facet values by typing in a dedicated search input from within a facet. When the end-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 (e.g., 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 the end-user is searching in (see Group By Operations). In this Group By operation:

      1. Set field to the name of the field the searched facet is based on (e.g., @bookgenre).
      2. In the allowedValues array, include the original, and *-prefixed and *-suffixed value retrieved at Step 1.a (e.g., a and *a*).
      3. Set maximumNumberOfValues to the maximum number of matching values to retrieve through facet search (e.g., 4). Default value is 10.
      4. Set all other Group By parameters to the value they were last time the facet was requested.

      Although the query prepared at Step 1.b is not meant to return actual result items, be sure to include other search request parameters as usual so that the query represents the current state of the search interface (aside from numberOfResults being forcefully set to 0 and groupBy not necessarily containing all necessary operations to update facets). Otherwise, the retrieved groupByResults object may not contain accurate/consistent facet values/occurrence counts.

    3. Call the Search API to execute the query prepared at Step 1.b (see Performing a Query). When the Search API returns:

      1. Call the Usage Analytics Write API to log the corresponding Custom event (see Logging Custom Events). 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 (e.g., @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 (e.g., @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 (e.g., Book Genre).
        • Set other required/optional properties as appropriate (language, originLevel1, originLevel2, etc.).
      2. Render the retrieved facet values as appropriate (e.g., in a dedicated result list below the facet search input).

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

Executing a query after the end-user has typed a in the dedicated search input of the Book Genre facet

POST https://plaform.cloud.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": [],
  ...
}

Logging a Custom usage analytics event for the previous action

POST https://platform.cloud.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",
  ...
}
People also viewed