Implement facets
Implement facets
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 JavaScript Search Framework in your custom search integration with Coveo.
-
As a reference, you may want to look at the source code of the
DynamicFacet
component to see how it’s implemented within the JavaScript Search Framework. -
This article explains how to implement facets using the
facets
query parameter, which is now the recommended approach. Guidelines for implementing facets using thegroupBy
query parameter are still available, although this approach requires more complex client-side logic, and can’t benefit from features such as Coveo Machine Learning (Coveo ML) Dynamic Navigation Experience (DNE).
Standard facet actions
The following table lists some of the most frequent actions you may want to make available in a facet implementation.
Every action belongs to a specific Coveo Usage Analytics (Coveo UA) event category, and each entry indicates the actionCause
, eventType
, eventValue
, and customData values (if applicable) that must be logged when the action is performed. Each entry also includes 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 |
Resetting selected | Search |
facetClearAll / breadcrumbResetAll
|
N/A | N/A |
facetField , facetId , facetTitle
|
Toggling Facet Values |
Requesting more values | Custom | showMoreFacetResults |
dynamicFacet |
showMoreFacetResults |
facetField , facetId , facetTitle
|
Requesting Additional Facet Values |
Requesting less values | Custom | showLessFacetResults |
dynamicFacet |
showLessFacetResults |
facetField , facetId , facetTitle
|
Requesting Additional Facet Values |
Searching for a value | N/A | N/A | N/A | N/A | N/A | Performing Facet Search |
It’s important to use the correct actionCause
, eventType
, eventValue
, and customData
values 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 JavaScript Search Framework and custom search interfaces).
- The 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., Ernest Hemingway
, Mark Twain
), 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 facets
query parameter. Each valid object you include in the facets
query parameter will populate a distinct object in the facets
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, prepare a new query. In the request body:
-
In the
facets
query parameter, include a correspondingfacet
object for each facet to display in the search interface. -
In the
facet
object operation, setfield
to the name of the field that the facet is based on (e.g.,@bookauthor
).
When the Search API returns, for each facet to render in the search interface:
-
In the query response body, retrieve the object in the
facets
array whosefield
value matches the field on which the facet relies. -
Use the
value
,numberOfResults
, andstate
attributes of each object in thevalues
array from the query response to render/update the facet.
Call the Usage Analytics Write API to log the corresponding Search event. In the request body, include an object containing the following key-value pairs in the facetState
property for each non-idle facet value:
-
"facetType": <TYPE>
-
"field": <FIELDNAME>
-
"id": <ID>
-
"state": <STATE>
-
"value": <VALUE>
-
"displayValue": <DISPLAY_VALUE>
(optional) -
"facetPosition": <FACET_POSITION>
(optional) -
"title": <TITLE>
(optional) -
"valuePosition": <VALUE_POSITION>
(optional)
where:
-
<TYPE>
(string) is the facet data type (e.g.,specific
). -
<FIELDNAME>
(string) is the@
-prefixed name of the field that this facet is based on (e.g.,@booklength
). -
<ID>
(string) is the unique identifier of the facet in which one or more values were toggled. Typically, this is the@
-prefixed name of the field that this facet is based on (e.g.,@booklength
). -
<STATE>
(string) is the current state of the facet value (eitherauto_selected
orselected
). -
<VALUE>
(string) is the name of the toggled facet value (e.g.,Hardcover
). -
<DISPLAY_VALUE>
(string) is the display name of the facet value (e.g.,Hardcover
). -
<FACET_POSITION>
(integer) is the 1-based position of the facet (e.g.,1
). -
<TITLE>
(string) is the facet title (e.g.,Format
) -
<VALUE_POSITION>
(integer) is the 1-based position of the value in the facet (e.g.,2
).
Example
A customer accesses the Books4Fun search interface. Once loaded, the search interface retrieves and renders the Book Format and Author facets with the following request:
POST https://platform.cloud.coveo.com/rest/search/v2 HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer **********-****-****-****-************
Payload (excerpt)
{
...
"q": "Doctor Sleep",
"cq": "NOT @source==\"Movies4Fun\"",
...
"facets": [
{
"facetId": "@bookformat",
"field": "bookformat",
"type": "specific",
"currentValues": [],
...
},
{
"field": "bookauthor",
"facetId": "@bookauthor",
"type": "specific",
"currentValues": [],
...
}
],
...
}
200 OK response body (excerpt)
{
...
"facets": [
{
"facetId": "@bookformat",
"field": "bookformat",
"moreValuesAvailable": true,
"values": [
{
"value": "Hardcover",
"state": "idle",
"numberOfResults": 83
},
{
"value": "Softcover",
"state": "idle",
"numberOfResults": 47
},
...
],
"indexScore": 0.435
},
{
"facetId": "@bookauthor",
"field": "bookauthor",
"moreValuesAvailable": true,
"values": [
{
"value": "Charles Dickens",
"state": "idle",
"numberOfResults": 24
},
{
"value": "Ernest Hemingway",
"state": "idle",
"numberOfResults": 26
},
{
"value": "Mark Twain",
"state": "idle",
"numberOfResults": 23
},
...
],
"indexScore": 0.435
}
],
...
}
A Search event is logged for this action:
POST https://analytics.cloud.coveo.com/rest/ua/v15/analytics/searches?visitor=********-****-****-******** HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer **********-****-****-****-************
Payload (excerpt)
[
{
"actionCause": "interfaceLoad",
...
"customData": {
"facetField": "@bookformat",
"facetId": "@bookformat",
"facetTitle": "Format",
},
...
"facetState": []
}
]
Toggling Facet Values
In a typical faceted search interface, the end user can toggle (i.e., select/un-select) any number of facet values to alter the facets
query parameter. When the end user interacts with a facet in this way, prepare a new query. For each facet currently displayed in the search interface, include a corresponding object in the facets
query parameter:
-
Set
facetId
to the unique identifier of the facet. -
Set
field
to the name of the field the facet is based on. -
Set
type
tospecific
. -
If the facet is the one that was just interacted with, set
freezeCurrentValues
totrue
. -
If more than the originally specified
numberOfResults
are being requested for the facet, setisFieldExpanded
totrue
(see Requesting Additional Facet Values). -
For each value currently displayed in the facet, include a corresponding object in the
currentValues
array:-
Set the
value
property to the value of the current facet. -
Set the
state
property toidle
if the facet isn’t selected, or set it toselected
otherwise. -
Set
preventAutoSelect
property tofalse
to ensure that Coveo ML automatically selects the facet value.
-
-
Call the Search API to execute this query.
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. -
facetClearAll
if the end user has cleared all previously selected values.
-
-
Include the following key-value pairs in the
customData
property:-
"facetField": <FIELDNAME>
-
"facetId": <ID>
-
"facetTitle": <TITLE>
-
"facetValue": <VALUE>
where:
-
<FIELDNAME>
(string) is the@
-prefixed name of the field that this facet is based on (e.g.,@booklength
). -
<ID>
(string) is the unique identifier of the facet in which one or more values were toggled. Typically, this is the@
-prefixed name of the field that this facet is based on (e.g.,@booklength
). -
<TITLE>
(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 that this facet is based on (e.g.,Book Length
). -
<VALUE>
(string) is the name of the toggled facet value (e.g.,Short
).
The JavaScript Search Framework includes components that allow the end user to interact with facets indirectly (e.g.,
Breadcrumb
andFieldValue
components).If you implement your own components, be sure to use the appropriate
actionCause
andcustomData
values 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 a single facet value by interacting with breadcrumbs breadcrumbFacet
facetId
,facetTitle
,facetValue
Clear all selected facet values by interacting with breadcrumbs breadcrumbResetAll
N/A -
-
For each non-idle facet, include an object containing the following key-value pairs in the
facetState
property:-
"facetType": <TYPE>
-
"field": <FIELDNAME>
-
"id": <ID>
-
"state": <STATE>
-
"value": <VALUE>
-
"displayValue": <DISPLAY_VALUE>
(optional) -
"facetPosition": <FACET_POSITION>
(optional) -
"title": <TITLE>
(optional) -
"valuePosition": <VALUE_POSITION>
(optional)
where:
-
<TYPE>
(string) is the facet data type (e.g.,specific
). -
<FIELDNAME>
(string) is the@
-prefixed name of the field that this facet is based on (e.g.,@booklength
). -
<ID>
(string) is the unique identifier of the facet in which one or more values were toggled. Typically, this is the@
-prefixed name of the field that this facet is based on (e.g.,@booklength
). -
<STATE>
(string) is the current state of the facet value (eitherauto_selected
orselected
). -
<VALUE>
(string) is the name of the toggled facet value (e.g.,Hardcover
). -
<DISPLAY_VALUE>
(string) is the display name of the facet value (e.g.,Hardcover
). -
<FACET_POSITION>
(integer) is the 1-based position of the facet (e.g.,1
). -
<TITLE>
(string) is the facet title (e.g.,Format
) -
<VALUE_POSITION>
(integer) is the 1-based position of the value in the facet (e.g.,2
).
-
-
Set other required/optional properties as appropriate (
language
, originLevel1, originLevel2, etc.).
When the Usage Analytics Write API returns successfully:
Example
A customer is browsing products on the Books4Fun site. They select the Hardcover
value from the Book Format facet, which executes the following query:
POST https://platform.cloud.coveo.com/rest/search/v2 HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer **********-****-****-****-************
Payload (excerpt)
{
...
"q": "Doctor Sleep",
"cq": "NOT @source==\"Movies4Fun\"",
...
"facets": [
{
"facetId": "@bookformat",
"field": "bookformat",
"type": "specific",
"currentValues": [
{
"value": "Hardcover",
"state": "selected",
"preventAutoSelect": false
}
{
"value": "Softcover",
"state": "idle",
"preventAutoSelect": false
},
...
],
"numberOfValues": 3,
...
},
{
"field": "bookauthor",
"facetId": "@bookauthor",
"type": "specific",
"currentValues": [
{
"value": "Ernest Hemingway",
"state": "idle",
"preventAutoSelect": false
},
{
"value": "Mark Twain",
"state": "idle",
"preventAutoSelect": false
}
],
"numberOfValues": 3,
...
}
],
...
"language": "en",
"originLevel1": "Books4FunSearch",
"originLevel2": "All",
...
}
200 OK response body (excerpt)
{
...
"facets": [
{
"facetId": "@bookformat",
"field": "bookformat",
"moreValuesAvailable": true,
"values": [
{
"value": "Hardcover",
"state": "selected",
"numberOfResults": 83
},
{
"value": "Softcover",
"state": "idle",
"numberOfResults": 47
},
...
],
"indexScore": 0.435
},
{
"facetId": "@bookauthor",
"field": "bookauthor",
"moreValuesAvailable": true,
"values": [
{
"value": "Charles Dickens",
"state": "idle",
"numberOfResults": 24
},
{
"value": "Ernest Hemingway",
"state": "idle",
"numberOfResults": 26
},
{
"value": "Mark Twain",
"state": "idle",
"numberOfResults": 23
},
...
],
"indexScore": 0.435
}
],
...
}
A Search event is logged for this action:
POST https://analytics.cloud.coveo.com/rest/ua/v15/analytics/searches?visitor=********-****-****-******** HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer **********-****-****-****-************
Payload (excerpt)
[
{
"actionCause": "facetSelect",
"eventType": "dynamicFacet",
...
"customData": {
"facetField": "@bookformat",
"facetId": "@bookformat",
"facetTitle": "Format",
"facetValue": "Hardcover"
},
...
"facetState": [
{
"field": "@bookformat",
"id": "@bookformat",
"title": "Format",
"facetType": "specific",
"facetPosition": 1,
"value": "Hardcover",
"valuePosition": 2,
"displayValue": "Hardcover",
"state": "selected"
}
]
}
]
Requesting additional facet values
The numberOfValues
facet request parameter lets you specify the maximum number of facet values that the index may retrieve to populate the values
array of a given facet
object (the default value is 8
).
You may also want to allow the end user to autonomously request to display more values in a facet. When the end user interacts with a facet in this way, prepare a new query:
-
Set the
numberOfResults
query parameter to0
. -
In the
facets
query parameter, include thefacet
object for which the end user has requested additional values. -
In the
facet
object operation, setfield
to the name of the field that the facet is based on (e.g.,@bookauthor
). -
For each value currently displayed in the facet, include a corresponding object in the
currentValues
array:-
Set the
value
property to the value of the current facet. -
Set the
state
property toidle
if the facet isn’t selected, or set it toselected
otherwise. -
Set
preventAutoSelect
property tofalse
to ensure that Coveo ML automatically selects the facet value.
-
-
Set
numberOfValues
to a higher value than the one that was specified the last time the facet was requested (e.g.,16
). -
Set
isFieldExpanded
totrue
. -
Set all of the other facet request parameters to the same values as the last time the facet was requested.
Although this query isn’t meant to return actual result items, you should include the other search request parameters as usual so that it represents the current state of the search interface (aside from
numberOfResults
being forcefully set to0
andfacets
not necessarily containing all of the operations required to update facets). Otherwise, the retrievedfacets
object may not contain accurate or consistent facet values/occurrence counts. -
Call the Search API to execute this query.
When the Search API returns, call the Usage Analytics Write API to log the corresponding Custom event. In the request body:
-
Set the
actionCause
property toshowMoreFacetResults
when showing more results orshowLessFacetResults
when showing less. -
Set the
eventType
property todynamicFacet
. -
Set the
eventValue
property toshowMoreFacetResults
when showing more results orshowLessFacetResults
when showing less. -
Include the following key-value pairs in the
customData
property:-
"facetField": <FIELDNAME>
-
"facetId": <ID>
-
"facetTitle": <TITLE>
where:
-
<FIELDNAME>
(string) is the@
-prefixed name of the field that this facet is based on (e.g.,@bookformat
). -
<ID>
(string) is the unique identifier of the facet in which the sort criteria was changed. Typically, this is the name of the field that this facet is based on (e.g.,@bookformat
). -
<TITLE>
(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 that this facet is based on (e.g.,Book Format
).
-
-
Set other required/optional properties as appropriate (
language
,originLevel1
,originLevel2
, etc.).
When the Usage Analytics Write API returns successfully, re-render the facet as appropriate.
Example
A customer is browsing products on the Books4Fun site. They can’t find their favourite author among the currently rendered values in the Author facet, so they request to display more values. This interaction executes the following request:
POST https://platform.cloud.coveo.com/rest/search/v2 HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer **********-****-****-****-************
Payload (excerpt)
{
...
"q": "Doctor Sleep",
"cq": "NOT @source==\"Movies4Fun\"",
"numberOfResults": 0,
...
"facets": [
{
"facetId": "@bookformat",
"field": "bookformat",
"type": "specific",
"currentValues": [
{
"value": "Hardcover",
"state": "selected",
"preventAutoSelect": false
}
{
"value": "Softcover",
"state": "idle",
"preventAutoSelect": false
},
...
],
"numberOfValues": 3,
...
},
{
"field": "bookauthor",
"facetId": "@bookauthor",
"type": "specific",
"currentValues": [
{
"value": "Ernest Hemingway",
"state": "idle",
"preventAutoSelect": false
},
{
"value": "Mark Twain",
"state": "idle",
"preventAutoSelect": false
}
],
"numberOfValues": 6,
"isFieldExpanded": true,
...
}
],
...
"language": "en",
"originLevel1": "Books4FunSearch",
"originLevel2": "All",
...
}
200 OK response body (excerpt)
{
...
"facets": [
{
"facetId": "@bookformat",
"field": "bookformat",
"moreValuesAvailable": true,
"values": [
{
"value": "Hardcover",
"state": "selected",
"numberOfResults": 83
},
{
"value": "Softcover",
"state": "idle",
"numberOfResults": 47
},
...
],
"indexScore": 0.435
},
{
"facetId": "@bookauthor",
"field": "bookauthor",
"moreValuesAvailable": true,
"values": [
{
"value": "Charles Dickens",
"state": "idle",
"numberOfResults": 24
},
{
"value": "Ernest Hemingway",
"state": "idle",
"numberOfResults": 26
},
{
"value": "Mark Twain",
"state": "idle",
"numberOfResults": 23
},
{
"value": "Stephen King",
"state": "idle",
"numberOfResults": 14
},
...
],
"indexScore": 0.435
}
],
...
}
A Custom event is logged for this action:
POST https://analytics.cloud.coveo.com/rest/ua/v15/analytics/custom?visitor=********-****-****-******** HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer **********-****-****-****-************
Payload (excerpt)
[
{
"actionCause": "showMoreFacetResults",
"eventType": "dynamicFacet",
"eventValue": "showMoreFacetResults",
...
"customData": {
"facetField": "@bookformat",
"facetId": "@bookformat",
"facetTitle": "Format",
},
...
"facetState": [
{
"field": "@bookformat",
"id": "@bookformat",
"title": "Format",
"facetType": "specific",
"facetPosition": 1,
"value": "Hardcover",
"valuePosition": 2,
"displayValue": "Hardcover",
"state": "selected"
}
]
}
]
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 this way, on each valid keystroke:
-
Retrieve the current value from the facet search input (e.g.,
a
). -
Prepare a new facet search request. In the request body:
-
Set
field
property to the name of the field that the facet being searched is based on (e.g.,bookauthor
). -
Set the
query
property to the*
-prefixed and*
-suffixed value that was retrieved in step 1 (e.g.,a
becomes*a*
). -
Set
numberOfValues
property to the maximum number of matching values to retrieve through facet search (e.g.,4
). The default value is10
. -
Set other optional properties as appropriate (
ignoreValues
,captions
, etc.).
-
-
Set all of the other facet request parameters to the same values as the last time the facet was requested.
Although this query isn’t meant to return actual result items, you should include the other search request parameters as usual so that it represents the current state of the search interface (aside from
numberOfResults
being forcefully set to0
andfacets
not necessarily containing all of the operations required to update facets). Otherwise, the retrievedfacets
object may not contain accurate or consistent facet values/occurrence counts. -
Call the Search API to execute this query.
-
When the Search API returns, render the retrieved facet values using the
displayValue
andcount
values of each object in thevalues
array in the response body.
Example
A customer is browsing products on the Books4Fun site. They can’t find their favourite author among the currently rendered values in the Author facet, so they begin typing the authors name into the facet search box. This interaction executes the following request:
POST https://myorganizationid9sd8df7s.org.coveo.com/rest/search/v2/facet HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer **********-****-****-****-************
Payload (excerpt)
{
"field": "bookauthor",
"numberOfValues": 4,
"query": "*W*"
}
200 OK response body (excerpt)
{
"values": [
{
"displayValue": "William Shakespeare",
"rawValue": "William Shakespeare",
"count": 10
},
{
"displayValue": "William Faulkner",
"rawValue": "William Faulkner",
"count": 5
},
...
],
"moreValuesAvailable": false
}
These guidelines suggest a facet search implementation which is based on that of the 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.