Maison >interface Web >tutoriel HTML >Recherche en texte intégral dans Rails à l'aide d'Elasticsearch
Dans cet article, je vais vous montrer comment implémenter la recherche en texte intégral à l'aide de Ruby on Rails et Elasticsearch. De nos jours, tout le monde est habitué à saisir un terme de recherche et à obtenir des suggestions et des résultats mis en évidence pour le terme de recherche. La correction automatique est également une fonctionnalité intéressante si ce que vous essayez de rechercher est mal orthographié, comme nous l'avons vu sur des sites comme Google ou Facebook.
Réaliser toutes ces fonctionnalités en utilisant uniquement une base de données relationnelle comme MySQL ou Postgres n'est pas simple. Nous utilisons donc Elasticsearch, que vous pouvez considérer comme une base de données créée et optimisée spécifiquement pour la recherche. Il est open source et construit sur Apache Lucene.
L'une des meilleures fonctionnalités d'Elasticsearch est d'exposer ses fonctionnalités à l'aide d'une API REST. Il existe donc des bibliothèques qui encapsulent cette fonctionnalité pour la plupart des langages de programmation.
Plus tôt, j'ai mentionné qu'Elasticsearch est comme une base de données pour la recherche. Cela sera utile si vous connaissez une partie de sa terminologie.
Une chose à noter ici est que dans Elasticsearch, lorsque vous écrivez un document dans l'index, les champs du document sont analysés textuellement pour rendre la recherche facile et rapide. Elasticsearch prend également en charge la géolocalisation, vous pouvez donc rechercher des documents situés à une certaine distance d'un emplacement donné. C'est exactement ainsi que Foursquare implémente la recherche.
Je voudrais mentionner qu'Elasticsearch est conçu dans un souci de haute évolutivité, il est donc facile de créer des clusters avec plusieurs serveurs et d'avoir une haute disponibilité même si certains serveurs tombent en panne. Je n'entrerai pas dans les détails de la planification et du déploiement des différents types de clusters dans cet article.
Si vous utilisez Linux, vous pourrez peut-être installer Elasticsearch à partir de l'un des référentiels. Il peut être utilisé dans APT et YUM.
Si vous utilisez un Mac, vous pouvez l'installer en utilisant Homebrew : brew install elasticsearch
. Après avoir installé elasticsearch, vous verrez une liste de dossiers associés dans le terminal :
Pour vérifier que l'installation fonctionne, entrez elasticsearch
来启动它。然后在终端中运行 curl localhost:9200
dans le terminal et vous devriez voir quelque chose de similaire à ce qui suit :
Elastic HQ est un plugin de surveillance que nous pouvons utiliser pour gérer Elasticsearch depuis le navigateur, similaire à phpMyAdmin pour MySQL. Pour l'installer, lancez simplement dans le terminal :
/usr/local/Cellar/elasticsearch/2.2.0_1/libexec/bin/plugin -install royrusso/elasticsearch-HQ
Une fois l'installation terminée, accédez à http://localhost:9200/_plugin/hq dans votre navigateur :
Cliquez sur Connecter et vous verrez un écran affichant l'état du cluster : p>
À ce stade, comme vous pouvez vous y attendre, aucun index ou document n'a encore été créé, mais nous avons une instance locale d'Elasticsearch installée et en cours d'exécution.
Je vais créer une application Rails très simple dans laquelle vous ajoutez des articles à une base de données afin que nous puissions effectuer des recherches en texte intégral sur eux à l'aide d'Elasticsearch. Commencez par créer une nouvelle application Rails :
rails 新的 elasticsearch-rails
Ensuite, nous utilisons un échafaudage pour générer une nouvelle ressource d'article :
rails生成脚手架文章标题:string text:text
Nous devons maintenant ajouter une nouvelle route racine afin que nous puissions voir la liste des articles par défaut. Modifier config/routes.rb :
Rails.application.routes.draw do root to: 'articles#index' resources :articles end
Ouvrez un navigateur en exécutant la commande rake db:migrate
创建数据库。如果您启动 rails server
, accédez à localhost:3000 et ajoutez quelques articles à la base de données, ou téléchargez simplement le fichier db/seeds.rb avec les données factices que j'ai créées pour que vous n'ayez pas à dépenser beaucoup d'argent. du temps à remplir des formulaires.
Maintenant que nous avons notre petite application Rails contenant les articles de la base de données, nous sommes prêts à ajouter une fonctionnalité de recherche. Nous commencerons par ajouter des références à deux gemmes Elasticsearch officielles :
gem 'elasticsearch-model' gem 'elasticsearch-rails'
Sur de nombreux sites Web, il est courant d'avoir une zone de texte pour la recherche dans le menu supérieur de toutes les pages. Je vais donc créer une section de formulaire sur app/views/search/_form.html.erb. Comme vous pouvez le voir, j'envoie le formulaire généré en utilisant GET afin qu'il soit facile de copier et coller l'URL pour une recherche spécifique.
<%= form_for :term, url: search_path, method: :get do |form| %> <p> <%= text_field_tag :term, params[:term] %> <%= submit_tag "Search", name: nil %> </p> <% end %>
Ajoutez une référence au formulaire dans la mise en page principale du site Web. Modifiez app/views/layouts/application.html.erb.
<body> <%= render 'search/form' %> <%= yield %> </body>
Maintenant, nous avons également besoin d'un contrôleur pour effectuer la recherche proprement dite et afficher les résultats, nous exécutons donc la commande rails g 新控制器 Search
pour la générer.
class SearchController < ApplicationController def search if params[:term].nil? @articles = [] else @articles = Article.search params[:term] end end end
Comme vous pouvez le voir, j'appelle la méthode search
sur le modèle Article. Nous ne l'avons pas encore défini, donc si nous essayons d'effectuer une recherche à ce stade, nous obtiendrons une erreur. De plus, nous n’avons pas ajouté les routes du SearchController dans le fichier config/routes.rb , alors faisons ceci :
Rails.application.routes.draw do root to: 'articles#index' resources :articles get "search", to: "search#search" end
Si l'on regarde la documentation de la gem 'elasticsearch-rails', nous devons inclure deux modules sur le modèle à indexer dans Elasticsearch, dans notre cas article.rb.
require 'elasticsearch/model' class Article < ActiveRecord::Base include Elasticsearch::Model include Elasticsearch::Model::Callbacks end
Le premier modèle injecte la méthode Search que nous avons utilisée dans le contrôleur précédent. Le deuxième module s'intègre aux rappels ActiveRecord pour indexer chaque instance d'un article que nous enregistrons dans la base de données, et il met également à jour l'index si nous modifions ou supprimons un article de la base de données. Tout est donc transparent pour nous.
Si vous avez déjà importé des données dans la base de données, ces articles ne sont toujours pas dans l'index Elasticsearch ; seuls les nouveaux seront automatiquement indexés. Par conséquent, nous devons les indexer manuellement, ce qui est bien si nous lançons rails console
就很容易。然后我们只需要运行 irb(main) > Article.import
.
Nous sommes maintenant prêts à essayer la fonction de recherche. Si je tape "ruby" et clique sur Rechercher, voici les résultats :
Sur de nombreux sites Web, vous pouvez voir comment les termes que vous recherchez sont mis en évidence sur la page des résultats de recherche. C'est facile à faire avec Elasticsearch.
Modifiez app/models/article.rb et modifiez la méthode de recherche par défaut :
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
Par défaut, la méthode search
est définie par la gem 'elasticsearch-models' et fournit l'objet proxy __elasticsearch__ pour accéder à la classe wrapper de l'API Elasticsearch. Par conséquent, nous pouvons modifier la requête par défaut en utilisant les options JSON standard fournies par la documentation.
La méthode de recherche encapsulera désormais les résultats correspondant à la requête avec la balise HTML spécifiée. Pour ce faire, nous devons également mettre à jour la page de résultats de recherche pour afficher en toute sécurité les balises HTML. Pour ce faire, modifiez 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 %>
Ajoutez des styles CSS à app/assets/stylesheets/search.scss pour le balisage en surbrillance :
.search_results em { background-color: yellow; font-style: normal; font-weight: bold; }
Essayez de rechercher à nouveau "ruby" :
Comme vous pouvez le voir, mettre en évidence les termes de recherche est facile, mais pas idéal car nous devons envoyer une requête JSON et comme le précise la documentation Elasticsearch, nous n'avons aucune sorte d'abstraction.
La gemme Searchkick est fournie par Instacart et est une abstraction au-dessus de la gemme officielle Elasticsearch. Je vais refactoriser la fonctionnalité de mise en évidence afin que nous ajoutions d'abord gem 'searchkick'
au gemfile. La première classe que nous devons changer est le modèle Article.rb :
class Article < ActiveRecord::Base searchkick end
Comme vous pouvez le constater, c'est beaucoup plus simple. Nous devons réindexer à nouveau les articles et exécuter la commande rake searchkick:reindex CLASS=Article
. Afin de mettre en évidence les termes de recherche, nous devons transmettre un paramètre supplémentaire de search_controller.rb à la méthode de recherche.
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
Le dernier fichier que nous devons modifier est views/search/search.html.erb car searchkick renvoie désormais les résultats dans un format différent :
<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 %>
Il est maintenant temps d'exécuter à nouveau l'application et de tester la fonctionnalité de recherche :
请注意,我输入了搜索词“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-导轨”。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!