使用 InstantSearch 通过 ID 优化 facets
了解如何在通过唯一标识符优化 facets 的同时,使用 InstantSearch 在 UI 中显示 facet 名称。

在本指南中,我们将深入探讨 Meilisearch 中的 facets 概念,以及如何使用它在按 ID 过滤时显示 facet 名称。
什么是 facets?
Facets 是搜索引擎中使用的一种技术,用于将搜索结果分类为多个类别或“facet”。这些 facets 可以是任何东西,从类别、标签、价格范围,甚至是颜色。这使得用户更容易浏览和过滤结果,提供更精细和高效的搜索体验。
为什么使用 ID 作为 facet 过滤器?
对于大多数应用程序和用户来说,按 facet 名称(例如电影的类型)进行过滤通常就足够且直观。Facet 名称是人类可读的,并清楚地表明了过滤器的作用。但是,在某些用例中,出于以下几个关键原因,最好按 ID 进行过滤
- 不易出错:ID 通常更简单且标准化,使其不易出现可能存在于 facet 名称中的拼写错误或不一致。
- 唯一标识符:在某些数据库中,facet 名称可能会重复出现,但特性略有不同,而 ID 始终是唯一的。这在您拥有名称相似或相同但属性不同的项目的情况下尤其有用。
通过使用 ID 进行过滤,但向用户显示相应的 facet 名称,您可以实现两全其美:效率和可用性。这样,您就可以利用 ID 和名称的优势,使您的应用程序既强大又用户友好。
ID 到名称的挑战
在 Meilisearch 中,facets 是过滤器的一种特殊用例。本质上,您可以将添加到 filterableAttributes
列表中的任何属性用作 facet。当您向搜索查询添加 facet 参数时,Meilisearch 将返回一个 [facetDistribution](https://meilisearch.org.cn/docs/reference/api/search#facetdistribution)
对象。此对象提供在给定 facet 的值之间分布的匹配文档的数量。
"facetDistribution":{ "genres":{ "Classics":6, "Comedy":1, "Coming-of-Age":1, "Fantasy":2, "Fiction":8, … } }
但是,如果您添加到 filterableAttributes 列表的字段是 ID,则 facetDistribution
对象将返回这些 ID。
"facetDistribution":{ "genres":{ "5":6, "6":1, "8":1, "13":2, "16":8, … } }
虽然 ID 非常适合后端操作,但它们在前端不一定用户友好或有意义。这就是为什么您可能希望在用户界面中显示这些 ID 时将它们映射回其相应的 facet 名称。
前端中的 ID 到名称映射
假设您有一个电影数据集,其结构如下所示
{ "id": 5, "title": "Four Rooms", "overview": "It's Ted the Bellhop's first night on the job....", "genres": [ { "id": 7, "name": "Crime" }, { "id": 6, "name": "Comedy" }, ], "release_date": 818467200, }
您想要基于电影类型的 faceted search。因此,您将 genres.id 添加到 filterable attributes 列表。当然,在 UI 中,您希望显示 genres.name,以便用户在 UI 上看到“犯罪”而不是“7”。
让我们深入了解如何使用 InstantSearch 和 instant-meilisearch 来完成此操作。
使用 InstantSearch 和 instant-meilisearch
InstantSearch 是一个用于构建搜索 UI 的开源前端库。Instant-meilisearch 是将 InstantSearch 与 Meilisearch 集成的首选搜索客户端。
要将 instant-meilisearch 与 InstantSearch 一起使用,您需要
1. 导入所需的模块,包括 instantMeiliSearch
import { instantMeiliSearch } from '@meilisearch/instant-meilisearch' import instantsearch from 'instantsearch.js' import { searchBox, infiniteHits,refinementList } from 'instantsearch.js/es/widgets'
2. 使用您的 Meilisearch 主机和搜索 API 密钥建立 Meilisearch 客户端
const searchClient = instantMeiliSearch( 'https://ms-7053a8dd7c09-72.lon.meilisearch.io', 'meilisearchApiKey' )
3. 使用您的 Meilisearch 索引名称和搜索客户端设置 instantsearch
const searchIndex = instantsearch({ indexName: 'movies', searchClient })
在本指南中,我们将使用 InstantSearch 的 refinementList
widget 来将 ID 映射到用户友好的名称。
此 widget 带有一个名为 transformItems
的可选参数。此函数接收 items
(或 facets),并允许您在它们显示在 UI 上之前对其进行转换。它还在其参数中包含完整的 results 数据。
每个 item(或 facet)都包含以下属性
count
:facet 在结果集中出现的次数value
:用于优化的值(在我们的例子中,它将是 genres.id)label
:要显示的标签highlighted
:突出显示的标签。此值显示在默认模板中
正如您所见,highlighted
是 refinementlList
widget 默认使用的标签。
有了这些信息,我们可以使用 transformItems
函数来显示 genres.name
而不是 genres.id
。
transformItems(items, { results }) { // The 'results' parameter contains the full results data return items.map(item => { // Initialize genreName with the existing highlighted label let genreName = item.highlighted; // Loop through results.hits to find a matching genre for (let hit of results.hits) { const matchedGenre = hit.genres.find(genre => genre.id.toString() === item.value); // Update genreName if a match is found if (matchedGenre) { genreName = matchedGenre.name; break; } } // Return the updated item with the new tagName as the highlighted value return { ...item, highlighted: genreName }; }); }
通过此配置,您将有效地将 ID 映射到更用户友好的名称,从而增强用户的搜索体验。
完整代码应如下所示
import { instantMeiliSearch } from '@meilisearch/instant-meilisearch' import instantsearch from 'instantsearch.js' import { searchBox, infiniteHits,refinementList } from 'instantsearch.js/es/widgets' const searchClient = instantMeiliSearch( 'https://ms-7053a8dd7c09-72.lon.meilisearch.io', 'meilisearchApiKey' ) const searchIndex = instantsearch({ indexName: 'movies', searchClient }) const searchBox = instantsearch.widgets.searchBox({ // ... }); const hits = instantsearch.widgets.hits({ // ... }); const refinementList = instantsearch.widgets.refinementList({ container: '#facets', attribute: 'genres.id', transformItems(items, { results }) { return items.map(item => { let genreName = item.highlighted; for (let hit of results.hits) { const matchedGenre = hit.genres.find(genre => genre.id.toString() === item.value); if (matchedGenre) { genreName = matchedGenre.name; break; } } return { ...item, highlighted: genreName }; }); } }) searchIndex.addWidgets([searchBox, infiniteHits, refinementList]); searchIndex.start()
此示例演示了如何使用 vanilla JavaScript 实现 ID 到名称的映射,但您可以使用您喜欢的前端框架实现类似的结果。查看 React 和 Vue 的各自文档。
有关更多 Meilisearch 的信息,您可以订阅我们的新闻资讯。您可以通过查看路线图并参与我们的产品讨论来了解有关我们产品的更多信息。
如有其他任何问题,请加入我们在 Discord 上的开发者社区。