0

0

使用Elasticsearch在Rails中进行全文搜索

WBOY

WBOY

发布时间:2023-08-31 08:41:05

|

1541人浏览过

|

来源于php中文网

原创

在本文中,我将向您展示如何使用 ruby on rails 和 elasticsearch 实现全文搜索。如今,每个人都习惯于输入搜索词并获取建议以及突出显示搜索词的结果。如果您尝试搜索的内容拼写错误,自动更正也是一项不错的功能,正如我们在 google 或 facebook 等网站上看到的那样。

仅使用 MySQL 或 Postgres 等关系数据库来实现所有这些功能并不简单。因此,我们使用 Elasticsearch,您可以将其视为专门为搜索构建和优化的数据库。它是开源的,构建在 Apache Lucene 之上。

Elasticsearch 最好的功能之一是使用 REST API 公开其功能,因此有一些库为大多数编程语言封装了该功能。

Elasticsearch 简介

之前,我提到 Elasticsearch 就像一个用于搜索的数据库。如果您熟悉它的一些术语,这将会很有用。

  • 字段:字段就像一个键值对。该值可以是简单值(字符串、整数、日期),也可以是嵌套结构(如数组或对象)。字段类似于关系数据库中表中的列。
  • 文档:文档是字段列表。它是一个存储在 Elasticsearch 中的 JSON 文档。它就像关系数据库中表中的一行。每个文档都存储在索引中,并具有类型和唯一 ID。
  • 类型:类型就像关系数据库中的表。每种类型都有一个可以为该类型的文档指定的字段列表。
  • 索引:索引相当于关系数据库。它包含多种类型的定义并存储多个文档。

这里需要注意的一件事是,在 Elasticsearch 中,当您将文档写入索引时,会逐字分析文档字段,以使搜索变得轻松快捷。 Elasticsearch 还支持地理定位,因此您可以搜索位于给定位置一定距离内的文档。这正是 Foursquare 实现搜索的方式。

我想提一下,Elasticsearch 在构建时就考虑到了高可扩展性,因此很容易构建具有多个服务器的集群,并且即使某些服务器出现故障也具有高可用性。我不会在本文中详细介绍如何规划和部署不同类型的集群。

安装Elasticsearch

如果您使用的是 Linux,则可能可以从其中一个存储库安装 Elasticsearch。它可以在 APT 和 YUM 中使用。

如果您使用 Mac,则可以使用 Homebrew 安装:brew install elasticsearch。安装elasticsearch后,您将在终端中看到相关文件夹的列表:

使用Elasticsearch在Rails中进行全文搜索

要验证安装是否正常工作,请在终端中输入 elasticsearch 来启动它。然后在终端中运行 curl localhost:9200,您应该会看到类似以下内容的内容:

使用Elasticsearch在Rails中进行全文搜索

安装 Elastic HQ

Elastic HQ 是一个监控插件,我们可以使用它从浏览器管理 Elasticsearch,类似于 MySQL 的 phpMyAdmin。要安装它,只需在终端中运行:

/usr/local/Cellar/elasticsearch/2.2.0_1/libexec/bin/plugin -install royrusso/elasticsearch-HQ

安装完成后,在浏览器中导航至 http://localhost:9200/_plugin/hq:

使用Elasticsearch在Rails中进行全文搜索

点击连接,您将看到一个显示集群状态的屏幕: p>

使用Elasticsearch在Rails中进行全文搜索

此时,正如您所料,尚未创建任何索引或文档,但我们已经有了 Elasticsearch 的本地实例安装并运行。

创建 Rails 应用程序

我将创建一个非常简单的 Rails 应用程序,您可以在其中将文章添加到数据库中,以便我们可以使用 Elasticsearch 对它们执行全文搜索。首先创建一个新的 Rails 应用程序:

rails 新的 elasticsearch-rails

接下来我们使用脚手架生成一个新的文章资源:

rails生成脚手架文章标题:string text:text

现在我们需要添加一个新的根路由,这样我们就可以默认看到文章列表。编辑config/routes.rb

Rails.application.routes.draw do
  root to: 'articles#index'
  resources :articles
end

通过运行命令 rake db:migrate 创建数据库。如果您启动 rails server,打开浏览器,导航到 localhost:3000 并向数据库添加一些文章,或者只是下载文件 db/seeds.rb 以及我创建的虚拟数据,以便您不必花费大量时间填写表格。

添加搜索

