如何使用第二个索引实现文档提升
了解如何使用第二个索引实现 Meilisearch 中的推广搜索结果,用于置顶文档。

在本指南中,我们将引导您了解如何在 Meilisearch 中实现推广搜索结果。我们的目标是当特定关键词与用户查询匹配时,优先显示特定文档。这些提升的文档应在搜索结果的顶部返回。
本指南介绍了如何在后端实现推广文档。对于前端优先的实现,请参阅使用 React InstantSearch 实现推广搜索结果
概述
以下是使用第二个索引实现“置顶文档”的文档提升以及 多重搜索功能的简化细分
- 创建索引: 设置两个索引:一个用于常规搜索,一个用于提升结果。提升索引将有一个特殊属性
keywords
,以触发提升。 - 填充 'games' 索引: 使用提供的 JSON 文件填充
games
索引,并使用您的数据集。此索引将作为我们提升文档的来源。 - 配置 'pinned_games' 索引: 配置
pinned_games
索引以显示属性,但不会显示关键词。相应地调整可搜索属性和显示属性。 - 提升文档: 识别您想要提升的文档,并为其分配相关关键词。例如,您可以将关键词
fps
和shooter
分配给游戏Counter-Strike
。 - 实现多重搜索: 利用 Meilisearch 的多重搜索功能,在常规索引和提升索引中执行搜索查询。这样,匹配关键词的提升文档将首先出现。
- 显示结果: 以用户友好的格式展示搜索结果,并用视觉指示器突出显示提升的文档。
实现
安装
在深入了解之前,请确保您的 Meilisearch 已启动并运行。如果您尚未安装,请按照以下步骤操作
- 启动一个 Meilisearch 实例 — 您可以在 本地运行 Meilisearch 或通过 Meilisearch Cloud。
- 确保您已安装您喜欢的 语言 SDK(或框架集成)。
本指南使用 Python SDK,但它与任何其他 Meilisearch 集成的工作方式相同。🎉
初始化索引
在我们的示例中,我们将使用 Steam 游戏数据集。您可以将此过程适应您自己的数据
- 下载
steam-games.json
和settings.json
文件,用于我们的 Steam 游戏数据集 - 通过从
steam-games.json
文件添加文档将数据集加载到您的 Meilisearch 实例中。
games
索引
import meilisearch import json from typing import Callable client = meilisearch.Client(url="http://localhost:7700") games = client.index("games") # helper to wait for Meilisearch tasks def wait_with_progress(client: meilisearch.Client, task_uid: int): while True: try: client.wait_for_task(task_uid, timeout_in_ms=1000) break except meilisearch.errors.MeilisearchTimeoutError: print(".", end="") task = client.get_task(task_uid) print(f" {task.status}") if task.error is not None: print(f"{task.error}") print("Adding settings...", end="") with open("settings.json") as settings_file: settings = json.load(settings_file) task = games.update_settings(settings) wait_with_progress(client, task.task_uid) with open("steam-games.json") as documents_file: documents = json.load(documents_file) task = games.add_documents_json(documents) print("Adding documents...", end="") wait_with_progress(client, task.task_uid)
pinned_games
索引
此索引将包含推广文档。pinned_games
索引的设置与 games
索引相同,但有以下差异
- 唯一的
searchableAttributes
是keywords
属性,包含触发置顶该文档的词。 displayedAttributes
是文档的所有属性,除了keywords
(我们不希望向最终用户显示关键词)
pinned = client.index("pinned_games") print("Adding settings...", end="") with open("settings.json") as settings_file: settings = json.load(settings_file) settings["searchableAttributes"] = ["keywords"] # all but "keywords" settings["displayedAttributes"] = ["name", "description", "id", "price", "image", "releaseDate", "recommendationCount", "platforms", "players", "genres", "misc"] task = pinned.update_settings(settings) # see `wait_with_progress` implementation in previous code sample wait_with_progress(client, task.task_uid)
更新推广文档索引
现在我们将从 games
索引中选择我们想要推广的文档来填充索引。
例如,假设我们想将游戏 "Counter-Strike"
置顶到 "fps"
和 "first", "person", "shooter"
关键词。
counter_strike = games.get_document(document_id=10) counter_strike.keywords = ["fps", "first", "person", "shooter"] print("Adding pinned document...", end="") task = pinned.add_documents(dict(counter_strike)) wait_with_progress(client, task.task_uid)
自定义搜索结果
现在,我们来创建一个函数,返回包含置顶文档的搜索结果。
from copy import deepcopy from typing import Any, Dict, List from dataclasses import dataclass @dataclass class SearchResults: pinned: List[Dict[str, Any]] regular: List[Dict[str, Any]] def search_with_pinned(client: meilisearch.Client, query: Dict[str, Any]) -> SearchResults: pinned_query = deepcopy(query) pinned_query["indexUid"] = "pinned_games" regular_query = deepcopy(query) regular_query["indexUid"] = "games" results = client.multi_search([pinned_query, regular_query]) # fetch the limit that was passed to each query so that we can respect that value when getting the results from each source limit = results["results"][0]["limit"] # fetch as many results from the pinned source as possible pinned_results = results["results"][0]["hits"] # only fetch results from the regular source up to limit regular_results = results["results"][1]["hits"][:(limit-len(pinned_results))] return SearchResults(pinned=pinned_results, regular=regular_results)
我们可以使用此函数来检索包含推广文档的搜索结果
results = search_with_pinned(client, {"q": "first person shoot", "attributesToRetrieve": ["name"]})
results
对象应如下所示
SearchResults(pinned=[{'name': 'Counter-Strike'}], regular=[{'name': 'Rogue Shooter: The FPS Roguelike'}, {'name': 'Rocket Shooter'}, {'name': 'Masked Shooters 2'}, {'name': 'Alpha Decay'}, {'name': 'Red Trigger'}, {'name': 'RAGE'}, {'name': 'BRINK'}, {'name': 'Voice of Pripyat'}, {'name': 'HAWKEN'}, {'name': 'Ziggurat'}, {'name': 'Dirty Bomb'}, {'name': 'Gunscape'}, {'name': 'Descent: Underground'}, {'name': 'Putrefaction'}, {'name': 'Killing Room'}, {'name': 'Hard Reset Redux'}, {'name': 'Bunny Hop League'}, {'name': 'Kimulator : Fight for your destiny'}, {'name': 'Intrude'}])
瞧 🎉 您现在有了一个搜索结果对象,其中包含两个数组:推广结果和“常规”结果。
遇到问题?请随时在我们的 Discord 社区寻求帮助。
更进一步
本教程探讨了一种实现推广结果的方法。另一种技术是在前端实现文档置顶;请查看我们的 React 实现指南。这种不同的方法具有与 InstantSearch 兼容的优点。
这两种技术都可以达到相似的结果。我们还计划将 推广文档集成到 Meilisearch 引擎中。请通过上述链接提供您的反馈,以帮助我们确定其优先级。
要了解更多 Meilisearch 相关内容,您可以订阅我们的 时事通讯。您可以通过查看我们的 路线图 并参与我们的 产品讨论来了解更多关于我们的产品。
对于其他任何事情,请加入我们的开发者社区 Discord。
祝好!