想要更好地控制您的搜索设置?了解我们的灵活的基础设施定价

回到主页Meilisearch 的标志
返回文章

使用 InstantSearch 通过 ID 优化分面

了解如何在 InstantSearch 中根据唯一标识符细化分面,同时在 UI 中显示分面名称。

2023 年 9 月 14 日阅读时长 4 分钟
Carolina Ferreira
Carolina FerreiraMeilisearch 开发者布道师@CarolainFG
Refining facets by ID with InstantSearch

在本指南中,我们将深入探讨 Meilisearch 中的分面概念,以及如何使用它在通过 ID 过滤时显示分面名称。

什么是分面?

分面是一种在搜索引擎中使用的技术,用于将搜索结果分类到多个类别或“分面”中。这些分面可以是任何东西,从类别、标签、价格范围,甚至颜色。这使得用户更容易浏览和筛选结果,提供更精细和高效的搜索体验。

为什么使用 ID 作为分面过滤器?

对于大多数应用程序和用户来说,按分面名称(例如电影类型)进行过滤通常是足够且直观的。分面名称易于人类阅读,并清楚地说明了过滤器的作用。然而,在某些用例中,出于以下几个关键原因,首选按 ID 进行过滤:

  • 更不容易出错:ID 通常更简单且标准化,因此不易受分面名称中可能存在的拼写错误或不一致的影响。
  • 唯一标识符:在某些数据库中,分面名称可能会重复出现,但具有略微不同的特性,而 ID 始终是唯一的。这在您拥有名称相似或相同但属性不同的项目时特别有用。

通过使用 ID 进行过滤,但向用户显示相应的分面名称,您可以实现两全其美:效率和可用性。通过这种方式,您可以利用 ID 和名称的优点,使您的应用程序既健壮又用户友好。

ID 到名称的挑战

在 Meilisearch 中,分面是过滤器的一种特殊用例。本质上,您可以将添加到 filterableAttributes 列表中的任何属性用作分面。当您向搜索查询添加分面参数时,Meilisearch 将返回一个 [facetDistribution](https://meilisearch.com.cn/docs/reference/api/search#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 添加到 可过滤属性 列表。当然,在 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 上之前对其进行转换。它还在其参数中包含完整的 results 数据。

每个项目(或分面)都包含以下属性:

  • count:分面在结果集中的出现次数
  • value:用于细化的值(在我们的例子中是 genres.id)
  • label:要显示的标签
  • highlighted:突出显示的标签。此值显示在默认模板中

如您所见,highlightedrefinementlList 小部件中默认使用的标签。

有了这些信息,我们可以使用 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 到名称的映射,但您可以使用您偏好的前端框架实现类似的结果。请查看 ReactVue 的相应文档。

要了解更多 Meilisearch 相关信息,您可以订阅我们的 时事通讯。您可以通过查看 路线图 并参与我们的 产品讨论 来了解更多关于我们产品的信息。

如有其他任何疑问,请加入我们的开发者社区 Discord

Meilisearch indexes embeddings 7x faster with binary quantization

Meilisearch 使用二值量化将嵌入索引速度提高7倍

通过在向量存储 Arroy 中实现二值量化,显著减少了大型嵌入的磁盘空间使用和索引时间,同时保持了搜索相关性和效率。

Tamo
Tamo2024年11月29日
How to add AI-powered search to a React app

如何向 React 应用添加 AI 驱动的搜索

使用 Meilisearch 的 AI 驱动搜索构建 React 电影搜索和推荐应用。

Carolina Ferreira
卡罗莱纳·费雷拉2024年9月24日
Meilisearch is too slow

Meilisearch 太慢了

在这篇博文中,我们探讨了 Meilisearch 文档索引器所需的增强功能。我们将讨论当前的索引引擎、其缺点以及优化性能的新技术。

Clément Renault
克莱门特·雷诺2024年8月20日
© . This site is unofficial and not affiliated with Meilisearch.