Skip to content

Commit b9013c4

Browse files
committed
Draft - Facet doc with details ported from REST API reference
1 parent ca25d85 commit b9013c4

File tree

1 file changed

+98
-63
lines changed

1 file changed

+98
-63
lines changed

articles/search/search-faceted-navigation.md

Lines changed: 98 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ manager: nitinme
77
author: HeidiSteen
88
ms.author: heidist
99
ms.service: azure-ai-search
10-
ms.topic: conceptual
10+
ms.topic: concept-article
1111
ms.date: 02/26/2025
1212
---
1313

1414
# Add faceted navigation to a search app
1515

1616
Faceted navigation is used for self-directed drill-down filtering on query results in a search app, where your application offers form controls for scoping search to groups of documents (for example, categories or brands), and Azure AI Search provides the data structures and filters to back the experience.
1717

18-
In this article, learn how to create a faceted navigation structure in Azure AI Search.
18+
In this article, learn how to return a faceted navigation structure in Azure AI Search.
1919

2020
## Faceted navigation in a search page
2121

@@ -27,63 +27,11 @@ In Azure AI Search, facets are one layer deep and can't be hierarchical. If you
2727

2828
Facets can help you find what you're looking for, while ensuring that you don't get zero results. As a developer, facets let you expose the most useful search criteria for navigating your search index.
2929

30-
## Add facets to an index
31-
32-
Facets are enabled on a field-by-field basis in an index definition when you set the "facetable" attribute to true.
33-
34-
Although it's not strictly required, it's a best practice to also set the "filterable" attribute so that you can build the necessary filters that back the faceted navigation experience in your search application.
35-
36-
The following example of the hotels sample index shows "facetable" and "filterable" on low cardinality fields that contain single values or short phrases: "Category", "Tags", "Rating".
37-
38-
```json
39-
{
40-
"name": "hotels",
41-
"fields": [
42-
{ "name": "hotelId", "type": "Edm.String", "key": true, "searchable": false, "sortable": false, "facetable": false },
43-
{ "name": "Description", "type": "Edm.String", "filterable": false, "sortable": false, "facetable": false },
44-
{ "name": "HotelName", "type": "Edm.String", "facetable": false },
45-
{ "name": "Category", "type": "Edm.String", "filterable": true, "facetable": true },
46-
{ "name": "Tags", "type": "Collection(Edm.String)", "filterable": true, "facetable": true },
47-
{ "name": "Rating", "type": "Edm.Int32", "filterable": true, "facetable": true },
48-
{ "name": "Location", "type": "Edm.GeographyPoint" }
49-
]
50-
}
51-
```
52-
53-
### Choosing fields
54-
55-
Facets can be calculated over single-value fields and collections. Fields that work best in faceted navigation have these characteristics:
56-
57-
* Human readable (nonvector) content
58-
59-
* Low cardinality (a small number of distinct values that repeat throughout documents in your search corpus)
60-
61-
* Short descriptive values (one or two words) that render nicely in a navigation tree
62-
63-
The values within a field, and not the field name itself, produce the facets in a faceted navigation structure. If the facet is a string field named *Color*, facets are blue, green, and any other value for that field.
30+
## Faceted navigation in code
6431

65-
You can't use `Edm.GeographyPoint` or `Collection(Edm.GeographyPoint)` fields in faceted navigation. Recall that facets work best on fields with low cardinality. Due to the resolution of geo-coordinates, it's rare that any two sets of coordinates are equal in a given dataset. As such, facets aren't supported for geo-coordinates. You should use a city or region field to facet by location.
32+
Facets are enabled on supported fields in an index, and then specified on a query. At query time, a faceted navigation structure is returned at the top of the response.
6633

