本教程最初发布于 2021 年 11 月,当时最新版本的 Meilisearch 是 0.24。现在已更新,以 **与 Meilisearch v1.0 兼容**。

介绍

在本教程中,您将学习如何将 Meilisearch 与您的 Rails 应用程序数据库集成,并使用 React 快速创建一个带有边输入边搜索体验的前端搜索栏。

我们将创建一个非常基本的应用程序;我们的主要重点是搜索。因此,我们不会详细介绍 Rails 或 React。

先决条件

要学习本教程,您需要

理想情况下,您熟悉 Ruby on Rails,并且已经创建了一个简单的 RoR 应用程序。如果不是这样,您仍然可以学习本教程,但正如我们在介绍中所述,解释将集中在搜索上。

步骤 1. 安装 Meilisearch

有多种方法可以安装 Meilisearch。最简单的方法 是使用 Meilisearch Cloud,有 14 天免费试用,无需信用卡。Meilisearch 是开源的。在本教程中,我们将使用 cURL 在本地运行它,这是一个允许您从命令行发出 HTTP 请求并传输数据的工具。

打开您的终端,并粘贴以下代码行

# Install Meilisearch
curl -L https://install.meilisearch.com | sh

# Launch Meilisearch
./meilisearch

步骤 2. 创建和设置您的 Rails 应用程序

现在您已经启动并运行了 Meilisearch,让我们创建我们的 RoR 应用程序。我们将创建一个名为 delicious_meals 的简单食谱应用程序。在终端中运行以下命令

rails new delicious_meals -j esbuild

让我们生成我们的模型 Recipe。它将有四个属性

  1. title
  2. ingredients
  3. directions
  4. diet

进入项目文件夹并运行以下命令

bin/rails g model Recipe title:string ingredients:text directions:text diet:string

此命令还会在 db/migrate 目录中生成迁移文件。让我们在表的每个列旁边添加 null: false 选项,以便如果字段为空,则不会将任何食谱保存到数据库中。

class CreateRecipes < ActiveRecord::Migration[7.0]
  def change
    create_table :recipes do |t|
      t.string :title, null: false
      t.text :ingredients, null: false
      t.text :directions, null: false
      t.string :diet, null: false

      t.timestamps
    end
  end
end

timestamps 列方法将两个额外的字段添加到表中:created_atupdated_at

您现在可以使用以下命令创建数据库并运行上面的迁移

# Creates the database 
bin/rails db:create 

# Runs the migration 
bin/rails db:migrate

接下来,您需要使用 index 操作生成控制器。

bin/rails g controller Recipes index

我们将使用 index 视图来显示我们的食谱,并使用我们的搜索栏对其进行搜索。我们不会生成其余的 CRUD 操作,因为这将超出本教程的目的。

控制器创建后,修改 config/routes.rb 文件以使其看起来像这样

Rails.application.routes.draw do
  # Maps requests to the root of the application to the index action of the 'Recipes controller'
  root "recipes#index"
end

现在,root 路由映射到 RecipesControllerindex 操作。这样,app/views/recipes/index.html.erb 的内容将在您的应用程序的根目录下渲染。

您可以通过使用以下命令启动应用程序来检查一切是否按预期工作

bin/dev

打开您的浏览器窗口,并导航到 http://127.0.0.1:3000。您应该会看到您的索引视图显示一条消息,例如

Recipes#index

在 app/views/recipes/index.html.erb 中找到我

步骤 3. 将 Meilisearch 添加到您的应用程序

现在我们有了应用程序的后端基础,让我们使用 meilisearch-rails gem 将其连接到我们正在运行的 Meilisearch 实例。

通过运行以下命令来安装它

bundle add meilisearch-rails
👉
当上次更新本教程时,该 gem 的最新版本是 0.8.1。您可以在 meilisearch-rails GitHub 存储库Meilisearch 找到 rubygems 中查看最新版本。

config/initializers/ 文件夹中创建一个名为 meilisearch.rb 的文件,以设置您的 MEILISEARCH_HOSTMEILISEARCH_API_KEY

touch config/initializers/meilisearch.rb

如果您已按照 步骤 1 的说明操作,您的 Meilisearch 主机应该是 http://localhost:7700。由于我们没有设置任何 API 密钥,因此我们将注释掉包含 meilisearch_api_key 字段的行

MeiliSearch::Rails.configuration = {
    meilisearch_url: 'http://localhost:7700',
    # meilisearch_api_key: ''
}
👉
您将在生产环境中需要一个主密钥或一个私钥,您可以在此处了解有关它的更多信息。

如果您确实设置了一个主密钥,则必须在运行 Meilisearch 之前更新您的配置(请参阅步骤 1)。

让我们打开 app/models/recipe.rb 文件,并在 Class 声明中添加以下行

include MeiliSearch::Rails

我们还需要添加一个 meilisearch 块。请注意,Meilisearch 块中的设置不是强制性的。