现在我们有了包含数据库中文章的小 Rails 应用程序,我们准备添加搜索功能。我们将首先添加对两个官方 Elasticsearch Gems 的引用:

gem 'elasticsearch-model'
gem 'elasticsearch-rails'

在许多网站上,所有页面的顶部菜单中都有一个用于搜索的文本框是很常见的。因此,我将在 app/views/search/_form.html.erb 上创建一个表单部分。 如您所见,我将发送使用 GET 生成表单,因此可以轻松复制并粘贴特定搜索的 URL。

<%= form_for :term, url: search_path, method: :get do |form| %>
  

<%= text_field_tag :term, params[:term] %> <%= submit_tag "Search", name: nil %>

<% end %>

在主网站布局中添加对表单的引用。编辑app/views/layouts/application.html.erb。


  <%= render 'search/form' %>
  <%= yield %>

现在我们还需要一个控制器来执行实际搜索并显示结果,因此我们运行命令 rails g 新控制器 Search 来生成它。

class SearchController < ApplicationController
  def search
    if params[:term].nil?
	  @articles = []
	else
	  @articles = Article.search params[:term]
	end
  end
end

如您所见,我在 Article 模型上调用方法 search。我们还没有定义它,所以如果我们尝试在此时执行搜索,我们会收到错误。另外,我们还没有在 config/routes.rb 文件中添加 SearchController 的路由,所以让我们这样做:

Rails.application.routes.draw do
  root to: 'articles#index'

  resources :articles
  get "search", to: "search#search"
end

如果我们查看 gem 'elasticsearch-rails' 的文档,我们需要在要在 Elasticsearch 中索引的模型上包含两个模块,在我们的例子中文章.rb.

require 'elasticsearch/model'

class Article < ActiveRecord::Base
  include Elasticsearch::Model
  include Elasticsearch::Model::Callbacks
end

第一个模型注入了我们在之前的控制器中使用的 Search 方法。第二个模块与 ActiveRecord 回调集成,为我们保存到数据库的文章的每个实例建立索引,如果我们修改或从数据库中删除文章,它还会更新索引。所以这对我们来说都是透明的。

如果您之前将数据导入数据库,这些文章仍然不在 Elasticsearch 索引中;只有新的才会自动索引。因此,我们必须手动索引它们,如果我们启动 rails console 就很容易。然后我们只需要运行 irb(main) > Article.import 即可。

使用Elasticsearch在Rails中进行全文搜索

佳可商务购物程序 2004
佳可商务购物程序 2004

在原版的基础上做了一下修正评论没有提交正文的问题特价商品的调用连接问题去掉了一个后门补了SQL注入补了一个过滤漏洞浮动价不能删除的问题不能够搜索问题收藏时放入购物车时出错点放入购物车弹出2个窗口修正主题添加问题商家注册页导航连接问题销售排行不能显示更多问题热点商品不能显示更多问题增加了服务器探测 增加了空间使用查看 增加了在线文件编辑增加了后台管理里两处全选功能更新说明:后台的部分功能已经改过前台

下载

现在我们已准备好尝试搜索功能。如果我输入“ruby”并单击搜索,结果如下:

使用Elasticsearch在Rails中进行全文搜索

搜索突出显示

在许多网站上,您可以在搜索结果页面上看到您搜索的字词如何突出显示。使用 Elasticsearch 可以很容易地做到这一点。

编辑app/models/article.rb并修改默认搜索方式:

def self.search(query)
  __elasticsearch__.search(
    {
      query: {
        multi_match: {
          query: query,
          fields: ['title', 'text']
        }
      },
      highlight: {
        pre_tags: [''],
        post_tags: [''],
        fields: {
          title: {},
          text: {}
        }
      }
    }
  )
end

默认情况下,search 方法由 gem 'elasticsearch-models' 定义,并提供代理对象 __elasticsearch__ 来访问 Elasticsearch API 的包装类。因此,我们可以使用文档提供的标准 JSON 选项修改默认查询。

现在搜索方法将用指定的 HTML 标签包装与查询匹配的结果。为此,我们还需要更新搜索结果页面,以便能够安全地渲染 HTML 标签。为此,请编辑 app/views/search/search.html.erb

Search Results

<% if @articles %>
    <% @articles.each do |article| %>
  • <%= link_to article.try(:highlight).try(:title) ? article.highlight.title[0].html_safe : article.title, controller: "articles", action: "show", id: article._id %>

    <% if article.try(:highlight).try(:text) %> <% article.highlight.text.each do |snippet| %>

    <%= snippet.html_safe %>...

    <% end %> <% end %>
  • <% end %>
