Implement facets using groupBy operations
Implement facets using groupBy operations
A search interface often includes facets that allow the end 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 properly implementing facets on your own, assuming that you can’t use the Coveo JavaScript Search Framework in your custom search integration with Coveo.
-
We recommend using 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 of the most frequent actions you may want to make available in a facet implementation.
Each entry indicates the specific Coveo Usage Analytics (Coveo UA) event category, actionCause
/eventValue
, eventType
, and customData that must be logged when the action is performed, as well as a link to the corresponding implementation guidelines (see Log Usage analytics events).
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 |
It’s important to use the proper eventType
, actionCause
/eventValue
, and customData
when logging a usage analytics event for a specific type of action in a search interface. Otherwise:
- Usage analytics reports may become incoherent in the underlying Coveo organization (especially if that organization powers both Coveo JavaScript Search Framework and custom search interfaces).
- The Coveo Machine Learning (Coveo ML) service may not function properly.
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. To do so:
-
In the query, populate the
groupBy
search request parameter with a distinct Group By operation for each facet to render.Only the
field
Group By parameter is required. By default, when a Group By operation merely contains thefield
parameter, this operation requests up to ten facet values sorted by descendingscore
order (or alphabetically in the case of ranged facets). You can use themaximumNumberOfValues
andsortCriteria
Group By parameters to override those settings (see Requesting Additional Facet Values and Sorting Facet Values).If the specified
field
references a field that doesn’t exist in the index, or whosefacet
option is set tofalse
, the correspondinggroupByResults
object in the query response body will contain an emptyvalues
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 emptyvalues
array typically means that the corresponding facet shouldn’t be rendered in the search interface. -
When the query is executed and the Search API returns, for each facet to render in the search interface:
-
In the query response body, retrieve the
groupByResults
object whosefield
value matches the field on which the facet relies. -
Use the
lookupvalue
(orvalue
) andnumberOfResults
attributes of each object in thevalues
array of thegroupByResults
object retrieved in step 2.a to render/update the facet.For a given object in the
values
array, thelookupValue
andvalue
attributes will contain identical data except when the object is a range value (see Requesting Ranged Facets).
-
-
In a
groupByResults
object, thefield
andglobalComputedFieldResults
properties, as well as allvalues
object properties (lookupValue
,numberOfResults
, etc.) are duplicated in PascalCase for legacy reasons (e.g., each item in thevalues
array of agroupByResults
item has both thenumberOfResults
andNumberOfResults
properties).Always use the camelCase version (e.g.,
numberOfResults
).For the sake of brevity, 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 theresults
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 thefilterField
search request parameter as necessary before executing the query (see Handling Folded Results).
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"
}
]
},
...
],
...
}
Requesting ranged facets
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
(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 isn’t 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 result in awkward ordering.
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.
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"
}
]
},
...
],
...
}
Requesting slider facets
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 end users to specify their own custom range of values through a slider facet rather than selecting and/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 (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 (aq
) accordingly (see Toggling Facet Values).
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"
}
]
},
...
],
...
}
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 (aq
). When the end user interacts with a facet in such a way:
-
Generate a query syntax expression that represents the updated facet state.
In a disjunctive facet based on the
@bookauthor
field, the currently selected values arePoe, Edgar Allan
andClarke, Arthur C.
The end user interacts with the facet to exclude theGibbon, Edward
value. The updated facet state could be represented by a query syntax expression such as:(@bookauthor==("Poe, Edgar Allan", "Clarke, Arthur C.")) (NOT @author==("Gibbon, Edward"))
-
Prepare a new query. Ensure that the
aq
search request parameter includes the expression generated in step 1. -
Call the Search API to execute the query prepared in step 2. When the Search API returns:
-
Call the Usage Analytics Write API to log the corresponding Search event. 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
orfacetRangeGraph
(whichever is more appropriate) if the end 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 ifactionCause
isfacetRangeSlider
orfacetRangeGraph
) -
"facetValue": <facetValue>
(don’t include ifactionCause
isfacetClearAll
,facetRangeSlider
, orfacetRangeGraph
) -
"facetRangeStart": <facetRangeStart>
(only include ifactionCause
isfacetRangeSlider
orfacetRangeGraph
) -
"facetRangeEnd": <facetRangeEnd>
(only include ifactionCause
isfacetRangeSlider
orfacetRangeGraph
)
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
andFieldValue
components).If you implement similar widgets, be sure to use the appropriate
actionCause
andcustomData
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 |
- Set other required/optional properties as appropriate (
language
, originLevel1, originLevel2, etc.).
-
-
Update all facets in the search interface (see Requesting and Rendering Facets).
-
Update the result list (see Rendering Query Results).
-
-
Executing a query after the end user has excluded the
Medium
value from the Book Length facet by interacting with the facet directlyPOST 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", ... }
-
Executing a query after the end user has selected the
Hardcover
value from the Book Format facet by interacting with a query resultPOST 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", ... }
-
Executing a query after the end user has un-excluded the
Medium
value from the Book Length facet by interacting with the breadcrumbsPOST 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", ... }
-
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", ... } ]
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 is 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:
-
Prepare a new query. Ensure that the
numberOfResults
search request parameter is set to0
. In thegroupBy
search request parameter, include a Group By operation to update the facet for which the end user has requested additional values. In this Group By operation:- Set
field
to the name of the field that the facet is based on (e.g.,@bookauthor
). - In the
allowedValues
array, include all of the values currently displayed by the facet (e.g.,Poe, Edgar Allan
,Clarke, Arthur C.
,Gibbon, Edward
). - Set
maximumNumberOfValues
to a higher value than the one that was specified the last time the facet was requested (e.g.,6
). - Set
completeFacetWithStandardValues
totrue
. - Set all other Group By parameters to the value they were the last time the facet was requested.
Although the query prepared in step 1 isn’t 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 to0
andgroupBy
not necessarily containing all of the operations required to update facets). Otherwise, the retrievedgroupByResults
object may not contain accurate or consistent facet values/occurrence counts. - Set
-
Call the Search API to execute the query prepared in step 1. 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:
- No specific usage analytics event needs to be logged.
- The result list shouldn’t be updated.
Executing a query after the end 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": [],
...
}
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:
-
Prepare a new query. Ensure that the
numberOfResults
search request parameter is set to0
. In thegroupBy
search request parameter, include a Group By operation to update the facet for which the end user has selected a new sort criteria. In this Group By operation:- Set
field
to the name of the field that the facet is based on (e.g.,@bookformat
). - Set
sortCriteria
to the value that corresponds to the end user’s selection. - Set all other Group By parameters to the value they were the last time the facet was requested.
Although the query prepared in step 1 isn’t 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 to0
andgroupBy
not necessarily containing all of the operations required to update facets). Otherwise, the retrievedgroupByResults
object may not contain accurate or consistent facet values/occurrence counts. - Set
-
Call the Search API to execute the query prepared in step 1. When the Search API returns:
-
Call the Usage Analytics Write API to log the corresponding Custom event. In the request body:
- Set the
eventType
property tofacet
. - Set the
eventValue
property tofacetUpdateSort
. -
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.).
- Set the
-
Re-render the facet as appropriate (see Requesting and Rendering Facets - Step 2.a).
-
-
Executing a query after the end 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": [], ... }
-
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", ... }
Using 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 (i.e., average
, maximum
, minimum
, or sum
), use the computedFields
Group By parameter along with either the ComputedFieldAscending
or ComputedFieldDescending
sortCriteria
value (see Computed Fields).
-
You can include many aggregate operations in the
computedFields
array of a Group By operation. In the query response body, thecomputedFieldResults
array property of eachvalues
element in the correspondinggroupByResults
object sequentially stores the results of these operations. When using theComputedFieldAscending
orComputedFieldDescending
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 firstcomputedFieldResults
values, theAlphaDescending
sort criteria is used as a fallback). -
The
groupByResults
object itself has aglobalComputedFieldResults
property which sequentially stores the results of the aggregate operation applied to all correspondingcomputedFieldResults
(i.e., the average of 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://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"
}
]
},
...
],
...
}
Performing facet search
You may want to allow the end user to look for specific facet values by typing into a dedicated search input within a facet. When the end user interacts with a facet in such a way:
-
On each valid keystroke in the corresponding facet search input:
- Retrieve the current value from the facet search input (e.g.,
a
). -
Prepare a new query. Ensure that the
numberOfResults
search request parameter is set to0
. In thegroupBy
search request parameter, include a Group By operation for the facet in which the end user is searching. In this Group By operation:- Set
field
to the name of the field that the searched facet is based on (e.g.,@bookgenre
). - In the
allowedValues
array, include the original and the*
-prefixed and*
-suffixed value retrieved in step 1.a (e.g.,a
and*a*
). - Set
maximumNumberOfValues
to the maximum number of matching values to retrieve through facet search (e.g.,4
). The default value is10
. - Set all other Group By parameters to the value they were the last time the facet was requested.
Although the query prepared in step 1.b isn’t 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 to0
andgroupBy
not necessarily containing all of the operations required to update facets). Otherwise, the retrievedgroupByResults
object may not contain accurate or consistent facet values/occurrence counts. - Set
-
Call the Search API to execute the query prepared in step 1.b. When the Search API returns:
-
Call the Usage Analytics Write API to log the corresponding Custom event. In the request body:
- Set the
eventType
property tofacet
. - Set the
eventValue
property tofacetSearch
. -
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.).
- Set the
-
Render the retrieved facet values as appropriate (e.g., in a dedicated result list below the facet search input).
-
- Retrieve the current value from the facet search input (e.g.,
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 opt to use an entirely different approach for your own implementation (to consume less queries per month (QPM), for example).
-
Executing a query after the end user has typed
a
in the dedicated search input of the Book Genre facetPOST 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": [], ... }
-
Logging a Custom usage analytics 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", ... }