👉
这篇文章最初由客座作者Riccardo Giorato 于 2020 年 5 月发布。当时,Meilisearch 处于 v0.09 版本。它已由Carolina Ferreira 更新,以与 Meilisearch v1 兼容。您可以在GitHub 上找到这篇文章的第一个版本。

介绍

在本教程中,您将学习如何轻松创建基于搜索的 Web 应用程序,并利用 Meilisearch 的强大功能获得即时可靠的结果。

我们将介绍将数据添加到 Meilisearch 的基本步骤,创建自定义前端搜索,并在最后进行自定义。

在本教程中,我们将为一个运动品牌创建即时搜索体验。以下是您将构建的内容的预览

Searching for multiple items on the decathlon demo using 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 是不受保护的。在生产环境中,您将需要一个主密钥。您可以在我们的文档中了解有关它的更多信息。

您可以通过访问以下地址来检查 Meilisearch 是否正在运行: http://localhost:7700/

想要避免本地安装?为了快速创建一流的搜索体验,我们提供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"
}

您可以使用像 Postman 这样的 REST 客户端轻松创建此索引,但在本教程中,我们将使用 Meilisearch Javascript SDK 直接从 Node.js 执行操作。

const { MeiliSearch } = require('meilisearch')

;(async () => {
  try {
    const config = {
      host: 'http://localhost: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: 'http://localhost: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

此代码应该输出一个带搜索表单的漂亮标题。

Decathlon header with a search bar

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: "http://localhost: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 组件,它将帮助我们显示我们的产品。在 components 文件夹中创建一个 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 在其中搜索与查询词匹配的值的属性。默认情况下,所有属性都是可搜索的,但您可以对其进行配置,使其仅搜索 namevendorcategorytags 字段,而忽略 imagesURL

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: 'http://localhost: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。您可以通过查看路线图 并参与产品讨论 来了解更多关于该产品的信息。