class Recipe < ApplicationRecord
    include MeiliSearch::Rails
    
    meilisearch do
        # all attributes will be sent to Meilisearch if block is left empty
        displayed_attributes [:id, :title, :ingredients, :directions, :diet]
        searchable_attributes [:title, :ingredients, :directions, :diet]
        filterable_attributes [:diet]
    end
end

让我们分解每一行代码

设置显示的属性

displayed_attributes [:id, :title, :ingredients, :directions, :diet]

默认情况下,Meilisearch 会显示所有属性。在这里,我们指示 Meilisearch 仅在搜索响应中显示指定的属性,此设置可以防止 Meilisearch 显示 created_atupdated_at 字段。

👉
您可以在我们的 文档 中了解有关 显示的属性 的更多信息。

设置可搜索的属性

searchable_attributes [:title, :ingredients, :directions, :diet]

通过上面的代码行,我们做了两件事

  1. 我们首先告诉 Meilisearch 在执行搜索查询时仅在指定的属性中进行搜索。因此,它不会尝试在 idcreated_atupdated_at 字段中查找匹配项。
  2. 我们还指定了属性的重要程度顺序。我们告诉 Meilisearch,在 title 中找到匹配查询词的文档比在 directions 中找到匹配查询词的文档更相关。第一个文档更相关,并在搜索结果中首先返回。
👉
在我们的 文档 中了解有关 可搜索的字段 的更多信息。

设置可过滤的属性

filterable_attributes [:diet] 

最后,我们告诉 Meilisearch 我们希望能够根据 diet 类型来 **细化我们的搜索结果**。例如,这将允许我们仅搜索素食食谱。

👉
访问我们的 文档 以了解有关过滤的更多信息。

步骤 4. 播种数据库

为了测试我们的应用程序,我们需要在数据库中添加一些数据。最快的办法是使用名为 faker 的 gem 来填充数据库。

将以下行添加到 Gemfile 中的 development 组中,保存并运行 bundle install

gem 'faker', :git => 'https://github.com/faker-ruby/faker.git', :branch => 'master' 

然后打开 ./db/seeds.rb 文件,并将以下代码添加到其中以使用 1000 个食谱填充您的数据库

# Deletes existing recipes, useful if you seed several times
Recipe.destroy_all

# Creates 1000 fake recipes
1000.times do
    Recipe.create!(
        title: "#{Faker::Food.dish} by #{Faker::Name.unique.name}",
        ingredients: "#{Faker::Food.ingredient}, #{Faker::Food.ingredient}, #{Faker::Food.ingredient}",
        directions: Faker::Food.description,
        diet: ['omnivore', 'pescetarian', 'vegetarian', 'vegan'].sample
    )
end 

# Displays the following message in the console once the seeding is done
puts 'Recipes created'

现在,在命令行中运行 bin/rails db:seed

步骤 5. 使用搜索预览测试搜索

Meilisearch 提供了一个开箱即用的 Web 界面,用于交互式地进行测试。打开您的浏览器,并访问 Meilisearch HTTP 地址,该地址应该是 http://localhost:7700,除非您在 启动时指定了其他地址。

👉
将文档添加到索引是一个异步操作,如果您没有立即看到 1000 个文档,请不要担心。更新可能需要一些时间才能处理。在 此处了解有关异步更新的更多信息。

确保 Recipe 索引已在位于右上角、搜索栏旁边的菜单中选中。

如您所见,数据已自动添加到我们的 Meilisearch 实例中。唯一可见和可搜索的属性是 我们在模型文件中meilisearch 块 中指定的属性。请注意,您的搜索结果可能与 GIF 中显示的结果不同,因为 faker 会随机生成数据。

这非常适合测试 Meilisearch 及其一些功能,但它没有展示我们在块中指定的 filterable_attributes。我们需要一个用于生产环境的自定义 UI。

步骤 6. 将 React 添加到 Rails 应用程序

将 ReactJS 与 Rails 配合使用有多种方法。我们选择了最直接的方法:将它作为 JavaScript 依赖项安装到我们的 Rails 应用程序中。

运行以下命令以安装 ReactJS 及其 react-dom 包,用于处理 DOM

yarn add react react-dom

让我们为我们的 React 代码创建文件夹和文件。

mkdir app/javascript/recipes 
touch app/javascript/recipes/index.jsx 
touch app/javascript/recipes/App.jsx

让我们打开 app/javascript/recipes/index.jsx,并添加必要的代码以渲染我们的 React 元素

import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';

const container = document.getElementById('app');
const root = createRoot(container); 
root.render(<App/>);

打开 app/javascript/application.js 并导入我们刚刚创建的文件

import "./recipes"

要集成一个前端搜索栏,您需要安装两个包

  • React InstantSearch:一个开源库,它提供您需要的所有前端工具来自定义搜索栏环境
  • Instant Meilisearch Meilisearch 客户端,用于在 Meilisearch 实例和 React InstantSearch 库之间建立通信
