În acest articol vă voi arăta cum să implementați căutarea full-text utilizând Ruby on Rails și Elasticsearch. Toată lumea este folosită în zilele noastre pentru a introduce un termen de căutare și a obține sugestii și rezultate cu termenul de căutare evidențiat. Dacă spui greșit ceea ce încerci să cauți, având auto-corect este, de asemenea, o caracteristică frumoasă, așa cum se poate vedea pe site-uri precum Google sau Facebook.
Implementarea tuturor acestor funcții utilizând doar o bază de date relațională precum MySQL sau Postgres nu este simplă. Din acest motiv, folosim Elasticsearch, despre care vă puteți gândi ca o bază de date construită și optimizată special pentru căutare. Este open source și este construit pe partea de sus a Apache Lucene.
Una dintre cele mai frumoase caracteristici ale Elasticsearch este aceea care expune funcționalitatea sa folosind REST API, astfel încât există biblioteci care înfășoară acea funcționalitate pentru majoritatea limbajelor de programare.
Anterior, am menționat că Elasticsearch este ca o bază de date pentru căutare. Ar fi util dacă sunteți familiarizat cu o anumită terminologie din jurul lui.
Un lucru de observat aici este că în Elasticsearch, atunci când scrieți un document într-un index, câmpurile documentului sunt analizate, cuvânt cu cuvânt, pentru a face căutarea ușor și rapid. Elasticsearch sprijină, de asemenea, localizarea geografică, astfel încât să puteți căuta documente care se află la o anumită distanță de o anumită locație. Exact așa efectuează Foursquare căutarea.
Aș dori să menționez că Elasticsearch a fost construit cu o mare scalabilitate în minte, deci este foarte ușor să construim un cluster cu mai multe servere și să avem disponibilitate ridicată chiar dacă unele servere coboară. Nu voi acoperi specificul modului de planificare și implementare a diferitelor tipuri de clustere în acest articol.
Dacă utilizați Linux, este posibil să instalați Elasticsearch din unul dintre depozit. Este disponibil în APT și YUM.
Dacă utilizați Mac, îl puteți instala utilizând Homebrew: prepara elassearch
. După instalarea elastică, veți vedea lista de dosare relevante din terminalul dvs.:
Pentru a verifica dacă instalarea funcționează, tastați elasticsearch
în terminalul dvs. pentru al porni. Atunci fugi curl localhost: 9200
în terminalul dvs. și ar trebui să vedeți ceva de genul:
Elastic HQ este un plugin de monitorizare pe care îl putem folosi pentru a gestiona Elasticsearch din browser, similar cu phpMyAdmin pentru MySQL. Pentru a le instala, trebuie doar să rulați în terminal:
/usr/local/Cellar/elasticsearch/2.2.0_1/libexec/bin/plugin -install royrusso / elasticsearch-HQ
Odată instalat, navigați la http: // localhost: 9200 / _plugin / hq în browserul dvs.:
Click pe Conectați și veți vedea un ecran care arată starea clusterului:
În acest moment, după cum s-ar putea aștepta, nu se creează încă indici sau documente, dar avem instanța noastră locală de Elasticsearch instalată și rulată.
Voi crea o aplicație foarte simplă Rails, în care puteți adăuga articole în baza de date, astfel încât să putem efectua o căutare completă pe ele folosind Elasticsearch. Începeți prin crearea unei noi aplicații Rails:
șine noi elastice-șine
Apoi vom genera o nouă resursă de articole cu schele:
șine genera schelă Titlul articolului: text șir: text
Acum trebuie să adăugăm o nouă rută rădăcină, astfel încât să vedem în mod implicit lista articolelor. Editați | × config / routes.rb:
Rails.application.routes.draw se bazează pe: resursele "articole # index": articolele se termină
Creați baza de date executând comanda rake db: migrați
. Dacă începeți rails server
, deschideți browserul dvs., navigați la localhost: 3000 și adăugați câteva articole în baza de date sau pur și simplu descărcați fișierul db / seeds.rb cu date fictive pe care le-am creat, astfel încât să nu trebuie să vă petreceți prea mult timp completarea formularelor.
Acum, când avem aplicația noastră Rails cu articole din baza de date, suntem gata să adăugăm funcția de căutare. Vom începe prin adăugarea referinței la ambele pietre Elasticsearch:
bijuterie elastica-model "bijuterie elastica"
Pe multe site-uri web, este foarte comun să aveți o casetă text pentru căutare în meniul de sus din toate paginile. Din acest motiv, voi crea o formă parțială app / opinii / căutare / _form.html.erb.După cum puteți vedea, trimit formularul utilizând GET, deci este ușor să copiați și să inserați adresa URL pentru o anumită căutare.
<%= form_for :term, url: search_path, method: :get do |form| %><%= text_field_tag :term, params[:term] %> <%= submit_tag "Search", name: nil %>
<% end %>
Adăugați o referință la formular pe aspectul principal al site-ului web. Editați | × app / opinii / machete / application.html.erb.
<%= render 'search/form' %> <%= yield %>
De asemenea, avem nevoie de un controler pentru a efectua căutarea reală și pentru a afișa rezultatele, astfel că generăm rularea comenzii rails g Căutare nouă de controler
.
clasa SearchController < ApplicationController def search if params[:term].nil? @articles = [] else @articles = Article.search params[:term] end end end
După cum puteți vedea, numesc metoda căutare
pe modelul articolului. Nu am definit-o încă, așa că dacă încercăm să efectuăm o căutare în acest moment, avem o eroare. De asemenea, nu am adăugat o ruta pentru SearchController pe config / routes.rb fișier, deci hai să facem acest lucru:
Rails.application.routes.draw se referă la: resursele "articole # index": articolele primesc "căutare", la: "search # search" end
Dacă ne uităm la documentația pentru bijuterie 'elasticsearch-șine', trebuie să includem două module pe modelele pe care vrem să le indexăm în Elasticsearch, în cazul nostru Article.rb.
solicita clasa "elasticsearch / model" < ActiveRecord::Base include Elasticsearch::Model include Elasticsearch::Model::Callbacks end
Primul model introduce metoda de căutare pe care o folosim în controlerul nostru anterior, printre altele. Cel de-al doilea modul se integrează cu callback-urile ActiveRecord pentru a indexa fiecare instanță a unui articol pe care îl salvăm în baza de date și, de asemenea, actualizează indexul dacă modificăm sau ștergem articolul din baza de date. Deci, este transparent pentru noi.
Dacă ați importat datele mai devreme în baza de date, acele articole nu sunt încă în indexul Elasticsearch; numai cele noi sunt indexate automat. Din acest motiv, trebuie să le indexăm manual și este ușor să începem consola rails
. Atunci trebuie doar să fugim irb (principală)> Articol.import
.
Acum suntem gata să încercăm funcția de căutare. Dacă introduc rubrica și fac clic pe căutare, aici sunt rezultatele:
Pe multe site-uri web, puteți vedea pe pagina cu rezultatele căutării modul în care termenul pe care l-ați căutat este evidențiat. Acest lucru este foarte ușor de făcut folosind Elasticsearch.
Editați | × app / modele / article.rb și modificați metoda de căutare prestabilită:
Definiți: pre_tags: ['interogare', 'căutare', 'căutare', ''], post_tags: [''], câmpuri: title: , text: )
În mod implicit, funcția căutare
metoda este definită de modelele "elasticsearch" de bijuterie, iar obiectul proxy __elasticsearch__ este oferit pentru a accesa clasa de învelitoare pentru API-ul Elasticsearch. Așadar, putem modifica interogarea implicită folosind opțiunile JSON standard furnizate de documentație.
Acum, metoda de căutare va împacheta rezultatele care corespund interogării cu etichetele HTML specificate. Din acest motiv, trebuie să actualizăm și pagina cu rezultatele căutării, astfel încât să putem face etichete HTML în siguranță. Pentru a face acest lucru, editați app / opinii / căutare / search.html.erb.
rezultatele cautarii
<% if @articles %>
<%= snippet.html_safe %>...
<% end %> <% end %>Căutarea dvs. nu a corespuns niciunui document.
<% end %>Adăugați un stil CSS la app / active / stylesheets / search.scss, pentru eticheta marcată:
.search_results em background-color: galben; font-style: normal; font-weight: bold;
Încercați să căutați "rubin" din nou:
După cum puteți vedea, este ușor să evidențiați termenul de căutare, dar nu ideal, deoarece trebuie să trimitem o interogare JSON specificată în documentația Elasticsearch și nu avem nici un fel de abstractizare.
Bijutectura Searchkick este furnizată de Instacart, și este o abstractizare în partea superioară a bijuteriilor oficiale Elasticsearch. Voi reface funcția de evidențiere, așa că începem prin adăugarea gem "searchkick"
la gemfile. Prima clasă pe care trebuie să o schimbăm este modelul Article.rb:
Articol de clasă < ActiveRecord::Base searchkick end
După cum puteți vedea, este mult mai simplu. Trebuie să reanalizăm articolele din nou și să executăm comanda rake searchkick: reindex CLASS = Articol
. Pentru a evidenția termenul de căutare, trebuie să transmitem un parametru suplimentar metodei de căutare de la noi search_controller.rb.
clasa SearchController < ApplicationController def search if params[:term].nil? @articles = [] else term = params[:term] @articles = Article.search term, fields: [:text], highlight: true end end end
Ultimul fișier pe care trebuie să îl modificăm este vizualizari / căutare / search.html.erb deoarece rezultatele sunt returnate într-un format diferit prin searchkick acum:
Cauta rezultate pentru: <%= params[:term] %>
<% if @articles %>
<%= details[:highlight][:text].html_safe %>...
Căutarea dvs. nu a corespuns niciunui document.
<% end %>Acum este timpul să rulați din nou aplicația și să testați funcționalitatea de căutare:
Observați că am introdus ca termen de căutare "dato". Am făcut acest lucru cu scopul de a vă arăta că în mod implicit searchkickeste creat pentru a analiza textul indexat și a fi mai permisiv cu greșelile.
Autosuggest sau typeahead prezice ceea ce un utilizator va tasta, făcând experiența de căutare mai rapidă și mai ușoară. Rețineți că, dacă nu aveți mii de înregistrări, ar fi mai bine să filtrați pe partea clientului.
Să începem prin adăugarea pluginului typeahead, care este disponibil prin intermediul gem "bootstrap-typeahead-șine"
, și adăugați-l la Gemfile. Apoi, trebuie să adăugăm câteva JavaScript app / active / application.js / javascript astfel încât atunci când începeți să tastați în caseta de căutare, apar câteva sugestii.
// = necesita jquery // = cere jquery_ujs // = cere turbolinks // = necesita bootstrap-typeahead-rails // = require_tree. var hot = funcția () var motor = nou Bloodhound (datumTokenizer: funcția (d) consolă.log (d) la distanță: url: '... / search / typeahead /% QUERY'); var promise = motor.initializare (); promite .done (functie () console.log ('succes');)) .fail (function () console.log ('error')); (nume, articol, afișaj: titlu, sursă: engine.ttAdapter ()); $ (Document) .ready (gata); $ (document) .on ('pagina: încărcare', gata);
Câteva comentarii despre fragmentul anterior. În ultimele două rânduri, pentru că nu am dezactivat turbolink-urile, aceasta este modalitatea de a conecta codul pe care vreau să îl rulez la încărcarea paginii. În prima parte a scenariului, puteți vedea că folosesc Bloodhound. Acesta este motorul de sugestie typeahead.js și, de asemenea, configurez punctul final JSON pentru a face cererile AJAX pentru a obține sugestiile. După asta, sună inițializa ()
pe motor și am configurat tiphead în câmpul text de căutare folosind id-ul "term".
Acum, trebuie să facem implementarea backend-ului pentru sugestii, să începem prin adăugarea rutei, să editați app / config / routes.rb.
Rails.application.routes.draw se referă la: "articole # index" resurse: articole "căutare", la: "căutare # căutare" obține "căutare / typeahead /: term '=>
Apoi, voi adăuga implementarea app / controlere / search_controller.rb.
Definiți tipul de afisare a fișierului json: Article.search (params [: term], fields: ["title"], limit: 10, load: false, greșit: below: 5. title: article.title, value: article.id sfârșitul final
Această metodă returnează rezultatele căutării pentru termenul introdus utilizând JSON. Căut doar după titlu, dar aș putea specifica și corpul articolului. De asemenea, limitez numărul de rezultate de căutare la maxim 10.
Acum suntem gata să încercăm implementarea tipului de tip:
După cum puteți vedea, folosirea Elasticsearch cu Rails face căutarea datelor noastre foarte ușor și foarte rapid. Aici v-am arătat cum să utilizați pietrele de joasă nivel oferite de Elasticsearch, precum și bijuteria Searchkick, care este o abstracție care ascunde unele detalii despre modul în care lucrează Elasticsearch.
În funcție de nevoile dvs. specifice, s-ar putea să fiți fericit să utilizați Searchkick și să vă implementați rapid și ușor căutarea dvs. de text întreg. Pe de altă parte, dacă aveți alte interogări complexe, inclusiv filtre sau grupuri, este posibil să trebuiască să aflați mai multe despre detaliile limbii de interogare pe Elasticsearch și să ajungeți să utilizați modelele elastice de pietre de nivel inferior și modelele elasticsearch- șine'.