您好,欢迎访问一九零五行业门户网

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

在本文中,我将向您展示如何使用 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 来启动它。然后在终端中运行 curl localhost:9200,您应该会看到类似以下内容的内容:
安装 elastic hqelastic hq 是一个监控插件,我们可以使用它从浏览器管理 elasticsearch,类似于 mysql 的 phpmyadmin。要安装它,只需在终端中运行:
/usr/local/cellar/elasticsearch/2.2.0_1/libexec/bin/plugin -install royrusso/elasticsearch-hq
安装完成后,在浏览器中导航至 http://localhost:9200/_plugin/hq:
点击连接,您将看到一个显示集群状态的屏幕: p>
此时,正如您所料,尚未创建任何索引或文档,但我们已经有了 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 :articlesend
通过运行命令 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| %> <p> <%= text_field_tag :term, params[:term] %> <%= submit_tag search, name: nil %> </p><% end %>
在主网站布局中添加对表单的引用。编辑app/views/layouts/application.html.erb。
<body> <%= render 'search/form' %> <%= yield %></body>
现在我们还需要一个控制器来执行实际搜索并显示结果,因此我们运行命令 rails g 新控制器 search 来生成它。
class searchcontroller < applicationcontroller def search if params[:term].nil? @articles = [] else @articles = article.search params[:term] end endend
如您所见,我在 article 模型上调用方法 search。我们还没有定义它,所以如果我们尝试在此时执行搜索,我们会收到错误。另外,我们还没有在 config/routes.rb 文件中添加 searchcontroller 的路由,所以让我们这样做:
rails.application.routes.draw do root to: 'articles#index' resources :articles get search, to: search#searchend
如果我们查看 gem 'elasticsearch-rails' 的文档,我们需要在要在 elasticsearch 中索引的模型上包含两个模块,在我们的例子中文章.rb.
require 'elasticsearch/model'class article < activerecord::base include elasticsearch::model include elasticsearch::model::callbacksend
第一个模型注入了我们在之前的控制器中使用的 search 方法。第二个模块与 activerecord 回调集成,为我们保存到数据库的文章的每个实例建立索引,如果我们修改或从数据库中删除文章,它还会更新索引。所以这对我们来说都是透明的。
如果您之前将数据导入数据库,这些文章仍然不在 elasticsearch 索引中;只有新的才会自动索引。因此,我们必须手动索引它们,如果我们启动 rails console 就很容易。然后我们只需要运行 irb(main) > article.import 即可。
现在我们已准备好尝试搜索功能。如果我输入“ruby”并单击搜索,结果如下:
搜索突出显示在许多网站上,您可以在搜索结果页面上看到您搜索的字词如何突出显示。使用 elasticsearch 可以很容易地做到这一点。
编辑app/models/article.rb并修改默认搜索方式:
def self.search(query) __elasticsearch__.search( { query: { multi_match: { query: query, fields: ['title', 'text'] } }, highlight: { pre_tags: ['<em>'], post_tags: ['</em>'], fields: { title: {}, text: {} } } } )end
默认情况下,search 方法由 gem 'elasticsearch-models' 定义,并提供代理对象 __elasticsearch__ 来访问 elasticsearch api 的包装类。因此,我们可以使用文档提供的标准 json 选项修改默认查询。
现在搜索方法将用指定的 html 标签包装与查询匹配的结果。为此,我们还需要更新搜索结果页面,以便能够安全地渲染 html 标签。为此,请编辑 app/views/search/search.html.erb。
<h1>search results</h1><% if @articles %> <ul class=search_results> <% @articles.each do |article| %> <li> <h3> <%= link_to article.try(:highlight).try(:title) ? article.highlight.title[0].html_safe : article.title, controller: articles, action: show, id: article._id %> </h3> <% if article.try(:highlight).try(:text) %> <% article.highlight.text.each do |snippet| %> <p><%= snippet.html_safe %>...</p> <% end %> <% end %> </li> <% end %></ul><% else %> <p>your search did not match any documents.</p><% end %>
将 css 样式添加到 app/assets/stylesheets/search.scss,用于突出显示的标记:
.search_results em { background-color: yellow; font-style: normal; font-weight: bold;}
再次尝试搜索“ruby”:
如您所见,突出显示搜索词很容易,但并不理想,因为我们需要发送 json 查询正如 elasticsearch 文档所指定的,我们没有任何类型的抽象。
searchkick 宝石searchkick gem 由 instacart 提供,它是官方 elasticsearch gem 之上的抽象。我将重构突出显示功能,因此我们首先将 gem 'searchkick' 添加到 gemfile 中。我们需要更改的第一个类是 article.rb 模型:
class article < activerecord::base searchkickend
正如您所看到的,它要简单得多。我们需要再次重新索引文章,并执行命令 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 endend
我们需要修改的最后一个文件是 views/search/search.html.erb ,因为 searchkick 现在以不同的格式返回结果:
<h2>search results for: <i><%= params[:term] %></i></h2><% if @articles %><ul class=search_results> <% @articles.with_details.each do |article, details| %> <li> <h3> <%= link_to article.title, controller: articles, action: show, id: article.id %> </h3> <p><%= details[:highlight][:text].html_safe %>...</p> </li> <% end %></ul><% else %> <p>your search did not match any documents.</p><% end %>
现在是时候再次运行应用程序并测试搜索功能了:
请注意,我输入了搜索词“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 } endend
此方法返回使用 json 输入的术语的搜索结果。我只按标题搜索,但我也可以指定文章的正文。我还将搜索结果的数量限制为最多 10 个。
现在我们准备尝试 typeahead 实现:
结论如您所见,将 elasticsearch 与 rails 结合使用使搜索数据变得非常简单且快速。在这里,我向您展示了如何使用 elasticsearch 提供的低级 gem,以及 searchkick gem,这是一个隐藏了 elasticsearch 工作原理的一些细节的抽象。
根据您的具体需求,您可能会很乐意使用 searchkick 并快速轻松地实施全文搜索。另一方面,如果您有一些其他复杂的查询,包括过滤器或组,您可能需要了解有关 elasticsearch 上查询语言的详细信息,并最终使用较低级别的 gem 'elasticsearch-models' 和 'elasticsearch-导轨”。
以上就是使用elasticsearch在rails中进行全文搜索的详细内容。
其它类似信息

推荐信息