67-
As a best practice for performance and storage optimization, turn faceting off for fields that should never be used as a facet. In particular, string fields for unique values, such as an ID or product name, should be set to `"facetable": false` to prevent their accidental (and ineffective) use in faceted navigation. This is especially true for the REST API that enables filters and facets on string fields by default.
68-
69-
In your code, check fields for null values, misspellings or case discrepancies, and single and plural versions of the same word. By default, filters and facets don't undergo lexical analysis or [spell check](speller-how-to-add.md), which means that all values of a "facetable" field are potential facets, even if the words differ by one character. Optionally, you can [assign a normalizer](search-normalizers.md) to a "filterable" and "facetable" field to smooth out variations in casing and characters.
70-
71-
### Defaults in REST and Azure SDKs
72-
73-
If you're using one of the Azure SDKs, your code must explicitly set the "facetable" attribute on a field.
74-
75-
The REST API has defaults for field attributes based on the [data type](/rest/api/searchservice/supported-data-types). The following data types are "filterable" and "facetable" by default:
76-
77-
* `Edm.String` and `Collection(Edm.String)`
78-
* `Edm.DateTimeOffset` and `Collection(Edm.DateTimeOffset)`
79-
* `Edm.Boolean` and`Collection(Edm.Boolean)`
80-
* `Edm.Int32`, `Edm.Int64`, `Edm.Double` and their collection equivalents
81-
82-
## Facet request and response
83-
84-
Facets are specified on the query, and the faceted navigation structure is returned at the top of the response.
85-
86-
The following REST example is an unqualified query (`"search": "*"`) that is scoped to the entire index (see the [built-in hotels sample](search-get-started-portal.md)). Facets are usually a list of fields, but this query shows just one for a more readable response.
34+
The following REST example is an unqualified query (`"search": "*"`) that is scoped to the entire index (see the [built-in hotels sample](search-get-started-portal.md)). It returns a faceted navigation structure for the "Category" field.
8735

