Dalam artikel ini, saya akan menunjukkan kepada anda cara melaksanakan carian teks penuh menggunakan Ruby on Rails dan Elasticsearch. Pada masa kini, semua orang sudah terbiasa menaip istilah carian dan mendapatkan cadangan serta hasil yang diserlahkan untuk istilah carian. Autocorrect juga merupakan ciri yang bagus jika perkara yang anda cuba cari tersalah eja, seperti yang kita lihat di tapak seperti Google atau Facebook.
Mencapai semua fungsi ini menggunakan hanya pangkalan data hubungan seperti MySQL atau Postgres bukanlah mudah. Jadi kami menggunakan Elasticsearch, yang boleh anda anggap sebagai pangkalan data yang dibina dan dioptimumkan khusus untuk carian. Ia adalah sumber terbuka dan dibina di atas Apache Lucene.
Salah satu ciri terbaik Elasticsearch ialah mendedahkan fungsinya menggunakan API REST, jadi terdapat perpustakaan yang merangkumi fungsi ini untuk kebanyakan bahasa pengaturcaraan.
Sebelum ini, saya ada menyebut bahawa Elasticsearch adalah seperti pangkalan data untuk carian. Ini akan berguna jika anda sudah biasa dengan beberapa istilahnya.
Satu perkara yang perlu diperhatikan di sini ialah dalam Elasticsearch, apabila anda menulis dokumen pada indeks, medan dokumen dianalisis secara literal untuk menjadikan carian mudah dan pantas. Elasticsearch juga menyokong geolokasi, jadi anda boleh mencari dokumen yang terletak dalam jarak tertentu dari lokasi tertentu. Beginilah cara Foursquare melaksanakan carian.
Saya ingin menyebut bahawa Elasticsearch dibina dengan berskala tinggi, jadi mudah untuk membina kelompok dengan berbilang pelayan dan mempunyai ketersediaan yang tinggi walaupun sesetengah pelayan gagal. Saya tidak akan menerangkan secara terperinci tentang cara merancang dan menggunakan pelbagai jenis kluster dalam artikel ini.
Jika anda menggunakan Linux, anda mungkin boleh memasang Elasticsearch dari salah satu repositori. Ia boleh digunakan dalam APT dan YUM.
Jika anda menggunakan Mac, anda boleh memasangnya menggunakan Homebrew:brew install elasticsearch
. Selepas memasang elasticsearch, anda akan melihat senarai folder berkaitan dalam terminal:brew install elasticsearch
。安装elasticsearch后,您将在终端中看到相关文件夹的列表:
要验证安装是否正常工作,请在终端中输入elasticsearch
来启动它。然后在终端中运行curl localhost:9200
,您应该会看到类似以下内容的内容:
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:
点击连接,您将看到一个显示集群状态的屏幕: p>
此时,正如您所料,尚未创建任何索引或文档,但我们已经有了 Elasticsearch 的本地实例安装并运行。
我将创建一个非常简单的 Rails 应用程序,您可以在其中将文章添加到数据库中,以便我们可以使用 Elasticsearch 对它们执行全文搜索。首先创建一个新的 Rails 应用程序:
rails 新的 elasticsearch-rails
接下来我们使用脚手架生成一个新的文章资源:
rails生成脚手架文章标题:string text:text
Untuk mengesahkan bahawa pemasangan berfungsi dengan betul, mulakannya dengan menaipelasticsearch
dalam terminal. Kemudian jalankancurl localhost:9200
dalam terminal dan anda akan melihat sesuatu yang serupa dengan:
# #
/usr/local/Cellar/elasticsearch/2.2.0_1/libexec/bin/plugin -install royrusso/elasticsearch-HQ
##### # ##Selepas pemasangan selesai, navigasi ke http://localhost:9200/_plugin/hq dalam penyemak imbas anda: ## ##
# ###Klik ##Connect ## dan anda akan melihat skrin yang menunjukkan status kelompok: ##
# ###Pada ketika ini, seperti yang anda jangkakan, masih belum ada indeks atau dokumen yang dibuat, tetapi kami mempunyai contoh tempatan Elasticsearch yang dipasang dan dijalankan. ## ##Buat Aplikasi Rel## ## Saya akan mencipta aplikasi Rails yang sangat mudah di mana anda menambah artikel pada pangkalan data supaya kami boleh melakukan carian teks penuh padanya menggunakan Elasticsearch. Mulakan dengan mencipta aplikasi Rails baharu: ## ##
rel elasticsearch-rails baharu
## ## Seterusnya kami menggunakan perancah untuk menjana sumber artikel baharu: ## ##
rel menjana tajuk artikel perancah: string text:text
###### ##Kini kita perlu menambah laluan akar baharu supaya kita boleh melihat senarai artikel secara lalai. Edit ##config/routes.rb##: ##
Rails.application.routes.draw do root to: 'articles#index' resources :articles end
Buat pangkalan data dengan menjalankan perintahrake db:migrate
. Jika anda memulakanrails server
, buka penyemak imbas, navigasi ke localhost:3000 dan tambahkan beberapa artikel pada pangkalan data atau muat turun sahaja fail db/seeds.rb dengan data dummy I dicipta, Supaya anda tidak perlu menghabiskan banyak masa untuk mengisi borang.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
即可。
现在我们已准备好尝试搜索功能。如果我输入“ruby”并单击搜索,结果如下:
在许多网站上,您可以在搜索结果页面上看到您搜索的字词如何突出显示。使用 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 %>
<%= snippet.html_safe %>...
<% end %> <% end %>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”:
如您所见,突出显示搜索词很容易,但并不理想,因为我们需要发送 JSON 查询正如 Elasticsearch 文档所指定的,我们没有任何类型的抽象。
Searchkick gem 由 Instacart 提供,它是官方 Elasticsearch gem 之上的抽象。我将重构突出显示功能,因此我们首先将gem 'searchkick'
添加到 gemfile 中。我们需要更改的第一个类是 Article.rb 模型:
class Article < ActiveRecord::Base searchkick end
正如您所看到的,它要简单得多。我们需要再次重新索引文章,并执行命令rake searchkick:reindex CLASS=Article
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
app/views/search/_form.html.erb.
Seperti yang anda lihat, saya menghantar borang yang dijana menggunakan GET supaya mudah untuk menyalin dan menampal URL untuk carian tertentu.Search Results for: <%= params[:term] %>
<% if @articles %>
<%= details[:highlight][:text].html_safe %>...
Your search did not match any documents.
<% end %>//= 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);
rails g new controller Search
untuk menjananya. ##
Rails.application.routes.draw do root to: 'articles#index' resources :articles get "search", to: "search#search" get 'search/typeahead/:term' => 'search#typeahead' end
search
pada model Artikel. Kami belum mentakrifkannya lagi, jadi jika kami cuba melakukan carian pada ketika ini, kami akan mendapat ralat. Selain itu, kami belum menambah laluan SearchController dalam fail ##config/routes.rb ##, jadi mari lakukan ini: ##
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
rel console
. Kemudian kita hanya perlu menjalankan
irb(main) > Article.import
. ## ##
# ###Kini kami bersedia untuk mencuba fungsi carian. Jika saya menaip "ruby" dan klik carian, hasilnya ialah: ## ##
# #
search
ditakrifkan oleh 'elasticsearch-models' permata dan menyediakan objek proksi __elasticsearch__ untuk mengakses kelas pembalut API Elasticsearch. Oleh itu, kami boleh mengubah suai pertanyaan lalai menggunakan pilihan JSON standard yang disediakan oleh dokumentasi. ## ##Kaedah carian kini akan membungkus hasil yang sepadan dengan pertanyaan dengan teg HTML yang ditentukan. Untuk melakukan ini, kami juga perlu mengemas kini halaman hasil carian untuk memaparkan teg HTML dengan selamat. Untuk melakukan ini, edit ##app/views/search/search.html.erb##. ## rrreee ##Tambahkan gaya CSS pada ##app/assets/stylesheets/search.scss## untuk teg yang diserlahkan: ## rrreee ## Cuba cari "delima" sekali lagi: ## ##
# ###Seperti yang anda lihat, menyerlahkan istilah carian adalah mudah, tetapi tidak sesuai kerana kami perlu menghantar pertanyaan JSON dan seperti yang dinyatakan oleh dokumentasi Elasticsearch, kami tidak mempunyai sebarang jenis abstraksi. ######
gem 'searchkick'
pada gemfile. Kelas pertama yang perlu kita ubah ialah model Article.rb: ## rrreee ##Seperti yang anda lihat, ia lebih mudah. Kita perlu mengindeks semula artikel itu sekali lagi dan melaksanakan perintah
rake searchkick:reindex CLASS=Article
. Untuk menyerlahkan istilah carian, kami perlu menghantar parameter tambahan daripada ##search_controller.rb## kepada kaedah carian. ## rrreee ##Fail terakhir yang perlu kami ubah suai ialah ##views/search/search.html.erb ## kerana searchkick kini mengembalikan hasil dalam format berbeza: ## rrreee ##Kini tiba masanya untuk menjalankan aplikasi sekali lagi dan menguji fungsi carian: ##
请注意,我输入了搜索词“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 提供的低级 gem,以及 Searchkick gem,这是一个隐藏了 Elasticsearch 工作原理的一些细节的抽象。
根据您的具体需求,您可能会很乐意使用 Searchkick 并快速轻松地实施全文搜索。另一方面,如果您有一些其他复杂的查询,包括过滤器或组,您可能需要了解有关 Elasticsearch 上查询语言的详细信息,并最终使用较低级别的 gem 'elasticsearch-models' 和 'elasticsearch-导轨”。
Atas ialah kandungan terperinci Carian teks penuh dalam Rails menggunakan Elasticsearch. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!