如何在 Azure 认知搜索中生成分面筛选器How to build a facet filter in Azure Cognitive Search

分面导航用于在搜索应用中自定向筛选查询结果,其中应用程序提供 UI 控件以将搜索范围限定到文档组(例如,类别或品牌),Azure 认知搜索提供数据结构以支持体验。Faceted navigation is used for self-directed filtering on query results in a search app, where your application offers UI controls for scoping search to groups of documents (for example, categories or brands), and Azure Cognitive Search provides the data structure to back the experience. 在本文中,可快速了解创建分面导航的基本步骤,支持想要提供的搜索体验。In this article, quickly review the basic steps for creating a faceted navigation structure backing the search experience you want to provide.

  • 选择用于筛选和分面的字段Choose fields for filtering and faceting
  • 设置字段属性Set attributes on the field
  • 生成索引和加载数据Build the index and load data
  • 将分面筛选器添加到查询Add facet filters to a query
  • 处理结果Handle results

分面为动态并在查询中返回。Facets are dynamic and returned on a query. 搜索响应带有用于导航结果的分面类别。Search responses bring with them the facet categories used to navigate the results. 如果不熟悉分面,可通过以下示例了解分面导航结构。If you aren't familiar with facets, the following example is an illustration of a facet navigation structure.

显示搜索对话框的图像,其中包含按业务标题分组的筛选后搜索结果。

不熟悉分面导航并希望了解更多详情?New to faceted navigation and want more detail? 请参阅如何在 Azure 认知搜索中实现分面导航See How to implement faceted navigation in Azure Cognitive Search.

选择字段Choose fields

可通过单值字段以及集合计算分面。Facets can be calculated over single value fields as well as collections. 适合分面导航的字段的多重性较低:搜索语料库(例如,颜色、国家/地区或品牌名列表)的文档中重复的不同值较少。Fields that work best in faceted navigation have low cardinality: a small number of distinct values that repeat throughout documents in your search corpus (for example, a list of colors, countries/regions, or brand names).

通过将 facetable 属性设置为 true,便可在创建索引时逐字段启用分面。Faceting is enabled on a field-by-field basis when you create the index by setting the facetable attribute to true. 通常,对于此类字段,还应该将 filterable 属性设置为 true,使搜索应用程序能够根据最终用户选择的分面,基于这些字段进行筛选。You should generally also set the filterable attribute to true for such fields so that your search application can filter on those fields based on facets that the end user selects.