8836
```http
8937
POST https://{{service_name}}.search.windows.net/indexes/hotels/docs/search?api-version={{api_version}}
@@ -101,7 +49,7 @@ POST https://{{service_name}}.search.windows.net/indexes/hotels/docs/search?api-
10149

10250
It's useful to initialize a search page with an open query to completely fill in the faceted navigation structure. As soon as you pass query terms in the request, the faceted navigation structure is scoped to just the matches in the results, rather than the entire index.
10351

104-
The response for the example includes the faceted navigation structure at the top. The structure consists of "Category" values and a count of the hotels for each one. It's followed by the rest of the search results, trimmed here for brevity. This example works well for several reasons. The number of facets for this field fall under the limit (default is 10) so all of them appear, and every hotel in the index of 50 hotels is represented in exactly one of these categories.
52+
The response for the example includes the faceted navigation structure at the top. The structure consists of "Category" values and a count of the hotels for each one. It's followed by the rest of the search results, trimmed here to just one document for brevity. This example works well for several reasons. The number of facets for this field fall under the limit (default is 10) so all of them appear, and every hotel in the index of 50 hotels is represented in exactly one of these categories.
10553

10654
```json
10755
{
@@ -153,6 +101,93 @@ The response for the example includes the faceted navigation structure at the to
153101
}
154102
```
155103

104+
## Add facets to an index
105+
106+
Facets are enabled on a field-by-field basis in an index definition when you set `"facetable": true` on field definitions.
107+
108+
Although it's not strictly required, it's a best practice to also set the "filterable" attribute so that you can build the necessary filters that back the faceted navigation experience in your search application.
109+
110+
Here's a JSON example of the hotels sample index, showing "facetable" and "filterable" on low cardinality fields that contain single values or short phrases: "Category", "Tags", "Rating".
111+
112+
```json
113+
{
114+
"name": "hotels",
115+
"fields": [
116+
{ "name": "hotelId", "type": "Edm.String", "key": true, "searchable": false, "sortable": false, "facetable": false },
117+
{ "name": "Description", "type": "Edm.String", "filterable": false, "sortable": false, "facetable": false },
118+
{ "name": "HotelName", "type": "Edm.String", "facetable": false },
119+
{ "name": "Category", "type": "Edm.String", "filterable": true, "facetable": true },
120+
{ "name": "Tags", "type": "Collection(Edm.String)", "filterable": true, "facetable": true },
121+
{ "name": "Rating", "type": "Edm.Int32", "filterable": true, "facetable": true },
122+
{ "name": "Location", "type": "Edm.GeographyPoint" }
123+
]
124+
}
125+
```
126+
127+
### Prerequisites
128+
129+
* A new or existing search index, with plain text fields containing text or numeric content.
130+
131+
You can't set facets on vector fields or fields of type `Edm.GeographyPoint` or `Collection(Edm.GeographyPoint)`.
132+
133+
Facetable must be null for complex fields.
134+
135+
### Choosing fields
136+
137+
Facets can be calculated over single-value fields and collections. Fields that work best in faceted navigation have these characteristics:
138+
139+
* Human readable (nonvector) content.
140+
141+
* Low cardinality (a small number of distinct values that repeat throughout documents in your search corpus).
142+
143+
* Short descriptive values (one or two words) that render nicely in a navigation tree.
144+
145+
Fields of type `Edm.String` that are filterable, sortable, or facetable can be at most 32 kilobytes in length. This is because values of such fields are treated as a single search term, and the maximum length of a term in Azure AI Search is 32 kilobytes. If you need to store more text than this in a single string field, you'll need to explicitly set filterable, sortable, and facetable to false in your index definition.
146+
147+
The values within a field, and not the field name itself, produce the facets in a faceted navigation structure. If the facet is a string field named *Color*, facets are blue, green, and any other value for that field.
148+
149+
For performance and storage optimization, set `"facetable": false` for fields that should never be used as a facet. These include:
150+
151+
* String fields for unique values, such as an ID or product name, to prevent their accidental (and ineffective) use in faceted navigation. This is especially true for the REST API that enables filters and facets on string fields by default.
152+
153+
* Geo-coordinates. You can't use `Edm.GeographyPoint` or `Collection(Edm.GeographyPoint)` fields in faceted navigation. Recall that facets work best on fields with low cardinality. Due to how geo-coordinates resolve, it's rare that any two sets of coordinates are equal in a given dataset. As such, facets aren't supported for geo-coordinates. You should use a city or region field to facet by location.
154+
155+
Setting a field as searchable, filterable, sortable, or facetable has an impact on index size and query performance. Don't set those attributes on fields that aren't meant to be referenced in query expressions.
156+
157+
In your code, check fields for null values, misspellings or case discrepancies, and single and plural versions of the same word. By default, filters and facets don't undergo lexical analysis or [spell check](speller-how-to-add.md), which means that all values of a "facetable" field are potential facets, even if the words differ by one character. Optionally, you can [assign a normalizer](search-normalizers.md) to a "filterable" and "facetable" field to smooth out variations in casing and characters.
158+
159+
### Defaults in REST and Azure SDKs
160+
161+
If you're using one of the Azure SDKs, your code must explicitly set the "facetable" attribute on a field.
162+
163+
The REST API has defaults for field attributes based on the [data type](/rest/api/searchservice/supported-data-types). The following data types are "filterable" and "facetable" by default:
164+
165+
* `Edm.String` and `Collection(Edm.String)`
166+
* `Edm.DateTimeOffset` and `Collection(Edm.DateTimeOffset)`
167+
* `Edm.Boolean` and`Collection(Edm.Boolean)`
168+
* `Edm.Int32`, `Edm.Int64`, `Edm.Double` and their collection equivalents
169+
170+
## Return a facet navigation structure in a query
171+
172+
<!-- facet or facets string Optional. A field to facet by, where the field is attributed as "facetable". When called with GET, facet is a field (facet: field1). When called with POST, this parameter is named facets instead of facet and it's specified as an array (facets: [field1, field2, field3]). The string may contain parameters to customize the faceting, expressed as comma-separated name-value pairs.
173+
174+
Valid parameters are "count", "sort", "values", "interval", and "timeoffset".
175+
176+
"count" is the maximum number of facet terms; default is 10. There's no upper limit on the number of terms, but higher values degrade performance, especially if the faceted field contains a large number of unique terms. For example, "facet=category,count:5" gets the top five categories in facet results. If the count parameter is less than the number of unique terms, the results may not be accurate. This is due to the way faceting queries are distributed across shards. You can set count to zero or to a value that's greater than or equal to the number of unique values in the facetable field to get an accurate count across all shards. The tradeoff is increased latency.
177+
178+
"sort" can be set to "count", "-count", "value", "-value". Use count to sort descending by count. Use -count to sort ascending by count. Use value to sort ascending by value. Use -value to sort descending by value (for example, "facet=category,count:3,sort:count" gets the top three categories in facet results in descending order by the number of documents with each city name). If the top three categories are Budget, Motel, and Luxury, and Budget has 5 hits, Motel has 6, and Luxury has 4, then the buckets are in the order Motel, Budget, Luxury. For -value, "facet=rating,sort:-value" produces buckets for all possible ratings, in descending order by value (for example, if the ratings are from 1 to 5, the buckets are ordered 5, 4, 3, 2, 1, irrespective of how many documents match each rating).
179+
180+
"values" can set to pipe-delimited numeric or Edm.DateTimeOffset values specifying a dynamic set of facet entry values (for example, "facet=baseRate,values:10 | 20" produces three buckets: one for base rate 0 up to but not including 10, one for 10 up to but not including 20, and one for 20 and higher). A string "facet=lastRenovationDate,values:2010-02-01T00:00:00Z" produces two buckets: one for hotels renovated before February 2010, and one for hotels renovated February 1, 2010 or later. The values must be listed in sequential, ascending order to get the expected results.
181+
182+
"interval" is an integer interval greater than 0 for numbers, or minute, hour, day, week, month, quarter, year for date time values. For example, "facet=baseRate,interval:100" produces buckets based on base rate ranges of size 100. If base rates are all between $60 and $600, there will be buckets for 0-100, 100-200, 200-300, 300-400, 400-500, and 500-600. The string "facet=lastRenovationDate,interval:year" produces one bucket for each year when hotels were renovated.
183+
184+
"timeoffset" can be set to ([+-]hh:mm, [+-]hhmm, or [+-]hh). If used, the timeoffset parameter must be combined with the interval option, and only when applied to a field of type Edm.DateTimeOffset. The value specifies the UTC time offset to account for in setting time boundaries. For example: "facet=lastRenovationDate,interval:day,timeoffset:-01:00" uses the day boundary that starts at 01:00:00 UTC (midnight in the target time zone).
185+
186+
count and sort can be combined in the same facet specification, but they can't be combined with interval or values, and interval and values can't be combined together.
187+
188+
Interval facets on date time are computed based on the UTC time if timeoffset isn't specified. For example: for "facet=lastRenovationDate,interval:day", the day boundary starts at 00:00:00 UTC.
189+
-->
190+
156191
## Facets syntax
157192

158193
A facet query parameter is set to a comma-delimited list of "facetable" fields and depending on the data type, can be further parameterized to set counts, sort orders, and ranges: `count:<integer>`, `sort:<>`, `interval:<integer>`, and `values:<list>`. For more detail about facet parameters, see [query parameters in the REST API](/rest/api/searchservice/documents/search-post#searchrequest).
@@ -172,6 +207,10 @@ For Numeric and DateTime values only, you can explicitly set values on the facet
172207

173208
Each range is built using 0 as a starting point, a value from the list as an endpoint, and then trimmed of the previous range to create discrete intervals.
174209

210+
## Tips for working with facets
211+
212+
This section is a collection of tips and workarounds that might be helpful.
213+
175214
### Discrepancies in facet counts
176215

177216
Under certain circumstances, you might find that facet counts aren't fully accurate due to the [sharding architecture](index-similarity-and-scoring.md#sharding-effects-on-query-results). Every search index is spread across multiple shards, and each shard reports the top N facets by document count, which are then combined into a single result. Because it's just the top N facets for each shard, it's possible to miss or under-count matching documents in the facet response.
@@ -180,10 +219,6 @@ To guarantee accuracy, you can artificially inflate the count:\<number> to a lar
180219

181220
The tradeoff with this workaround is increased query latency, so use it only when necessary.
182221

183-
## Tips for working with facets
184-
185-
This section is a collection of tips and workarounds that might be helpful.
186-
187222
### Preserve a facet navigation structure asynchronously of filtered results
188223

189224
In Azure AI Search, facets exist for current results only. However, it's a common application requirement to retain a static set of facets so that the user can navigate in reverse, retracing steps to explore alternative paths through search content.
@@ -192,7 +227,7 @@ If you want a static set of facets alongside a dynamic drilldown experience, you
192227

193228
### Clear facets
194229

195-
When you design the user experience, remember to add a mechanism for clearing facets. A common approach for clearing facets is issue an empty search request to reset the page.
230+
When you design the user experience, remember to add a mechanism for clearing facets. A common approach for clearing facets is issuing an empty search request to reset the page.
196231

197232
### Trim facet results with more filters
198233

0 commit comments

Comments
 (0)