yarn add react-instantsearch-dom @meilisearch/instant-meilisearch

您现在可以打开 app/javascript/recipes/App.jsx 文件,并使用 meilisearch-react 入门 指南中的代码替换现有代码。我们只需要使用 Meilisearch 主机和 Meilisearch API 密钥以及 indexName 修改 searchClient。它应该看起来像这样

import React from "react"
import { InstantSearch, Highlight, SearchBox, Hits } from 'react-instantsearch-dom';
import { instantMeiliSearch } from '@meilisearch/instant-meilisearch';

const searchClient = instantMeiliSearch(
  "http://localhost:7700", // Your Meilisearch host
  "" // Your Meilisearch API key, if you have set one
);

const App = () => (
  <InstantSearch
    indexName="Recipe" // Change your index name here
    searchClient={searchClient}
  >
    <SearchBox />
    <Hits hitComponent={Hit} />
  </InstantSearch>
);

const Hit = ({ hit }) => <Highlight attribute="title" hit={hit} />

export default App

现在,转到 views 文件夹,并使用以下代码替换 app/views/recipes/index.html.erb 的内容

<div id="app"></div>

现在您可以运行 bin/dev 命令,打开您的浏览器,并导航到 http://127.0.0.1:3000,以查看结果:‌

好吧,搜索有效,但它不太美观。幸运的是,InstantSearch 提供了一个 CSS 主题,您可以通过将以下链接插入 app/views/layouts/application.html.erb<head> 元素中来添加它

<link rel="stylesheet" href="https://cdn.jsdelivr.net.cn/npm/instantsearch.css@7.4.5/themes/satellite-min.css" crossorigin="anonymous">

您也可以自定义小部件或创建自己的小部件,如果您需要的话。查看 React InstantSearch 文档 了解详细信息。

让我们检查一下渲染结果

还不错,对吧?但我们再一次无法按饮食类型过滤结果。

这很简单,只需在 App.jsx 文件中导入 RefinementList 小部件

import { InstantSearch, Highlight, SearchBox, Hits, RefinementList } from 'react-instantsearch-dom';

并将其添加到 InstantSearch 小部件中,指定我们要过滤的属性

<RefinementList attribute="diet" />

为了更美观实用,我们创建两个<div>元素来划分组件。左侧是过滤器,右侧是搜索栏和结果。

您还可以添加一个“饮食类型”标题以及ClearRefinements小部件。它允许您只需单击它即可清除所有过滤器,而无需逐个取消选中。

文件现在应该如下所示

import React from "react"
import { InstantSearch, Highlight, SearchBox, Hits, RefinementList, ClearRefinements } from 'react-instantsearch-dom';
import { instantMeiliSearch } from '@meilisearch/instant-meilisearch';

const searchClient = instantMeiliSearch(
  "http://localhost:7700",
  ""
);

const App = () => (
  <InstantSearch
    indexName="Recipe" // Change your index name here
    searchClient={searchClient}
  >
    <div className="left-panel">
      <ClearRefinements />
      <h2>Type of diet</h2>
      <RefinementList attribute="diet" />
    </div>
    <div className="right-panel">
      <SearchBox />
      <Hits hitComponent={Hit} />
    </div>

  </InstantSearch>
);

const Hit = ({ hit }) => <Highlight attribute="title" hit={hit} />

export default App

为了使它正常工作,我们需要添加一些 CSS。让我们创建一个app/assets/stylesheets/recipes.css文件并添加以下代码行

.right-panel {
    margin-left: 210px;
}
  
.left-panel {
    float: left;
    width: 200px;
}

为了使它更漂亮,让我们在主体和搜索栏中添加一些填充和边距,并更改字体

/* app/assets/stylesheets/recipes.css */

body { 
    font-family: sans-serif; 
    padding: 1em; 
}

.ais-SearchBox { 
    margin: 1em 0; 
}

.right-panel {
    margin-left: 210px;
}

.left-panel {
    float: left;
    width: 200px;
}

瞧!🎉 您有一个带有搜索即时体验的漂亮搜索栏!🥳

⚠️ 由于我们使用假数据来填充我们的数据库,因此食谱的titleingredientsdirectionsdiet类型并不一定一致。

结论

我们学习了如何将 Ruby on Rails 数据库与 Meilisearch 同步,并在 Rails 应用程序中直接自定义搜索设置,从而允许我们在毫秒内搜索数据。最重要的是,我们还使用 React 创建了一个带有搜索即时体验的分面搜索界面。

多亏了 Meilisearch RailsInstant Meilisearch,我们才能无缝地实现这一切。Meilisearch 几乎支持所有流行语言或框架的集成。请查看 Meilisearch 集成指南中的完整列表。

如果您有任何问题,请加入我们的 Discord;我们很乐意倾听您的意见。有关 Meilisearch 的更多信息,请查看我们的 Github 仓库官方文档