<% else %>

Your search did not match any documents.

<% end %>

将 CSS 样式添加到 app/assets/stylesheets/search.scss,用于突出显示的标记:

.search_results em {
  background-color: yellow;
  font-style: normal;
  font-weight: bold;
}

再次尝试搜索“ruby”:

使用Elasticsearch在Rails中进行全文搜索

如您所见,突出显示搜索词很容易,但并不理想,因为我们需要发送 JSON 查询正如 Elasticsearch 文档所指定的,我们没有任何类型的抽象。

Searchkick 宝石

Searchkick gem 由 Instacart 提供,它是官方 Elasticsearch gem 之上的抽象。我将重构突出显示功能,因此我们首先将 gem 'searchkick' 添加到 gemfile 中。我们需要更改的第一个类是 Article.rb 模型:

class Article < ActiveRecord::Base
  searchkick
end

正如您所看到的,它要简单得多。我们需要再次重新索引文章,并执行命令 rake searchkick:reindex CLASS=Article。为了突出显示搜索词,我们需要从 search_controller.rb 向搜索方法传递一个附加参数。

class SearchController < ApplicationController
  def search
    if params[:term].nil?
	  @articles = []
	else
	  term = params[:term]
	  @articles = Article.search term, fields: [:text], highlight:  true
	end
  end
end

我们需要修改的最后一个文件是 views/search/search.html.erb ,因为 searchkick 现在以不同的格式返回结果:

Search Results for: <%= params[:term] %>

<% if @articles %>
    <% @articles.with_details.each do |article, details| %>
  • <%= link_to article.title, controller: "articles", action: "show", id: article.id %>

    <%= details[:highlight][:text].html_safe %>...

  • <% end %>
<% else %>

Your search did not match any documents.

<% end %>

现在是时候再次运行应用程序并测试搜索功能了:

使用Elasticsearch在Rails中进行全文搜索

请注意,我输入了搜索词“dato”。我这样做的目的是为了向您展示,默认情况下,searchkick 设置为分析索引的文本,并且更允许拼写错误。

自动建议

自动建议或预先输入可预测用户将输入的内容,从而使搜索体验更快、更轻松。请记住,除非您有数千条记录,否则最好在客户端进行过滤。

让我们首先添加 typeahead 插件,该插件可通过 gem 'bootstrap-typeahead-rails' 获得,并将其添加到您的 Gemfile 中。接下来,我们需要向 app/assets/javascripts/application.js 添加一些 JavaScript,以便当您开始在搜索框中输入内容时,会出现一些建议。

//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require bootstrap-typeahead-rails
//= require_tree .

var ready = function() {
  var engine = new Bloodhound({
      datumTokenizer: function(d) {
          console.log(d);
          return Bloodhound.tokenizers.whitespace(d.title);
      },
      queryTokenizer: Bloodhound.tokenizers.whitespace,
      remote: {
          url: '../search/typeahead/%QUERY'
      }
  });

  var promise = engine.initialize();

  promise
      .done(function() { console.log('success'); })
      .fail(function() { console.log('error') });

  $("#term").typeahead(null, {
    name: "article",
    displayKey: "title",
    source: engine.ttAdapter()
  })
};

$(document).ready(ready);
$(document).on('page:load', ready);

关于前一个片段的一些评论。在最后两行中,因为我没有禁用涡轮链接,所以这是连接我想要在页面加载时运行的代码的方法。在脚本的第一部分,您可以看到我正在使用 Bloodhound。它是 typeahead.js 建议引擎,我还设置了 JSON 端点来发出 AJAX 请求来获取建议。之后,我在引擎上调用 initialize(),并使用其 id“term”在搜索文本字段上设置预输入。

现在,我们需要对建议进行后端实现,让我们从添加路由开始,编辑 app/config/routes.rb

Rails.application.routes.draw do
  root to: 'articles#index'

  resources :articles
  get "search", to: "search#search"
  get 'search/typeahead/:term' => 'search#typeahead'
end

接下来,我将在 app/controllers/search_controller.rb 上添加实现。

def typeahead
  render json: Article.search(params[:term], {
    fields: ["title"],
    limit: 10,
    load: false,
    misspellings: {below: 5},
  }).map do |article| { title: article.title, value: article.id } end
end

