如何在你的 React 应用中实现即时搜索
了解如何借助 Meilisearch 和 React 的强大功能,轻松创建具有即时可靠结果的基于搜索的 Web 应用程序。
这篇文章最初由客座作者 Riccardo Giorato 于 2020 年 5 月发布,当时 Meilisearch 的版本为 v0.09。这篇文章已由 Carolina Ferreira 更新,以配合 Meilisearch v1 使用。您可以在 GitHub 上找到该文章的第一个版本。
简介
在本教程中,您将学习如何借助 Meilisearch 的强大功能,轻松创建具有即时可靠结果的基于搜索的 Web 应用程序。
我们将介绍将数据添加到 Meilisearch、创建自定义前端搜索以及最后进行自定义的基本步骤。
在本教程中,我们将为某个运动品牌创建一个即时搜索体验。以下是您将要构建的内容的预览
前提条件
在开始之前,请确保您的机器上已安装 Node.js >= 18。
您可以按照本教程逐步编写代码,使用这个 GitHub 项目。
最后,本教程假设您已经熟悉 React。如果不是这种情况,您可以查看 React 文档以了解更多信息。
入门
克隆存储库
使用以下命令克隆 GitHub 存储库
git clone https://github.com/meilisearch/tutorials.git cd src/react-decathlon
运行新的 Docker 镜像
如果您克隆了存储库以设置 Meilisearch 实例,只需在主文件夹中执行以下命令
npm install npm run setup_meili
如果您没有克隆存储库并想使用 Docker 启动 Meilisearch,请执行此命令
docker run -it --rm -p 7700:7700 -e MEILI_ENV='development' -v $(pwd)/meili_data:/meili_data getmeili/meilisearch:v1.0
默认情况下,Meilisearch 的 API 是不受保护的。在生产环境中,您将需要一个主密钥。您可以在我们的文档中了解更多相关信息。
您可以通过访问:https://127.0.0.1:7700/ 来检查 Meilisearch 是否正在运行
想避免本地安装?为了快速创建一流的搜索体验,我们提供了 Meilisearch Cloud 的便利,它是 Meilisearch 的托管和完全管理版本。有 14 天的免费试用期,无需信用卡 😉
在 Meilisearch 中创建索引
索引是存储文档的实体,就像一个包含某些特定设置的数组对象,以及一个唯一的 主键。
索引的每个文档都必须有一个主字段,这是一个必须存在于所有文档中的特殊字段。该字段保存文档的唯一值:它的 ID。
Meilisearch 可以从您的数据集中推断出主键,前提是它包含 id
子字符串。您也可以显式设置它。
以下是要添加到 Meilisearch 的示例文档。
{ "id": 100013768717, "name": "Fitness Foldable Shoe Bag", "url": "https://www.decathlon.com/products/gym-foldable-shoe-bag", "vendor": "Domyos", "category": "Sport bag", "tags": [ "Artistic Gymnastics", "Boy's", "CARDIO_FITNESS_ACCESSORIES", "CARDIO_FITNESS_BAGS", "CODE_R3: 11782" ], "images": "https://cdn.shopify.com/s/files/1/1330/6287/products/sac_20a_20chaussure_20kaki_20_7C_20001_20_7C_20PSHOT_20_490180e6-44e4-4340-8e3d-c29eb70c6ac8.jpg?v=1584683232", "creation_date": "2020-04-03T15:58:48-07:00", "price": "2.49" }
您可以使用 REST 客户端(如 Postman)轻松创建此索引,但在本教程中,我们将使用 Meilisearch Javascript SDK 直接从 Node.js 执行此操作。
const { MeiliSearch } = require('meilisearch') ;(async () => { try { const config = { host: 'https://127.0.0.1:7700' }; const meili = new MeiliSearch(config); await meili.createIndex('decathlon'); // or you can set the primary key explicitly: // await meili.createIndex({ uid: "decathlon", primaryKey: "id" }); } catch (e) { console.error(e); console.log("Meili error: ", e.message); } })();
您可以在 Meilisearch 文档中了解有关索引属性的更多信息。
索引文档
Meilisearch 接收 JSON 格式的文档并存储它们以用于搜索目的。这些文档由可以保存任何类型数据的字段组成。Meilisearch 还接受以下格式的数据集:NDJSON 和 CSV。您可以在文档中阅读有关该格式的更多信息。
在本教程中,您可以下载此包含运动服装商品的完整数据集:decathlon.json
使用以下脚本将此 JSON 文件中的所有对象上传到 Meilisearch。请记住在运行之前更改 JSON 文件的路径!
const { MeiliSearch } = require('meilisearch') ;(async () => { try { const config = { host: 'https://127.0.0.1:7700' }; const meili = new MeiliSearch(config); const decathlon = require("../decathlon.json"); // path to json file const index = meili.index("decathlon"); await index.addDocuments(decathlon); } catch (e) { console.error(e); console.log("Meili error: ", e.message); } })();
准备 React 应用
我们将需要一个标准的 React 应用。您可以使用您之前在入门部分中克隆的项目。
如果您更喜欢从一个空的应用开始,您可以使用 Create React App 使用以下命令创建自己的应用。您可以随意命名该应用。
npx create-react-app meili_react_demo cd meili_react_demo
包含 Tailwind CSS
为了加快样式设置过程,请直接将 Tailwind CSS 样式添加到 index.html 文件的 <head>
元素中
<script src="https://cdn.tailwindcss.com"></script>
配置 App.js 状态
然后,使用此代码修改 App.js
文件,以设置一个简单的搜索表单和一些状态变量来处理搜索的各个方面。
import React, { useState, useEffect } from 'react' import { MeiliSearch } from 'meilisearch' import Item from './components/Item' // TODO configure the MeiliSearch Client const index = client.index('decathlon') function App () { const [searchedWord, setSearch] = useState('') const [resultSearch, setResults] = useState([]) // TODO add function to send searchedWord to Meilisearch return ( <div className='mx-auto'> <div className='header font-sans text-white items-center justify-center'> <header className='py-12'> <img className='h-20 w-auto items-center justify-center p-2 mx-auto' src='/wide_logo.png' style={{ filter: 'invert(0%)' }} alt='Decathlon logo' /> <h1 className='flex flex-wrap flex-grow text-3xl w-full justify-center p-4'> Stop looking for an item — find it and work hard! </h1> <div className='border rounded overflow-hidden w-full flex justify-center mx-auto searchBox mt-6'> <button className='flex items-center justify-center px-4 shadow-md bg-white text-black'> <svg className='h-4 w-4 text-grey-dark' fill='currentColor' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' > <path d='M16.32 14.9l5.39 5.4a1 1 0 0 1-1.42 1.4l-5.38-5.38a8 8 0 1 1 1.41-1.41zM10 16a6 6 0 1 0 0-12 6 6 0 0 0 0 12z' /> </svg> </button> <input type='text' value={searchedWord} onChange={(event) => setSearch(event.target.value)} className='px-6 py-4 w-full text-black' placeholder='Product, sport, color, …' /> </div> </header> </div> <div> <div className='flex flex-wrap searchResults'> // TODO iterate over the search results to display them with the Item component </div> </div> </div> ) } export default App
此代码应输出带有搜索表单的精美标题。
React 中的搜索结果
使用 Javascript SDK 将 React 与 Meilisearch 连接起来是一个简单的操作,只需几个步骤即可完成。
Meilisearch 客户端
使用以下命令安装 Meilisearch SDK
// if you use npm npm install meilisearch // if you use yarn yarn add meilisearch
使用服务器 URL 设置 Meilisearch 客户端。在我们的例子中,它是 localhost Docker 机器。最后,从后端加载正确的索引。
将 App.js
中的此注释替换为以下代码片段
"// TODO 配置 Meilisearch 客户端"
import { MeiliSearch } from "meilisearch"; const client = new MeiliSearch({ host: "https://127.0.0.1:7700/", }); const index = client.index("decathlon");
发送搜索查询
添加一个 useEffect
钩子来执行在 Meilisearch 中键入的单词的搜索。所有结果都将设置为一个名为 resultsSearch
的简单状态变量。
将 App.js
中的此注释替换为以下代码片段
"// TODO 添加函数以将 searchedWord 发送到 Meilisearch"
useEffect(() => { // Create a scoped async function in the hook async function searchWithMeili() { const search = await index.search(searchedWord); setResults(search.hits); } // Execute the created function directly searchWithMeili(); }, [searchedWord]);
展示结果
您将迭代 Meilisearch 返回的 JSON 对象 — 它们的结构将与上传的 JSON 对象相同 — 并且您将在一个 Item
组件中显示它们,链接到产品页面。
让我们创建 Item
组件,它将帮助我们显示产品。创建一个组件文件夹,其中包含一个 Item.js
文件,然后复制粘贴以下代码片段
function Item ({ url, image, name, category, vendor, price, id }) { return ( <div className='flex w-full sm:w-1/2 md:w-1/3 lg:w-1/4 xl:w-1/6 p-3' key={id}> <a className='flex-1 rounded overflow-hidden shadow-lg' href={url}> <img className='w-full h-48 object-cover' src={image} alt={name} onError={(e) => { e.target.onerror = null e.target.src = '/wide_logo.png' }} /> <div className='px-6 py-3'> <div className='font-bold text-sm mb-1 text-gray-600 capitalize'> {category} </div> {name} <div className='font-bold text-xl mb-2 text-gray-800'> {vendor} - </div> <p className='text-black text-xl font-bold text-base py-2'> $ {price} </p> </div> </a> </div> ) } export default Item
然后,将 App.js
中的此注释替换为以下代码片段
{resultSearch?.map((result) => ( <Item url={result.url} image={result.images} name={result.name} category={result.category} vendor={result.vendor} price={result.price} key={result.id} /> ))}
您可以在 GitHub 上查看完整代码。
配置搜索
使用 Meilisearch,您可以获得大量的自定义选项来微调您的搜索体验。我们将在这里查看一些功能。您可以在 文档中了解更多相关信息。
搜索排名
我们将从更改排名规则开始,Meilisearch 使用该规则对您在进行搜索查询时上传的文档进行排序。排名规则的顺序会影响搜索结果的相关性。您可以在 文档中了解更多相关信息。
让我们使用以下顺序
[ "words", "typo", "proximity", "attribute", "sort", "exactness", “creation_date:desc” ]
这使用默认顺序以及自定义规则:creation_date
。如果所有先前的值都相同,则此规则会按创建日期对项目进行排名。
可搜索属性
您还可以配置 可搜索属性。这些是 Meilisearch 搜索其值的属性,以匹配查询词。默认情况下,所有属性都是可搜索的,但是您可以将其配置为仅搜索 name
、vendor
、category
和 tags
字段,而忽略 images
和 URL
searchableAttributes: ["name", "vendor", "category", "tags"]
显示的属性
显示的属性是 Meilisearch 可以使用 displayedAttributes 数组在前端应用程序中返回给用户的属性。与可搜索属性一样,默认情况下会显示所有属性。
"displayedAttributes": [ "name", "vendor", "category", "tags", "images", "url" ]
将新设置上传到 Meilisearch
现在是使用上面解释的搜索设置自定义 Meilisearch 索引的时候了。
const { MeiliSearch } = require('meilisearch') ;(async () => { try { const config = { host: 'https://127.0.0.1:7700' }; const meili = new MeiliSearch(config); const index = meili.index("decathlon"); const newSettings = { rankingRules: [ "words", "typo", "proximity", "attribute", "sort", "exactness", "creation_date:desc" ], searchableAttributes: ["name", "vendor", "category", "tags"], displayedAttributes: [ "name", "vendor", "category", "tags", "images", "url" ] }; await index.updateSettings(newSettings); } catch (e) { console.error(e); console.log("Meili error: ", e.message); } })();
结论
如果没有一个令人难以置信的团队夜以继日地为这个伟大的项目工作,这个快速搜索是不可能实现的!如果您喜欢为 Meilisearch 大家庭做贡献,请查看以下存储库
订阅我们的新闻邮件,即可每月直接在您的收件箱中收到这些更新。
如需了解更多关于 Meilisearch 的信息,请加入我们在Discord上的开发者社区。您可以通过查看路线图并参与产品讨论来了解更多关于该产品的信息。