Carian teks penuh dalam Rails menggunakan Elasticsearch

WBOY
Lepaskan: 2023-08-31 08:41:05
asal
1387 orang telah melayarinya

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.

Pengenalan Elasticsearch

Sebelum ini, saya ada menyebut bahawa Elasticsearch adalah seperti pangkalan data untuk carian. Ini akan berguna jika anda sudah biasa dengan beberapa istilahnya.

  • Field: Medan adalah seperti pasangan nilai kunci. Nilai boleh menjadi nilai mudah (rentetan, integer, tarikh) atau struktur bersarang (seperti tatasusunan atau objek). Medan adalah serupa dengan lajur dalam jadual dalam pangkalan data hubungan.
  • doc: Dokumen ialah senarai medan. Ia adalah dokumen JSON yang disimpan dalam Elasticsearch. Ia seperti baris dalam jadual dalam pangkalan data hubungan. Setiap dokumen disimpan dalam indeks dan mempunyai jenis dan ID unik.
  • Type: Jenis adalah seperti jadual dalam pangkalan data hubungan. Setiap jenis mempunyai senarai medan yang boleh ditentukan untuk dokumen jenis itu.
  • INDEX: Indeks adalah bersamaan dengan pangkalan data hubungan. Ia mengandungi pelbagai jenis definisi dan menyimpan berbilang dokumen.

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.

Pasang Elasticsearch

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后,您将在终端中看到相关文件夹的列表:

Carian teks penuh dalam Rails menggunakan Elasticsearch

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

Carian teks penuh dalam Rails menggunakan Elasticsearch

安装 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:

Carian teks penuh dalam Rails menggunakan Elasticsearch

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

Carian teks penuh dalam Rails menggunakan Elasticsearch

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

创建 Rails 应用程序

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

rails 新的 elasticsearch-rails

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

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

Carian teks penuh dalam Rails menggunakan Elasticsearch# #

Untuk mengesahkan bahawa pemasangan berfungsi dengan betul, mulakannya dengan menaipelasticsearchdalam terminal. Kemudian jalankancurl localhost:9200dalam terminal dan anda akan melihat sesuatu yang serupa dengan:

Carian teks penuh dalam Rails menggunakan Elasticsearch# #

Pasang HQ Elastik

Elastic HQ ialah pemalam pemantauan yang boleh kami gunakan untuk mengurus Elasticsearch daripada penyemak imbas, serupa dengan phpMyAdmin untuk MySQL. Untuk memasangnya, jalankan sahaja di terminal: /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: ## ## Carian teks penuh dalam Rails menggunakan Elasticsearch# ###Klik ##Connect ## dan anda akan melihat skrin yang menunjukkan status kelompok: ## Carian teks penuh dalam Rails menggunakan Elasticsearch# ###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
Salin selepas log masuk

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'
Salin selepas log masuk

在许多网站上,所有页面的顶部菜单中都有一个用于搜索的文本框是很常见的。因此,我将在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 %>
Salin selepas log masuk

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

 <%= render 'search/form' %> <%= yield %> 
Salin selepas log masuk

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

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

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

Rails.application.routes.draw do root to: 'articles#index' resources :articles get "search", to: "search#search" end
Salin selepas log masuk

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

require 'elasticsearch/model' class Article < ActiveRecord::Base include Elasticsearch::Model include Elasticsearch::Model::Callbacks end
Salin selepas log masuk

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

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

Carian teks penuh dalam Rails menggunakan Elasticsearch

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

Carian teks penuh dalam Rails menggunakan Elasticsearch

搜索突出显示

在许多网站上,您可以在搜索结果页面上看到您搜索的字词如何突出显示。使用 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
Salin selepas log masuk

默认情况下,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 %>
Salin selepas log masuk

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

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

再次尝试搜索“ruby”:

Carian teks penuh dalam Rails menggunakan Elasticsearch

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

Searchkick 宝石

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

class Article < ActiveRecord::Base searchkick end
Salin selepas log masuk

正如您所看到的,它要简单得多。我们需要再次重新索引文章,并执行命令rake searchkick:reindex CLASS=Article

Tambah carian