此方法返回使用 JSON 输入的术语的搜索结果。我只按标题搜索,但我也可以指定文章的正文。我还将搜索结果的数量限制为最多 10 个。

现在我们准备尝试 typeahead 实现:

使用Elasticsearch在Rails中进行全文搜索

结论

如您所见,将 Elasticsearch 与 Rails 结合使用使搜索数据变得非常简单且快速。在这里,我向您展示了如何使用 Elasticsearch 提供的低级 gem,以及 Searchkick gem,这是一个隐藏了 Elasticsearch 工作原理的一些细节的抽象。

根据您的具体需求,您可能会很乐意使用 Searchkick 并快速轻松地实施全文搜索。另一方面,如果您有一些其他复杂的查询,包括过滤器或组,您可能需要了解有关 Elasticsearch 上查询语言的详细信息,并最终使用较低级别的 gem 'elasticsearch-models' 和 'elasticsearch-导轨”。

相关专题

更多
Golang gRPC 服务开发与Protobuf实战
Golang gRPC 服务开发与Protobuf实战

本专题系统讲解 Golang 在 gRPC 服务开发中的完整实践,涵盖 Protobuf 定义与代码生成、gRPC 服务端与客户端实现、流式 RPC(Unary/Server/Client/Bidirectional)、错误处理、拦截器、中间件以及与 HTTP/REST 的对接方案。通过实际案例,帮助学习者掌握 使用 Go 构建高性能、强类型、可扩展的 RPC 服务体系,适用于微服务与内部系统通信场景。

8

2026.01.15

公务员递补名单公布时间 公务员递补要求
公务员递补名单公布时间 公务员递补要求

公务员递补名单公布时间不固定,通常在面试前,由招录单位(如国家知识产权局、海关等)发布,依据是原入围考生放弃资格,会按笔试成绩从高到低递补,递补考生需按公告要求限时确认并提交材料,及时参加面试/体检等后续环节。要求核心是按招录单位公告及时响应、提交材料(确认书、资格复审材料)并准时参加面试。

44

2026.01.15

公务员调剂条件 2026调剂公告时间
公务员调剂条件 2026调剂公告时间

(一)符合拟调剂职位所要求的资格条件。 (二)公共科目笔试成绩同时达到拟调剂职位和原报考职位的合格分数线,且考试类别相同。 拟调剂职位设置了专业科目笔试条件的,专业科目笔试成绩还须同时达到合格分数线,且考试类别相同。 (三)未进入原报考职位面试人员名单。

55

2026.01.15

国考成绩查询入口 国考分数公布时间2026
国考成绩查询入口 国考分数公布时间2026

笔试成绩查询入口已开通,考生可登录国家公务员局中央机关及其直属机构2026年度考试录用公务员专题网站http://bm.scs.gov.cn/pp/gkweb/core/web/ui/business/examResult/written_result.html,查询笔试成绩和合格分数线,点击“笔试成绩查询”按钮,凭借身份证及准考证进行查询。

11

2026.01.15

Java 桌面应用开发(JavaFX 实战)
Java 桌面应用开发(JavaFX 实战)

本专题系统讲解 Java 在桌面应用开发领域的实战应用,重点围绕 JavaFX 框架,涵盖界面布局、控件使用、事件处理、FXML、样式美化(CSS)、多线程与UI响应优化,以及桌面应用的打包与发布。通过完整示例项目,帮助学习者掌握 使用 Java 构建现代化、跨平台桌面应用程序的核心能力。

65

2026.01.14

php与html混编教程大全
php与html混编教程大全

本专题整合了php和html混编相关教程,阅读专题下面的文章了解更多详细内容。

36

2026.01.13

PHP 高性能
PHP 高性能

本专题整合了PHP高性能相关教程大全,阅读专题下面的文章了解更多详细内容。

75

2026.01.13

MySQL数据库报错常见问题及解决方法大全
MySQL数据库报错常见问题及解决方法大全

本专题整合了MySQL数据库报错常见问题及解决方法,阅读专题下面的文章了解更多详细内容。

21

2026.01.13

PHP 文件上传
PHP 文件上传

本专题整合了PHP实现文件上传相关教程,阅读专题下面的文章了解更多详细内容。

35

2026.01.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
php-src源码分析探索
php-src源码分析探索

共6课时 | 0.5万人学习

简单聊聊mysql8与网络通信
简单聊聊mysql8与网络通信

共1课时 | 793人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号