Warnung
Das Juwel ist derzeit nicht aufrechterhalten und die Entwicklung wird auf Eis gelegt. Wenn Sie an der Übernahme interessiert sind, können Sie sich gerne an mich wenden.
Benötigen Sie eine schnelle Volltext-Suche nach Ihrem Ruby-Skript, aber Solr und Elasticsearch sind ein Overkill? ?
Du bist am richtigen Ort. Tantiny ist eine minimalistische Volltext-Suchbibliothek für Ruby, die auf Tanti V y basiert (eine großartige Alternative zu Apache Lucene in Rost). Es eignet sich hervorragend für Fälle, in denen Ihre Aufgabe eine Volltext-Suche erfordert, aber die Konfiguration einer ausgewachsenen verteilten Suchmaschine würde mehr Zeit in Anspruch nehmen als die Aufgabe selbst. Und selbst wenn Sie eine solche Engine in Ihrem Projekt bereits verwenden (was tatsächlich sehr wahrscheinlich ist), kann es immer einfacher sein, Tantiny nur zu verwenden, da es im Gegensatz zu Solr und Elasticsearch nichts benötigt, um zu funktionieren (kein separater Server oder Prozess oder was auch immer), ist es nur eingebettbar. Wenn Sie sich also in einer Situation befinden, wenn Sie Ihre Suchmaschine der Wahl verwenden, ist es schwierig/unangebracht oder erfordern zusätzliche Setup, Sie können jederzeit zu einer schnellen und schmutzigen Lösung zurückkehren, die trotzdem flexibel und schnell ist.
Tantiny ist nicht gerade Rubinbindungen an Tantivy, sondern versucht, nahe zu sein. Die Hauptphilosophie besteht darin, einen niedrigen Zugang zu Tantivys invertiertem Index zu gewährleisten, aber mit einer schönen Rubin-ähnlichen API, vernünftigen Standardeinstellungen und zusätzlichen Funktionalität.
Schauen Sie sich das grundlegendste Beispiel an:
index = Tantiny :: Index . new ( "/path/to/index" ) { text :description }
index << { id : 1 , description : "Hello World!" }
index << { id : 2 , description : "What's up?" }
index << { id : 3 , description : "Goodbye World!" }
index . reload
index . search ( "world" ) # 1, 3 Fügen Sie diese Zeile der GemFile Ihrer Anwendung hinzu:
gem "tantiny"Und dann ausführen:
$ bundle install
Oder installieren Sie es selbst als:
$ gem install tantiny
Sie müssen Rost in Ihrem System nicht installieren lassen, da Tantiny versucht, die vorgefertigten Binärdateien während der Installation auf Github-Releases zu laden. Wenn jedoch für Ihr System (eine Kombination aus Plattform, Architektur und Ruby-Version) jedoch keine vorgefertigten Binärdateien gefunden wurden, müssen Sie zuerst Rost installieren.
Warnung
Es werden nur Rostversionen bis zu 1.77 unterstützt. Weitere Informationen finden Sie in dieser Ausgabe.
Wichtig
Bitte geben Sie die Nebenversion an, wenn Sie die Abhängigkeit von tantiny erklären. Die API ist ein Subjekt, das sich ändern kann, und bis sie 1.0.0 erreicht, wird eine Beule in der kleinen Version höchstwahrscheinlich eine Breaking -Änderung bedeuten.
Sie müssen einen Pfad zu dem, an dem der Index gespeichert wird, und einen Block angeben, der das Schema definiert:
Tantiny :: Index . new "/tmp/index" do
id :imdb_id
facet :category
string :title
text :description
integer :duration
double :rating
date :release_date
endHier sind die Beschreibungen für jeden Feldtyp:
| Typ | Beschreibung |
|---|---|
| Ausweis | Gibt an, wo die IDs von Dokumenten gespeichert sind (Standardeinstellungen an :id ). |
| Facette | Felder mit Werten wie /animals/birds (dh hierarchielle Kategorien). |
| Saite | Felder mit Text, die nicht tokenisiert werden. |
| Text | Felder mit Text, die vom angegebenen Tokenizer tokenisiert werden. |
| ganze Zahl | Felder mit Ganzzahlwerten. |
| doppelt | Felder mit Float -Werten. |
| Datum | Felder mit entweder DateTime -Typ oder etwas, das sich darauf wandelt. |
Sie können den Index jede Art von Objekt füttern, das in Ihrem Schema Methoden angegeben hat, aber auch Hashes funktionieren auch:
rio_bravo = OpenStruct . new (
imdb_id : "tt0053221" ,
type : '/western/US' ,
title : "Rio Bravo" ,
description : "A small-town sheriff enlists a drunk, a kid and an old man to help him fight off a ruthless cattle baron." ,
duration : 141 ,
rating : 8.0 ,
release_date : Date . parse ( "March 18, 1959" )
)
index << rio_bravo
hanabi = {
imdb_id : "tt0119250" ,
type : "/crime/Japan" ,
title : "Hana-bi" ,
description : "Nishi leaves the police in the face of harrowing personal and professional difficulties. Spiraling into depression, he makes questionable decisions." ,
duration : 103 ,
rating : 7.7 ,
release_date : Date . parse ( "December 1, 1998" )
}
index << hanabi
brother = {
imdb_id : "tt0118767" ,
type : "/crime/Russia" ,
title : "Brother" ,
description : "An ex-soldier with a personal honor code enters the family crime business in St. Petersburg, Russia." ,
duration : 99 ,
rating : 7.9 ,
release_date : Date . parse ( "December 12, 1997" )
}
index << brotherUm das Dokument zu aktualisieren, fügen Sie es einfach erneut hinzu (solange die ID gleich ist):
rio_bravo . rating = 10.0
index << rio_bravoSie können es auch löschen, wenn Sie möchten:
index . delete ( rio_bravo . imdb_id ) Wenn Sie mehrere Schreibvorgänge ausführen müssen (dh mehr als eins), sollten Sie immer transaction verwenden:
index . transaction do
index << rio_bravo
index << hanabi
index << brother
end Transaktionengruppen ändert sich und verpflichten sie in einem Go zum Index. Dies ist dramatisch effizienter als diese Änderungen nacheinander durchzuführen. In << Tat sind alle Schreibvorgänge (dh << und delete ) in eine Transaktion eingewickelt, wenn Sie sie außerhalb einer Transaktion aufrufen. Wenn Sie also 10 -mal außerhalb einer Transaktion aufrufen, ist es dasselbe wie die Durchführung von 10 separaten Transaktionen.
Tantiny ist thread-sicher, was bedeutet, dass Sie sicher eine einzelne Instanz des Index zwischen Threads teilen können. Sie können auch separate Prozesse hervorbringen, die demselben Index schreiben und aus lesen könnten. Während das Lesen aus dem Index parallel sein sollte, ist es nicht . Wenn Sie transaction oder eine andere Operation aufrufen, die den Index (dh << und delete ) ändern, sperrt der Index für die Dauer des Vorgangs oder wartet auf einen anderen Vorgang oder Thread, um die Sperre freizugeben. Die einzige Ausnahme ist, wenn es einen weiteren Prozess mit einem Index mit einem exklusiven Schriftsteller gibt, der irgendwo in diesem Fall ausgeführt wird, in welchem Fall die Methoden, die den Index ändern, sofort fehlschlagen.
Daher ist es am besten, einen einzelnen Schriftstellerprozess und viele Leserprozesse zu haben, wenn Sie es vermeiden möchten, Anrufe zu blockieren. Der richtige Weg, dies zu tun, besteht darin, exclusive_writer auf true zu setzen, wenn der Index initialisiert wird:
index = Tantiny :: Index . new ( "/path/to/index" , exclusive_writer : true ) { }Auf diese Weise wird der Indexschreiber nur einmal erfasst, was bedeutet, dass das Speicher dafür und die Indizierungsthreads nur einmal zugewiesen werden. Andernfalls wird jedes Mal, wenn Sie einen Schreibvorgang durchführen, ein neuer Indexautor erworben.
Stellen Sie sicher, dass Ihr Index auf dem neuesten Stand ist, indem Sie ihn zuerst neu laden:
index . reloadUnd durchsuchen (endlich!):
index . search ( "a drunk, a kid, and an old man" )Standardmäßig werden IDs von 10 besten übereinstimmenden Dokumenten zurückgegeben. Sie können es jedoch anpassen:
index . search ( "a drunk, a kid, and an old man" , limit : 100 ) Sie fragen sich vielleicht, wie genau es die Suche durchführt? Nun, das Standardverhalten besteht darin, smart_query -Suche (siehe unten für Details) über alle in Ihrem Schema definierten text zu verwenden. Sie können also die Parameter übergeben, die das smart_query genau hier akzeptiert:
index . search ( "a dlunk, a kib, and an olt mab" , fuzzy_distance : 1 )Sie können es jedoch anpassen, indem Sie Ihre eigene Abfrage aus grundlegenden Bausteinen verfassen:
popular_movies = index . range_query ( :rating , 8.0 .. 10.0 )
about_sheriffs = index . term_query ( :description , "sheriff" )
crime_movies = index . facet_query ( :cetegory , "/crime" )
long_ass_movies = index . range_query ( :duration , 180 .. 9999 )
something_flashy = index . smart_query ( :description , "bourgeoisie" )
index . search ( ( popular_movies & about_sheriffs ) | ( crime_movies & ! long_ass_movies ) | something_flashy )Ich weiß, seltsamer Geschmack! Aber ziemlich cool, oder? Schauen Sie sich alle verfügbaren Fragen unten an.
| Abfrage | Verhalten |
|---|---|
| All_query | Gibt alle indizierten Dokumente zurück. |
| leere_query | Gibt genau nichts zurück (intern). |
| Term_query | Dokumente, die den angegebenen Begriff enthalten. |
| fuzzy_term_query | Dokumente, die den angegebenen Begriff in einem Levenshtein -Abstand enthalten. |
| Phrase_Query | Dokumente, die die angegebene Abfolge von Begriffen enthalten. |
| regex_query | Dokumente, die einen Begriff enthalten, der dem angegebenen Regex entspricht. |
| Präfix_Query | Dokumente, die einen Begriff mit dem angegebenen Präfix enthalten. |
| Range_query | Dokumente, die mit einem integer , double oder date innerhalb des angegebenen Bereichs. |
| faceet_query | Dokumente, die zur angegebenen Kategorie gehören. |
| Smart_query | Eine Kombination aus term_query , fuzzy_term_query und prefix_query . |
Schauen Sie sich die Signaturdatei an, um zu sehen, welche Parameter Fragen akzeptieren.
Alle Abfragen können auf Multuple -Feldern suchen (mit Ausnahme von facet_query , da es dort keinen Sinn ergibt).
Also die folgende Frage:
index . term_query ( %i[ title description ] , "hello" )Entspricht:
index . term_query ( :title , "hello" ) | index . term_query ( :description , "hello" ) Alle Abfragen unterstützen den boost -Parameter, der es ermöglicht, die Position der Dokumente in der Suche zu stoßen:
about_cowboys = index . term_query ( :description , "cowboy" , boost : 2.0 )
about_samurai = index . term_query ( :description , "samurai" ) # sorry, Musashi...
index . search ( about_cowboys | about_samurai )smart_query Verhalten Die smart_query -Suche extrahiert Begriffe aus Ihrem Abfragezeichenfolge unter Verwendung der jeweiligen Feld Tokenizer und durchsucht den Index nach Dokumenten, die diese Begriffe über die term_query enthalten. Wenn der Parameter fuzzy_distance angegeben wird, wird er die fuzzy_term_query verwendet. Außerdem kann der letzte Begriff durch die Verwendung des prefix_query unvollendet sein.
Also die folgende Frage:
index . smart_query ( %i[ en_text ru_text ] , "dollars рубли eur" , fuzzy_distance : 1 )Entspricht:
t1_en = index . fuzzy_term_query ( :en_text , "dollar" )
t2_en = index . fuzzy_term_query ( :en_text , "рубли" )
t3_en = index . fuzzy_term_query ( :en_text , "eur" )
t3_prefix_en = index . prefix_query ( :en_text , "eur" )
t1_ru = index . fuzzy_term_query ( :ru_text , "dollars" )
t2_ru = index . fuzzy_term_query ( :ru_text , "рубл" )
t3_ru = index . fuzzy_term_query ( :ru_text , "eur" )
t3_prefix_ru = index . prefix_query ( :ru_text , "eur" )
( t1_en & t2_en & ( t3_en | t3_prefix_en ) ) | ( t1_ru & t2_ru & ( t3_ru | t3_prefix_ru ) ) Beachten Sie, wie Wörter "Dollar" und "рбли" je nach Feld, in dem wir suchen, unterschiedlich eingebaut sind. Dies setzt voraus, dass wir in unserem Schema en_text und ru_text -Felder haben, die englische bzw. russische Stemmer -Tokenizer verwenden.
regex_query Das regex_query akzeptiert das Regex -Muster, muss aber ein Rostregex sein, kein Ruby Regexp . Anstelle von index.regex_query(:description, /hel[lp]/) müssen Sie index.regex_query(:description, "hel[lp]") verwenden. Randnotiz ist der regex_query ziemlich schnell, da sie die FST -Kiste intern verwendet.
Wir haben also noch mehr als einmal Tokenizer erwähnt. Was sind sie?
Tokenizer ist das, was Tantivy verwendet, um Ihren Text auf Begriffe zu hacken, um einen umgekehrten Index zu erstellen. Dann können Sie den Index nach diesen Begriffen durchsuchen. Es ist ein wichtiges Konzept zu verstehen, dass Sie nicht verwirrt werden, wenn index.term_query(:description, "Hello") nichts zurückgibt, weil Hello kein Begriff ist, aber hello ist. Sie müssen die Begriffe aus der Abfrage extrahieren, bevor Sie den Index durchsuchen. Derzeit macht nur smart_query das für Sie. Außerdem ist der einzige Feldtyp, der tokenisiert wird, text . Für string -Felder sollten Sie also die genaue Übereinstimmung verwenden (dh index.term_query(:title, "Hello") ).
Standardmäßig wird der simple Tokenizer verwendet. Sie können jedoch den gewünschten Tokenizer über Indexoptionen oder lokal über Feldspezifische Optionen global angeben:
en_stemmer = Tantiny :: Tokenizer . new ( :stemmer )
ru_stemmer = Tantiny :: Tokenizer . new ( :stemmer , language : :ru )
Tantiny :: Index . new "/tmp/index" , tokenizer : en_stemmer do
text :description_en
text :description_ru , tokenizer : ru_stemmer
endEinfache Tokenizer schottet den Text über Zeichensetzung und Whitespaces, entfernt lange Token und senkt den Text.
tokenizer = Tantiny :: Tokenizer . new ( :simple )
tokenizer . terms ( "Hello World!" ) # ["hello", "world"]Stemmer -Tokenizer sind genau wie ein einfacher Tokenizer, aber mit zusätzlichem Stamm entsprechend der angegebenen Sprache (Standardeinstellungen für Englisch).
tokenizer = Tantiny :: Tokenizer . new ( :stemmer , language : :ru )
tokenizer . terms ( "Привет миру сему!" ) # ["привет", "мир", "сем"]Schauen Sie sich die Quelle an, um zu sehen, welche Sprachen unterstützt werden.
Ngram Tokenizer schüttelt Ihren Text auf Ngrams der angegebenen Größe.
tokenizer = Tantiny :: Tokenizer . new ( :ngram , min : 5 , max : 10 , prefix_only : true )
tokenizer . terms ( "Morrowind" ) # ["Morro", "Morrow", "Morrowi", "Morrowin", "Morrowind"] Möglicherweise haben Sie festgestellt, dass die search nur Dokumente -IDs zurückgibt. Dies ist aus Design. Die Dokumente selbst werden im Index nicht gespeichert. Tantiny ist eine minimalistische Bibliothek, daher versucht sie, die Dinge einfach zu halten. Wenn Sie ein vollständiges Dokument abrufen müssen, verwenden Sie neben dem Redis einen Schlüsselwertgeschäft wie Redis.
Führen Sie bin/setup nach dem Auschecken des Repo aus, um Abhängigkeiten zu installieren. Führen Sie dann rake build aus, um native Erweiterungen zu erstellen, und rake spec , um die Tests durchzuführen. Sie können auch bin/console für eine interaktive Eingabeaufforderung ausführen, mit der Sie experimentieren können.
Wir verwenden konventionelle Commits, um die ChangeLog automatisch zu generieren, die semantische Version zu stoßen und das Gem zu veröffentlichen und zu veröffentlichen. Alles, was Sie tun müssen, ist, sich an die Konvention zu halten, und CI wird sich um alles andere für Sie kümmern.
Fehlerberichte und Pull -Anfragen sind auf Github unter https://github.com/baygeeldin/Tantiny willkommen.
Das Edelstein ist unter den Bedingungen der MIT -Lizenz als Open Source erhältlich.