使用 REST API 创建索引时,可能会在分面导航中使用的任何字段类型默认将被标记为 facetableWhen creating an index using the REST API, any field type that could possibly be used in faceted navigation is marked as facetable by default:

  • Edm.String
  • Edm.DateTimeOffset
  • Edm.Boolean
  • 数字字段类型:Edm.Int32Edm.Int64Edm.DoubleNumeric field types: Edm.Int32, Edm.Int64, Edm.Double
  • 上述类型的集合(例如 Collection(Edm.String)Collection(Edm.Double)Collections of the above types (for example, Collection(Edm.String) or Collection(Edm.Double))

无法在分面导航中的使用 Edm.GeographyPointCollection(Edm.GeographyPoint) 字段。You cannot use Edm.GeographyPoint or Collection(Edm.GeographyPoint) fields in faceted navigation. 分面最适合用于基数较小的字段。Facets work best on fields with low cardinality. 由于地理坐标的精度,给定数据集中两组坐标完全相同的情况很少见。Due to the resolution of geo-coordinates, it is rare that any two sets of co-ordinates will be equal in a given dataset. 因此,地理坐标不支持分面。As such, facets are not supported for geo-coordinates. 需要城市或区域字段才可实现按位置进行分面。You would need a city or region field to facet by location.

设置属性Set attributes

将控制字段使用方式的索引属性添加到索引中的各字段定义。Index attributes that control how a field is used are added to individual field definitions in the index. 在以下示例中,基数较小且适合用于分面的字段包括:category(酒店、汽车旅馆、招待所)、tagsratingIn the following example, fields with low cardinality, useful for faceting, consist of: category (hotel, motel, hostel), tags, and rating. 在以下示例中,为方便演示,已显式为这些字段设置了 filterablefacetable 属性。These fields have the filterable and facetable attributes set explicitly in the following example for illustrative purposes.

提示

为实现最佳性能和存储优化,请针对绝不应用作分面的字段关闭分面功能。As a best practice for performance and storage optimization, turn faceting off for fields that should never be used as a facet. 具体而言,应将唯一值的字符串字段(例如 ID 或产品名称)设置为 "facetable": false,以避免在分面导航中意外(和无效)使用它们。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.

{
  "name": "hotels",  
  "fields": [
    { "name": "hotelId", "type": "Edm.String", "key": true, "searchable": false, "sortable": false, "facetable": false },
    { "name": "baseRate", "type": "Edm.Double" },
    { "name": "description", "type": "Edm.String", "filterable": false, "sortable": false, "facetable": false },
    { "name": "description_fr", "type": "Edm.String", "filterable": false, "sortable": false, "facetable": false, "analyzer": "fr.lucene" },
    { "name": "hotelName", "type": "Edm.String", "facetable": false },
    { "name": "category", "type": "Edm.String", "filterable": true, "facetable": true },
    { "name": "tags", "type": "Collection(Edm.String)", "filterable": true, "facetable": true },
    { "name": "parkingIncluded", "type": "Edm.Boolean",  "filterable": true, "facetable": true, "sortable": false },
    { "name": "smokingAllowed", "type": "Edm.Boolean", "filterable": true, "facetable": true, "sortable": false },
    { "name": "lastRenovationDate", "type": "Edm.DateTimeOffset" },
    { "name": "rating", "type": "Edm.Int32", "filterable": true, "facetable": true },
    { "name": "location", "type": "Edm.GeographyPoint" }
  ]
}

备注

此索引定义复制自使用 REST API 创建 Azure 认知搜索索引This index definition is copied from Create an Azure Cognitive Search index using the REST API. 除了字段定义的表面差异外,二者完全相同。It is identical except for superficial differences in the field definitions. 已在 categorytagsparkingIncludedsmokingAllowedrating 字段中显式添加 filterablefacetable 属性。The filterable and facetable attributes are explicitly added on category, tags, parkingIncluded, smokingAllowed, and rating fields. 在实践中,使用 REST API 时,默认会在这些字段中启用 filterablefacetableIn practice, filterable and facetable would be enabled by default on these fields when using the REST API. 使用 .NET SDK 时,必须显式启用这些属性。When using the .NET SDK, these attributes must be enabled explicitly.

生成和加载索引Build and load an index

编写查询之前的一个中间步骤(也许是众所周知的步骤)是生成并填充索引An intermediate (and perhaps obvious) step is that you have to build and populate the index before formulating a query. 为了保持内容完整,此处阐述了此步骤。We mention this step here for completeness. 确定索引是否可用的一种方法是在门户中查看索引列表。One way to determine whether the index is available is by checking the indexes list in the portal.

将分面筛选器添加到查询Add facet filters to a query

在应用程序代码中构造一个查询,该查询指定有效查询的所有部分,包括搜索表达式、分面、筛选器、计分配置文件 - 所有这些内容都用于明确表述请求。In application code, construct a query that specifies all parts of a valid query, including search expressions, facets, filters, scoring profiles– anything used to formulate a request. 以下示例基于住宿、分级及其他便利设施类型来生成创建分面导航的请求。The following example builds a request that creates facet navigation based on the type of accommodation, rating, and other amenities.

var sp = new SearchParameters()
{
    ...
    // Add facets
    Facets = new[] { "category", "rating", "parkingIncluded", "smokingAllowed" }.ToList()
};

针对单击事件返回筛选结果Return filtered results on click events

当最终用户单击某个分面值时,单击事件的处理程序应使用筛选表达式来实现用户的意图。When the end user clicks on a facet value, the handler for the click event should use a filter expression to realize the user's intent. 假设分面依据为 category,通过 $filter 表达式单击类别“汽车旅馆”,表示选择属于该类型的住宿。Given a category facet, clicking the category "motel" is implemented with a $filter expression that selects accommodations of that type. 当用户单击“汽车旅馆”,指示应仅显示汽车旅馆时,应用程序发送的下一条查询将包括 $filter=category eq 'motel'When a user clicks "motel" to indicate that only motels should be shown, the next query the application sends includes $filter=category eq 'motel'.

下面的代码片段添加类别以筛选用户是否从类别分面中选择某个值。The following code snippet adds category to the filter if a user selects a value from the category facet.

if (!String.IsNullOrEmpty(categoryFacet))
    filter = $"category eq '{categoryFacet}'";

如果用户单击 tags 等集合字段的分面值(例如值“pool”),则应用程序应使用以下筛选语法:$filter=tags/any(t: t eq 'pool')If the user clicks on a facet value for a collection field like tags, for example the value "pool", your application should use the following filter syntax: $filter=tags/any(t: t eq 'pool')

提示和解决方法Tips and workarounds

就地使用分面初始化页面Initialize a page with facets in place

如果希望就地使用分面初始化页面,可在页面初始化过程中发送查询,以借助初始分面结构设定页面的种子。If you want to initialize a page with facets in place, you can send a query as part of page initialization to seed the page with an initial facet structure.

以异步方式保留筛选结果的分面导航结构Preserve a facet navigation structure asynchronously of filtered results

在 Azure 认知搜索中使用分面导航所面临的一个难题是,分面仅针对当前结果存在。One of the challenges with facet navigation in Azure Cognitive Search is that facets exist for current results only. 在实践中,通常会保留一组静态分面,便于用户按相反顺序进行导航,回顾步骤以通过搜索内容了解可选路径。In practice, it's common to retain a static set of facets so that the user can navigate in reverse, retracing steps to explore alternative paths through search content.

虽然这是常见的用例,但并不是分面导航结构当前提供的现成内容。Although this is a common use case, it's not something the facet navigation structure currently provides out-of-the-box. 通过发布 2 个筛选查询(一个将范围限定到结果,另一个用于创建针对导航的静态分面列表),需要静态分面的开发人员通常便可解决限制问题。Developers who want static facets typically work around the limitation by issuing two filtered queries: one scoped to the results, the other used to create a static list of facets for navigation purposes.

另请参阅See also