portaldacalheta.pt
  • Põhiline
  • Veebi Kasutajaliides
  • Ui Disain
  • Andmeteadus Ja Andmebaasid
  • Vilgas
Tehnoloogia

Elasticsearch for Ruby on Rails: õpetus nätskale pärlile



Elasticsearch pakub võimsa, RESTful HTTP-liidese andmete indekseerimiseks ja päringuteks, mis on üles ehitatud Apache Lucene raamatukogu. Kohe karbist välja pakub see skaleeritavat, tõhusat ja tugevat otsingut koos UTF-8 toega. See on võimas tööriist suure hulga struktureeritud andmete indekseerimiseks ja päringute tegemiseks ning siin Ahvi põgenemine , see toetab meie platvormi otsingut ja seda kasutatakse varsti ka automaatse täitmise jaoks. Oleme tohutud fännid.

Chewy laiendab Elasticsearch-Ruby klienti, muutes selle võimsamaks ja pakkudes tihedamat integreerimist Railsiga.

Kuna meie platvorm on üles ehitatud Rubiin rööbastel , kasutab meie Elasticsearchi integreerimine järgmist elasticsearch-ruby projekt (Ruby integratsiooniraamistik Elasticsearchile, mis pakub klienti ühenduse loomiseks Elasticsearchi klastriga, Ruby API Elasticsearchi REST API jaoks ning mitmesugused laiendused ja utiliidid). Sellele vundamendile tuginedes oleme välja töötanud ja välja andnud oma paranduse (ja lihtsustuse) Elasticsearchi rakenduse otsinguarhitektuuris, mis on pakitud meie poolt nimetatud rubiinkiviks Nätske (saadaval on näidisrakendus siin ).



Chewy laiendab Elasticsearch-Ruby klienti, muutes selle võimsamaks ja pakkudes tihedamat integreerimist Railsiga. Selles Elasticsearchi juhendis arutlen (kasutusnäidete kaudu), kuidas me selle saavutasime, sealhulgas rakendamise käigus tekkinud tehnilised takistused.



Elasticsearchi ja Ruby on Rails suhet on kujutatud selles visuaalses juhendis.



Enne juhendiga jätkamist piisab vaid paarist kiirest märkmest:

  • Mõlemad Nätske ja a Nätske demorakendus on saadaval GitHubis.
  • Neile, kes on huvitatud Elasticsearchi kohta rohkem 'kapoti all' olevast teabest, lisasin lühikese kirjutise Lisa selle postituse juurde.

Miks nätske?

Vaatamata Elasticsearchi mastaapsusele ja efektiivsusele ei osutunud selle integreerimine Railsiga nii lihtsaks, kui arvati. ApeeScape'is leidsime, et peame Elasticsearch-Ruby põhiklienti oluliselt täiendama, et muuta see toimivamaks ja toetada täiendavaid toiminguid.



Vaatamata Elasticsearchi mastaapsusele ja efektiivsusele ei osutunud selle integreerimine Railsiga nii lihtsaks, kui arvati.

Ja nii sündiski nätsu pärl.

Chewy mõned eriti tähelepanuväärsed omadused on:



  1. Iga indeks on jälgitav kõigi seotud mudelite järgi.

    graafiline disain vs visuaalne disain

    Enamik indekseeritud mudeleid on omavahel seotud. Mõnikord on vaja need seotud andmed denormaliseerida ja siduda sama objektiga (nt kui soovite märgendite massiivi koos nendega seotud artikliga indekseerida). Chewy võimaldab teil määrata iga mudeli jaoks värskendatava registri, nii et vastavad artiklid lisatakse uuesti iga kord, kui asjakohast silti värskendatakse.



  2. Indeksiklassid ei sõltu ORM / ODM mudelitest.

    Selle täiustusega on näiteks mudelitevahelise automaatse täitmise rakendamine palju lihtsam. Saate lihtsalt määratleda indeksi ja töötada sellega objektorienteeritud viisil. Erinevalt teistest klientidest eemaldab Chewy gem vajaduse käsitsi juurutada indeksiklassid, andmete impordi tagasihelistamised ja muud komponendid.



  3. Hulgimport on kõikjal .

    Chewy kasutab täieliku reindeksimise ja registrivärskenduste jaoks hulgi Elasticsearch API-d. Samuti kasutab see aatomiuuenduste kontseptsiooni, kogudes muutunud objektid aatomiplokis ja värskendades neid kõiki korraga.



  4. Chewy pakub AR-stiilis päringut DSL.

    Olles aheldatav, ühendatav ja laisk, võimaldab see täiustus päringuid tõhusamalt koostada.

