在本指南中,我们将深入探讨 Meilisearch 中的分面概念,以及如何使用它在按 ID 过滤时显示分面名称。
什么是分面?
分面 是搜索引擎中使用的一种技术,用于将搜索结果分类为多个类别或“分面”。这些分面可以是任何东西,从类别、标签、价格范围到颜色。这使得用户更容易浏览和过滤结果,从而提供更加完善和高效的搜索体验。
为什么要使用 ID 作为分面过滤器?
对于大多数应用程序和用户而言,按分面名称(例如电影类型)进行过滤通常就足够且直观。分面名称对人类来说是可读的,并能清楚地说明过滤器的作用。但是,在某些情况下,由于以下几个关键原因,更倾向于按 ID 过滤
- 不太容易出错:ID 通常更简单且标准化,使其不太容易出现分面名称中可能存在的拼写错误或不一致性。
- 唯一标识符:在某些数据库中,分面名称可能会重复,但其特征略有不同,而 ID 始终是唯一的。这在您拥有名称相似或相同但属性不同的项目时尤其有用。
通过使用 ID 进行过滤,但向用户显示相应的分面名称,您可以同时获得效率和可用性的优势。这样,您就可以利用 ID 和名称的优势,使您的应用程序既健壮又易于使用。
ID 到名称的挑战
在 Meilisearch 中,分面是过滤器的专用用例。从本质上讲,您可以使用添加到 filterableAttributes
列表中的任何属性作为分面。当您在搜索查询中添加分面参数时,Meilisearch 将返回一个 facetDistribution
对象。此对象提供了分布在给定分面的值之间的匹配文档数量。
"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 显示在用户界面中时,将它们映射回其相应的分面名称。
前端中的 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,
}
您希望基于电影类型进行分面搜索。因此,您将 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
小部件将 ID 映射到用户友好的名称。
此小部件带有一个可选参数,名为 transformItems
。此函数接收 items
(或分面),并允许您在它们显示在 UI 上之前对其进行转换。它还包含其参数中的完整结果数据。
每个项目(或分面)都包含以下属性
count
:分面在结果集中出现的次数value
:用于精炼的值(在本例中,它将是 genres.id)label
:要显示的标签highlighted
:突出显示的标签。此值将显示在默认模板中
如您所见,highlighted
是 refinementlList
小部件默认使用的标签。
有了这些信息,我们可以使用 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()
此示例展示了如何使用 原生 JavaScript 实现 ID 到名称的映射,但您可以使用您喜欢的任何前端框架来实现类似的结果。查看 React 和 Vue 的相关文档。
有关 Meilisearch 的更多信息,您可以订阅我们的 新闻稿。您可以通过查看 路线图 并参与我们的 产品讨论 来详细了解我们的产品。
其他任何问题,请加入我们在 Discord 上的开发者社区。