Kini setelah kami mempunyai aplikasi Rails kecil kami yang mengandungi artikel dalam pangkalan data kami, kami bersedia untuk menambah fungsi carian. Kami akan mulakan dengan menambah rujukan kepada dua Permata Elasticsearch rasmi:
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
Salin selepas log masuk

Di kebanyakan tapak web, adalah perkara biasa untuk mempunyai kotak teks untuk carian dalam menu atas semua halaman. Jadi saya akan membuat bahagian borang pada

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 %>
    <% @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 %>
Salin selepas log masuk

Tambahkan rujukan pada borang dalam susun atur laman web utama. Edit ##app/views/layouts/application.html.erb. ####
//= 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);
Salin selepas log masuk
Salin selepas log masuk
##Kini kami juga memerlukan pengawal untuk melakukan carian sebenar dan memaparkan hasilnya, jadi kami menjalankan perintah rails g new controller Searchuntuk menjananya. ##
Rails.application.routes.draw do root to: 'articles#index' resources :articles get "search", to: "search#search" get 'search/typeahead/:term' => 'search#typeahead' end
Salin selepas log masuk
Salin selepas log masuk
##Seperti yang anda lihat, saya memanggil kaedah searchpada 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
Salin selepas log masuk
Salin selepas log masuk
##Jika kita melihat dokumentasi untuk permata ##'elasticsearch-rails'##, kita perlu memasukkan dua modul pada model untuk diindeks dalam Elasticsearch, dalam kes kita ##artikel .rb##.## rrreee ##Model pertama menyuntik kaedah Carian yang kami gunakan dalam pengawal sebelumnya. Modul kedua disepadukan dengan panggil balik ActiveRecord untuk mengindeks setiap contoh artikel yang kami simpan ke pangkalan data, dan ia juga mengemas kini indeks jika kami mengubah suai atau memadamkan artikel daripada pangkalan data. Jadi semuanya telus kepada kami. ## ##Jika anda sebelum ini mengimport data ke dalam pangkalan data, artikel ini masih tiada dalam indeks Elasticsearch hanya yang baharu akan diindeks secara automatik. Oleh itu, kita perlu mengindeksnya secara manual, yang mudah jika kita melancarkan rel console. Kemudian kita hanya perlu menjalankan irb(main) > Article.import. ## ## Carian teks penuh dalam Rails menggunakan Elasticsearch# ###Kini kami bersedia untuk mencuba fungsi carian. Jika saya menaip "ruby" dan klik carian, hasilnya ialah: ## ## Carian teks penuh dalam Rails menggunakan Elasticsearch# #

Sorotan Carian

## Di banyak tapak web, anda boleh melihat cara istilah yang anda cari diserlahkan pada halaman hasil carian. Ini mudah dilakukan menggunakan Elasticsearch. ## ##edit##app/models/article.rb## dan ubah suai kaedah carian lalai: ## rrreee ##Secara lalai, kaedah searchditakrifkan 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: ## ## Carian teks penuh dalam Rails menggunakan Elasticsearch# ###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. ######

Permata tendangan carian

##Searchkick permata disediakan oleh Instacart dan merupakan abstraksi di atas permata Elasticsearch rasmi. Saya akan memfaktorkan semula fungsi penonjolan, jadi kami mula-mula menambah 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: ##

Carian teks penuh dalam Rails menggunakan Elasticsearch

请注意,我输入了搜索词“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);
Salin selepas log masuk
Salin selepas log masuk

关于前一个片段的一些评论。在最后两行中,因为我没有禁用涡轮链接,所以这是连接我想要在页面加载时运行的代码的方法。在脚本的第一部分,您可以看到我正在使用 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
Salin selepas log masuk
Salin selepas log masuk

接下来,我将在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
Salin selepas log masuk
Salin selepas log masuk

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

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

Carian teks penuh dalam Rails menggunakan Elasticsearch

结论

如您所见,将 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!

Label berkaitan:
sumber:php.cn
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan
Tentang kita Penafian Sitemap
Laman web PHP Cina:Latihan PHP dalam talian kebajikan awam,Bantu pelajar PHP berkembang dengan cepat!