OK, nii et vaatame, kuidas see kõik pärlil läbi mängib ...

Elasticsearchi põhijuht

Elasticsearchil on mitu dokumendiga seotud mõistet. Esimene on index (a database analoog in RDBMS ), mis koosneb documents komplektist, mis võib olla mitu types (kus type on omamoodi RDBMS-tabel).

Igas dokumendis on komplekt fields. Iga välja analüüsitakse iseseisvalt ja selle analüüsivõimalused salvestatakse kausta mapping selle tüübi järgi. Chewy kasutab seda struktuuri 'sellisena nagu see on' oma objektimudelis:

class EntertainmentIndex { author.name } field :author_id, type: 'integer' field :description field :tags, index: 'not_analyzed', value: ->{ tags.map(&:name) } end {movie: Video.movies, cartoon: Video.cartoons}.each do |type_name, scope| define_type scope.includes(:director, :tags), name: type_name do field :title, analyzer: 'title' field :year, type: 'integer' field :author, value: ->{ director.name } field :author_id, type: 'integer', value: ->{ director_id } field :description field :tags, index: 'not_analyzed', value: ->{ tags.map(&:name) } end end end

Eespool määratlesime Elasticsearchi indeksi nimega entertainment kolme tüüpi: book, movie ja cartoon. Iga tüübi jaoks määrasime kogu indeksi jaoks mõned väljade vastendused ja sätete räsi.

disainikunsti määratlemise põhimõtted

Niisiis oleme määratlenud EntertainmentIndex ja me tahame täita mõned päringud. Esimese sammuna peame looma registri ja importima oma andmed:

EntertainmentIndex.create! EntertainmentIndex.import # EntertainmentIndex.reset! (which includes deletion, # creation, and import) could be used instead

.import meetod on teadlik imporditud andmetest, kuna oma tüüpide määratlemisel edastasime selle ulatusega; seega impordib see kõik püsiladustuses olevad raamatud, filmid ja koomiksid.

Kui see on tehtud, saame esitada mõned päringud:

EntertainmentIndex.query(match: {author: 'Tarantino'}).filter{ year > 1990 } EntertainmentIndex.query(match: {title: 'Shawshank'}).types(:movie) EntertainmentIndex.query(match: {author: 'Tarantino'}).only(:id).limit(10).load # the last one loads ActiveRecord objects for documents found

Nüüd on meie register peaaegu valmis kasutamiseks otsingu rakendamisel.

Rööbaste integreerimine

Railsiga integreerimiseks on esimene asi, mida me oskame reageerida RDBMS-i objekti muudatustele. Chewy toetab seda käitumist tagasihelistamise kaudu, mis on määratletud update_index klassi meetod. update_index võtab kaks argumenti:

  1. Tüübi identifikaator, mis antakse väljale 'index_name#type_name' vormingus
  2. Meetodi nimi või käivitatav plokk, mis tähistab tagasiviidet värskendatud objektile või objektikogule

Peame määratlema need tagasihelistamised iga sõltuva mudeli jaoks:

class Book

Kuna sildid on ka indekseeritud, peame järgmiseks mõned välised mudelid ahvile lappima, et need reageeriksid muudatustele:

ActsAsTaggableOn::Tag.class_eval do has_many :books, through: :taggings, source: :taggable, source_type: 'Book' has_many :videos, through: :taggings, source: :taggable, source_type: 'Video' # Updating all tag-related objects update_index 'entertainment#book', :books update_index('entertainment#movie') { videos.movies } update_index('entertainment#cartoon') { videos.cartoons } end ActsAsTaggableOn::Tagging.class_eval do # Same goes for the intermediate model update_index('entertainment#book') { taggable if taggable_type == 'Book' } update_index('entertainment#movie') { taggable if taggable_type == 'Video' && taggable.movie? } update_index('entertainment#cartoon') { taggable if taggable_type == 'Video' && taggable.cartoon? } end

Siinkohal iga objekt salvesta või hävitama värskendab vastavat Elasticsearchi indeksitüüpi.

Aatomilisus

Üks püsiv probleem on meil endiselt. Kui teeme midagi sellist books.map(&:save) mitme raamatu salvestamiseks palume värskendada entertainment indeks iga kord, kui üksik raamat salvestatakse . Seega, kui salvestame viis raamatut, värskendame Chewy indeksit viis korda. See käitumine on aktsepteeritav VASTUS , kuid kindlasti pole vastuvõetav kontrolleri toimingute puhul, mille jõudlus on kriitiline.

Me käsitleme seda probleemi Chewy.atomic -ga plokk:

class ApplicationController

Lühidalt öeldes: Chewy.atomic paketab need värskendused järgmiselt:

angularjs sisendi kinnitamine ilma vormita
  1. Keelab after_save helista tagasi.
  2. Kogub salvestatud raamatute ID-sid.
  3. Pärast Chewy.atomic plokk, kasutab kogutud ID-sid ühe Elasticsearchi indeksi värskendustaotluse tegemiseks.

Otsimine

Nüüd oleme valmis kasutama otsinguliidest. Kuna meie kasutajaliides on vorm, on loomulikult parim viis selle loomiseks FormBuilder ja ActiveModel . (ApeeScape'is kasutame ActiveData ActiveModeli liideste juurutamiseks, kuid kasutage julgelt oma lemmik kalliskivi.)

class EntertainmentSearch include ActiveData::Model attribute :query, type: String attribute :author_id, type: Integer attribute :min_year, type: Integer attribute :max_year, type: Integer attribute :tags, mode: :arrayed, type: String, normalize: ->(value) { value.reject(&:blank?) } # This accessor is for the form. It will have a single text field # for comma-separated tag inputs. def tag_list= value self.tags = value.split(',').map(&:strip) end def tag_list self.tags.join(', ') end end

Päringute ja filtrite õpetus

Nüüd, kui meil on ActiveModeli-laadne objekt, mis suudab atribuute aktsepteerida ja tüüpilisendada, rakendame otsingu:

class EntertainmentSearch ... def index EntertainmentIndex end def search # We can merge multiple scopes [query_string, author_id_filter, year_filter, tags_filter].compact.reduce(:merge) end # Using query_string advanced query for the main query input def query_string index.query(query_string: {fields: [:title, :author, :description], query: query, default_operator: 'and'}) if query? end # Simple term filter for author id. `:author_id` is already # typecasted to integer and ignored if empty. def author_id_filter index.filter(term: {author_id: author_id}) if author_id? end # For filtering on years, we will use range filter. # Returns nil if both min_year and max_year are not passed to the model. def year_filter body = {}.tap do |body| body.merge!(gte: min_year) if min_year? body.merge!(lte: max_year) if max_year? end index.filter(range: {year: body}) if body.present? end # Same goes for `author_id_filter`, but `terms` filter used. # Returns nil if no tags passed in. def tags_filter index.filter(terms: {tags: tags}) if tags? end end

Kontrollerid ja vaated

Siinkohal saab meie mudel esitada päringuid edastatud atribuutidega. Kasutamine näeb välja umbes selline:

EntertainmentSearch.new(query: 'Tarantino', min_year: 1990).search

Pange tähele, et kontrollerisse soovime laadida täpsed ActiveRecordi objektid Nätske dokumendipakendid:

class EntertainmentController

Nüüd on aeg mõned üles kirjutada HAML kell entertainment/index.html.haml:

= form_for @search, as: :search, url: entertainment_index_path, method: :get do |f| = f.text_field :query = f.select :author_id, Dude.all.map d, include_blank: true = f.text_field :min_year = f.text_field :max_year = f.text_field :tag_list = f.submit - if @entertainments.any? %dl - @entertainments.each do |entertainment| %dt %h1= entertainment.title %strong= entertainment.class %dd %p= entertainment.year %p= entertainment.description %p= entertainment.tag_list = paginate @entertainments - else Nothing to see here

Sorteerimine

Boonusena lisame oma otsingufunktsioonidele ka sortimise.

Oletame, et peame sortima nii pealkirja ja aasta väljadel kui ka asjakohasuse järgi. Kahjuks on pealkiri One Flew Over the Cuckoo's Nest jaotatakse üksikuteks terminiteks, nii et sortimine nende erinevate terminite järgi on liiga juhuslik; selle asemel tahaksime sortida kogu pealkirja järgi.

Lahendus on kasutada spetsiaalset pealkirjavälja ja rakendada oma analüsaatorit:

class EntertainmentIndex

Lisaks lisame oma otsingumudelile nii need uued atribuudid kui ka sortimise töötlemise etapi:

class EntertainmentSearch # we are going to use `title.sorted` field for sort SORT = {title: {'title.sorted' => :asc}, year: {year: :desc}, relevance: :_score} ... attribute :sort, type: String, enum: %w(title year relevance), default_blank: 'relevance' ... def search # we have added `sorting` scope to merge list [query_string, author_id_filter, year_filter, tags_filter, sorting].compact.reduce(:merge) end def sorting # We have one of the 3 possible values in `sort` attribute # and `SORT` mapping returns actual sorting expression index.order(SORT[sort.to_sym]) end end

Lõpuks muudame oma vormi, lisades sortimisvalikute valikukasti:

= form_for @search, as: :search, url: entertainment_index_path, method: :get do |f| ... / `EntertainmentSearch.sort_values` will just return / enum option content from the sort attribute definition. = f.select :sort, EntertainmentSearch.sort_values ...

Vigade käitlemine

Kui teie kasutajad esitavad valesid päringuid, näiteks ( või AND, põhjustab Elasticsearchi klient tõrke. Selle lahendamiseks tehkem kontrolleris mõned muudatused:

class EntertainmentController e @entertainments = [] @error = e.message.match(/QueryParsingException[([^;]+)]/).try(:[], 1) end end

Lisaks peame vea renderdama vaates:

... - if @entertainments.any? ... - else - if @error = @error - else Nothing to see here

Elasticsearchi päringute testimine

Testimise põhiseadistus on järgmine:

  1. Käivitage Elasticsearchi server.
  2. Koristage ja looge meie indeksid.
  3. Importige meie andmed.
  4. Tehke meie päring.
  5. Võrdlege tulemus meie ootustega.

1. etapi jaoks on mugav kasutada jaotises määratletud testiklastrit elasticsearch-extensions pärl. Lisage lihtsalt järgmine rida oma projekti Rakefile pärlijärgne install:

require 'elasticsearch/extensions/test/cluster/tasks'

Seejärel saate järgmise Reha ülesanded:

$ rake -T elasticsearch rake elasticsearch:start # Start Elasticsearch cluster for tests rake elasticsearch:stop # Stop Elasticsearch cluster for tests

Elasticsearch ja Rspec

Esiteks peame veenduma, et meie indeksit värskendatakse, et see oleks meie andmete muutustega sünkroonne. Õnneks on Chewy pärl kaasas abivalmis update_index rspec tikud:

describe EntertainmentIndex do # No need to cleanup Elasticsearch as requests are # stubbed in case of `update_index` matcher usage. describe 'Tag' do # We create several books with the same tag let(:books) { create_list :book, 2, tag_list: 'tag1' } specify do # We expect that after modifying the tag name... expect do ActsAsTaggableOn::Tag.where(name: 'tag1').update_attributes(name: 'tag2') # ... the corresponding type will be updated with previously-created books. end.to update_index('entertainment#book').and_reindex(books, with: {tags: ['tag2']}) end end end

Järgmisena peame testima, kas tegelikud otsingupäringud täidetakse õigesti ja kas need tagavad oodatud tulemused:

describe EntertainmentSearch do # Just defining helpers for simplifying testing def search attributes = {} EntertainmentSearch.new(attributes).search end # Import helper as well def import *args # We are using `import!` here to be sure all the objects are imported # correctly before examples run. EntertainmentIndex.import! *args end # Deletes and recreates index before every example before { EntertainmentIndex.purge! } describe '#min_year, #max_year' do let(:book) { create(:book, year: 1925) } let(:movie) { create(:movie, year: 1970) } let(:cartoon) { create(:cartoon, year: 1995) } before { import book: book, movie: movie, cartoon: cartoon } # NOTE: The sample code below provides a clear usage example but is not # optimized code. Something along the following lines would perform better: # `specify { search(min_year: 1970).map(&:id).map(&:to_i) # .should =~ [movie, cartoon].map(&:id) }` specify { search(min_year: 1970).load.should =~ [movie, cartoon] } specify { search(max_year: 1980).load.should =~ [book, movie] } specify { search(min_year: 1970, max_year: 1980).load.should == [movie] } specify { search(min_year: 1980, max_year: 1970).should == [] } end end

Testige klastri tõrkeotsingut

Lõpuks on siin testklastri tõrkeotsingu juhend:

  • Alustamiseks kasutage mälusisest ühe sõlme klastrit. Spetsifikatsioonide jaoks on see palju kiirem. Meie puhul: TEST_CLUSTER_NODES=1 rake elasticsearch:start

  • elasticsearch-extensions -Ga on probleeme testige klastri juurutamist ise, mis on seotud ühe sõlmega klastri olekukontrolliga (see on mõnel juhul kollane ja pole kunagi roheline, nii et rohelise olekuga klastri käivitamise kontroll ebaõnnestub iga kord). Küsimus on fikseeritud kahvlis, kuid loodetavasti saab see peagi põhirepois korda.

  • Iga andmekogumi jaoks grupeerige oma taotlus spetsifikatsioonidesse (st importige oma andmed üks kord ja täitke seejärel mitu taotlust). Elasticsearch soojeneb pikka aega ja kasutab andmete importimisel palju kuhjaga mälu, nii et ärge üle pingutage, eriti kui teil on hunnik spetsifikatsioone.

  • Veenduge, et teie masinal on piisavalt mälu, vastasel juhul Elasticsearch hangub (iga virtuaalse masina testimiseks vajame umbes 5 GB ja Elasticsearchi enda jaoks umbes 1 GB).

    @meediumiekraan ja (maksimaalne laius

Pakkimine

Elasticsearchi nimetatakse ise „paindlikuks ja võimsaks avatud lähtekoodiga, hajutatud, reaalajas otsingu- ja analüüsimootoriks”. See on otsingutehnoloogiate kuldstandard.

Chewyga meie rööbaste arendajad on need eelised pakkinud lihtsa, hõlpsasti kasutatava, kvaliteetse ja avatud lähtekoodiga Ruby pärlina, mis tagab Railsiga tiheda integreerimise. Elasticsearch ja Rails - kui vinge kombinatsioon!

Elasticsearch ja Rails - kui vinge kombinatsioon! Piiksuma


Lisa: Elasticsearchi sisemine osa

Siin on a väga Elasticsearchi lühike sissejuhatus 'kapoti all' ...

Elasticsearch on üles ehitatud Lucene , mida ise kasutab ümberpööratud indeksid selle esmase andmestruktuurina. Näiteks kui meil on stringid „koerad hüppavad kõrgele”, „hüppavad üle aia” ja „tara oli liiga kõrge”, saame järgmise struktuuri:

'the' [0, 0], [1, 2], [2, 0] 'dogs' [0, 1] 'jump' [0, 2], [1, 0] 'high' [0, 3], [2, 4] 'over' [1, 1] 'fence' [1, 3], [2, 1] 'was' [2, 2] 'too' [2, 3]

Seega sisaldab iga termin nii viiteid tekstile kui ka positsioone selles. Lisaks otsustame oma tingimusi muuta (nt eemaldades stopp-sõnad nagu “the”) ja rakendada foneetiline räsimine igale terminile (kas oskate arvata algoritm ?):

'DAG' [0, 1] 'JANP' [0, 2], [1, 0] 'HAG' [0, 3], [2, 4] 'OVAR' [1, 1] 'FANC' [1, 3], [2, 1] 'W' [2, 2] 'T' [2, 3]

Kui küsime siis sõna 'koer hüppab', analüüsitakse seda samamoodi nagu lähteteksti, muutudes pärast räsimist 'DAG JANPiks' ('koeral' on sama räsi kui 'koertel', nagu ka 'hüpped' ja “Hüpe”).

Lisame stringi üksikute sõnade vahele ka teatud loogika (lähtudes konfiguratsiooniseadetest), valides („DAG“ JA „JANP“) või („DAG“ VÕI „JANP“). Esimene tagastab [0] & [0, 1] ristmiku (s.t dokument 0) ja viimane [0] | [0, 1] (st dokumendid 0 ja 1). Tekstisiseseid positsioone saab kasutada tulemuste ja asukohast sõltuvate päringute hindamiseks.

GraphQL vs REST - GraphQLi õpetus

Tagumine Ots

GraphQL vs REST - GraphQLi õpetus
Strateegiad teie majanduslanguse vastu

Strateegiad teie majanduslanguse vastu

Kasumlikkus Ja Tõhusus

Lemmik Postitused
Raha kogumise pigi teki kunst
Raha kogumise pigi teki kunst
Amazon vs. Walmart: Bezos läheb kogu toiduainete omandamisega jugulaarseks
Amazon vs. Walmart: Bezos läheb kogu toiduainete omandamisega jugulaarseks
Bootstrapped: kaugettevõtte ehitamine
Bootstrapped: kaugettevõtte ehitamine
Bootstrapi kasutamine ja .NET-projektide loomine
Bootstrapi kasutamine ja .NET-projektide loomine
Kuidas luua meilisõnumite analüüsi bot: NLP-õpetus.
Kuidas luua meilisõnumite analüüsi bot: NLP-õpetus.
 
Kommunikatsioonidirektor
Kommunikatsioonidirektor
Kuidas vältida funktsioonide libisemist kasutajalugude parimate tavade abil
Kuidas vältida funktsioonide libisemist kasutajalugude parimate tavade abil
Täpsem Git-juhend: Git Stash, Reset, Rebase ja palju muud
Täpsem Git-juhend: Git Stash, Reset, Rebase ja palju muud
Disainihariduse tähtsus
Disainihariduse tähtsus
KPI-d edu saavutamiseks - ülevaade projektijuhi jõudlusmõõdikutest
KPI-d edu saavutamiseks - ülevaade projektijuhi jõudlusmõõdikutest
Lemmik Postitused
  • liitreaalsus vs segareaalsus
  • javascript Typeerror ei ole funktsioon
  • kuidas Monte Carlo simulatsiooni
  • c corp vs s corp tax
  • mis on buutstrapi veebisait
  • tarkvaraarenduse spetsifikatsioonidokument
Kategooriad
  • Veebi Kasutajaliides
  • Ui Disain
  • Andmeteadus Ja Andmebaasid
  • Vilgas
  • © 2022 | Kõik Õigused Kaitstud

    portaldacalheta.pt