Verwendung von Solr NGrams zur Implementierung von Auto-Suggest in der Suche
Erfahren Sie, wie Sie eine Auto-Suggest-Funktion für die Suche mit EdgeNGrams als Teil der Analysekette implementieren.
Eine beliebte Funktion der meisten modernen Suchanwendungen ist die automatische Vorschlags- oder Vervollständigungsfunktion, bei der dem Benutzer, während er seine Suchanfrage in ein Textfeld eingibt, Vorschläge für beliebte Suchanfragen angezeigt werden. Mit jedem weiteren Zeichen, das der Benutzer eintippt, wird die Liste der Vorschläge verfeinert. In Solr gibt es verschiedene Ansätze, um diese Funktion bereitzustellen. Wir werden uns jedoch einen Ansatz ansehen, bei dem EdgeNGrams als Teil der Analysekette verwendet wird. Zwei weitere Ansätze sind die Verwendung der TermsComponent (neu in Solr 1.4) oder die Facettierung.
N-Gramme und Kanten-N-Gramme
Ein N-Gramm ist eine n-Zeichen-Teilzeichenkette einer längeren Folge von Zeichen. Der Begriff „Bargeld“ setzt sich zum Beispiel aus den folgenden n-Grammen zusammen:
- Unigramme: „c“, „a“, „s“, „h“
- Bigramme: „ca“, „as“, „sh“
- Trigramme: „cas“, „ash“
- 4-Gramm: „Bargeld“
N-Gramme können nützlich sein, wenn Teilstrings von Begriffen durchsucht werden müssen. Ein Edge n-gram ist ein n-gram, das aus einer Seite oder einem Rand eines Begriffs gebildet wird. Edge n-grams für den Begriff „cash“ wären:
- „c“, „ca“, „cas“, „bar“
Es ist leicht zu erkennen, wie Kanten-N-Gramme verwendet werden könnten, um Suchanfragen vorzuschlagen, während ein Benutzer eine Suchanfrage Zeichen für Zeichen eingibt.
Ein Überblick über den Prozess
Um Suchanfragen vorschlagen zu können, benötigen wir typische Suchanfragen, die von Benutzern eingegeben werden, in einem Solr-Index. Es ist eine gute Praxis, die von den Benutzern einer Suchanwendung eingegebenen Abfragen zu erfassen und zu analysieren. Idealerweise haben Sie einen geplanten Prozess, um Ihre Solr-Ausgabeprotokolle zu analysieren und die von den Benutzern der Anwendung eingegebenen Abfragen zu erfassen. Die Abfragen könnten in einer relationalen Datenbank gespeichert werden, wo sie unabhängig von Ihren laufenden Solr-Instanzen analysiert werden können. Ein weiterer Vorteil der Speicherung von Abfragen (und der Anzahl der Eingaben) besteht darin, dass Sie den DataImportHandler von Solr verwenden können, um einen Index mit Abfrageinformationen zu erstellen, der als Grundlage für eine automatische Vorschlagsfunktion dienen kann. Sie könnten einen Auto-Auggest-Index als separaten Core entwerfen, der in einer einzigen Solr-Instanz zusammen mit einem Core für Ihren Hauptindex gehostet wird. Beachten Sie, dass es sich wahrscheinlich nicht lohnt, Abfragen zu indizieren, die keine Ergebnisse liefern, da wir einem Benutzer diese nicht vorschlagen möchten. Daher werden wir ein boolesches Feld einfügen, das uns mitteilt, welche Abfragen Ergebnisse enthalten. Ein minimales Tabellendesign für unsere Bedürfnisse könnte wie folgt aussehen:
create table autosuggest ( query varchar(250), hasResults boolean, count int);
Weitere Metadaten könnten sicherlich nach Bedarf für Berichte und Analysen hinzugefügt werden – dies sind nur die Spalten, die wir benötigen, um zu demonstrieren, wie die Auto-Suggest-Funktion aufgebaut ist. Wenn die Abfragen aus den Protokolldateien geparst werden, muss der Prozess den Solr-Hauptindex abfragen, um festzustellen, ob die Abfrage ein oder mehrere Ergebnisse hat, damit die Spalte hasResults gefüllt werden kann.
Normalerweise wird ein AJAX-Frontend verwendet, um den „Auto-Suggest“-Index abzufragen. In diesem Artikel werden wir uns weder mit der Erstellung der AJAX-Komponente noch mit der Vorverarbeitung der Datenbanktabelle befassen. Stattdessen konzentrieren wir uns auf die Konfiguration von Solr für die Indizierung der Abfragen und die anschließende Suche im Index, wobei die Antworten im JSON-Format mit dem JSON-Response-Writer von Solr geschrieben werden.
Im Allgemeinen sind die Schritte, die wir unternehmen werden, folgende:
- Analysieren Sie die Protokolldateien, um eine Liste der von den Benutzern eingegebenen Abfragen zu erhalten, und laden Sie diese Abfragen (sowie alle anderen nützlichen Metadaten) in eine Datenbanktabelle. Dies sollte ein kontinuierlicher, geplanter Prozess sein.
- Konfigurieren Sie schema.xml.
- Konfigurieren Sie eine dih-config.xml-Datei.
- Führen Sie einen vollständigen Import mit dem DataImportHandler durch.
- Erstellen Sie Abfragen, die von einem AJAX-Client verwendet werden können.
schema.xml konfigurieren
Wir müssen einen fieldType definieren, der die Abfrage nicht tokenisiert und nur eine minimale Analyse durchführt. Wir können die KeywordTokenizerFactory verwenden, die unsere Abfragen als einen einzigen Begriff belässt. Wir werden die LowerCaseFilterFactory einbeziehen, um die Eingabe zu vereinfachen, und dann, was am wichtigsten ist, werden wir die EdgeNgramFilterFactory einbeziehen. Die EdgeNgramFilterFactory wird unsere Abfragequellen in eine Reihe von EdgeNgrams zerlegen. Wir müssen eine minGramSize und eine maxGramSize angeben. Für dieses Beispiel setzen wir das Minimum auf „1“ und das Maximum auf „25“. Beachten Sie, dass die Speicherung von n-Grammen in einem Index etwas zusätzlichen Speicherplatz erfordert. Da unser „Auto-Suggest“-Index jedoch nur zwei Felder enthält (und nur eines davon gespeichert wird) und wir wahrscheinlich mit einer nicht allzu hohen Anzahl von Dokumenten rechnen können, sollte dies kein Problem darstellen.
Hier ist unsere Definition für einen fieldType, um das zu erreichen, was wir brauchen:
<fieldType name="edgytext" class="solr.TextField" positionIncrementGap="100"> <analyzer type="index"> <tokenizer class="solr.KeywordTokenizerFactory"/> <filter class="solr.LowerCaseFilterFactory"/> <filter class="solr.EdgeNGramFilterFactory" minGramSize="1" maxGramSize="25" /> </analyzer> <analyzer type="query"> <tokenizer class="solr.KeywordTokenizerFactory"/> <filter class="solr.LowerCaseFilterFactory"/> </analyzer> </fieldType>
Es sollten nur zwei Felder definiert werden: „user_query“, um die Abfrage und ihre n-Gramme zu speichern, und „count“, das angibt, wie oft eine Abfrage in den Protokollen gefunden wurde, und nach dem wir sortieren können, um die beliebtesten Abfragen weiter oben in der Vorschlagsliste anzuzeigen:
<field name="user_query" type="edgytext" indexed="true" stored="true" omitNorms="true" omitTermFreqAndPositions="true" /> <field name="count" type="int" indexed="true" stored="false" omitNorms="true" omitTermFreqAndPositions="true" />
dih-config.xml konfigurieren
Der DataImportHandler (DIH) von Solr ist ein extrem schnelles und effizientes Tool für die Indizierung von Daten in einer relationalen Datenbank. Es gibt zwei Stellen, an denen die Konfiguration vorgenommen werden muss, um den DIH zu aktivieren. Zunächst müssen wir einen Request Handler in solrconfig.xml einrichten:
<requestHandler name="/indexer/autosuggest" class="org.apache.solr.handler.dataimport.DataImportHandler"> <lst name="defaults"> <str name="config">dih-config.xml</str> </lst> <lst name="invariants"> <str name="optimize">false</str> </lst> </requestHandler>
Dann müssen wir die in der Konfiguration des Request Handlers angegebene dih-config.xml erstellen. Erstellen Sie eine Datei namens „dih-config.xml“ im selben Verzeichnis wie solrconfig.xml und schema.xml mit folgendem Inhalt:
<?xml version="1.0"?> <dataConfig> <dataSource type="JdbcDataSource" readOnly="true" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/myDatabase" user="user" password="password"/> <document name="autoSuggester"> <entity name="main" query="select query, count from autosuggest where hasResults = true"> <field column="query" name="user_query"/> <field column="count" name="count"/> </entity> </document> </dataConfig>
Die dataConfig ist ziemlich einfach. Wir richten eine Verbindung zu unserer Datenquelle ein und führen dann eine einfache Select-Abfrage aus, um alle Datensätze zu erhalten, die Ergebnisse haben. Es wäre auch möglich, eine Delta-Import-Abfrage einzurichten, aber je nachdem, wie viele vom Benutzer eingegebene Abfragen Sie indizieren, ist das vielleicht nicht nötig. Der DIH ist sehr schnell und konnte bei meinen Tests etwa 75.000 Dokumente pro Minute indizieren (auf einem MacBook Pro mit einer Heap-Größe von 2 GB), so dass es möglicherweise einfacher ist, einen vollständigen Index einmal oder mehrmals am Tag auszuführen. Wenn Sie Delta-Abfragen einrichten möchten, müssten Sie der Tabelle ein Datumsfeld hinzufügen, das in die Delta-Abfragen einbezogen werden kann. (Weitere Einzelheiten zur DIH-Konfiguration finden Sie im Wiki: http://wiki.apache.org/solr/DataImportHandler)
Indizierung der Daten
Jetzt sind wir bereit, die Daten in der Autosuggest-Tabelle zu indizieren. Dies kann auf zwei verschiedene Arten geschehen:
- Verwenden Sie ein Dienstprogramm wie „curl“ oder „wget“: curl ‚http://localhost:8983/solr/indexer/autosuggest?command=full-import (Hinweis: Beim Entwickeln und Debuggen können Sie den Parameter „&rows=10“ oder eine beliebige Zahl hinzufügen, um den Import zu begrenzen).
- Auf der Seite dataimport.jsp: http://localhost:8983/solr/admin/dataimport.jsp. Sie sollten einen Link zu dem in solrconfig.xml definierten Handler sehen (den wir /indexer/autosuggest genannt haben). Klicken Sie auf diesen Link. Die Datei dih-config.xml wird zusammen mit verschiedenen Schaltflächen für unterschiedliche Operationen angezeigt. Wenn Sie auf „Vollständig importieren“ klicken, wird der gesamte Importvorgang ausgelöst.
Jetzt sollten wir Dokumente in unserem Auto-Suggest-Index haben. Wir können die Analyseseite der Verwaltungskonsole (http://localhost:8983/solr/admin/analysis.jsp) verwenden, um einen Blick darauf zu werfen, wie die n-Gramme für eine Abfrage erstellt werden. Wählen Sie auf der Analyseseite aus dem Dropdown-Feld „Feld“ die Option „Typ“ und geben Sie den Wert „edgytext“ ein, den wir in der schema.xml definiert haben. Aktivieren Sie im Abschnitt „Feldwert“ die Option „ausführliche Ausgabe“ und geben Sie im Textfeld „i’m not down“ ein. Klicken Sie auf „Analysieren“ und beobachten Sie, wie die EdgeNGramFilterFactory unsere Abfrage in n-Gramme aufbricht.
Einige Abfragen ausführen
Jetzt können wir einige Tests durchführen, um zu sehen, ob wir die erwarteten Ergebnisse erhalten. Mein Testindex war für eine Musik-Website, auf der die Benutzer häufig Songtitel als Suchbegriffe eingeben. Nehmen wir an, ich suche nach dem Lied „I’m Not Down“. Das erste eingegebene Zeichen sollte die folgende Abfrage auslösen, die vom AJAX-Frontend gesendet wird: http://localhost:8983/solr/select/?q=user_query: „i“&wt=json&fl=user_query&indent=on&echoParams=none&rows=10&sort=count desc
Beachten Sie, dass wir den JSON-Antwortschreiber (&wt=json) verwenden, nur nach dem Feld fragen, an dem wir interessiert sind (&fl=user_query), echoParams ausschalten (&echoParams=none), um die Antwort so klein wie möglich zu halten, und nach dem Feld count absteigend sortieren (&sort=count desc). (&indent=on ist für eine saubere Anzeige eingestellt, aber für eine Produktionsseite sollte dies weggelassen werden). Da wir die Zählwerte für jede Abfrage in die Datenbank eingegeben haben, können wir danach sortieren, um die beliebtesten Suchanfragen an die Spitze unserer Ergebnisse zu setzen. Die Antwort für diese Abfrage, bei der nur das erste Zeichen eingegeben wurde, sieht so aus:
{ "responseHeader":{ "status":0, "QTime":1}, "response":{"numFound":12,"start":0,"docs":[ { "user_query":"i'm only sleeping"}, { "user_query":"i'm glad"}, { "user_query":"i'm not down"}, { "user_query":"i'm a believer"}, { "user_query":"i'm not your stepping stone"}, { "user_query":"i'm not in love"}, { "user_query":"i'm shakin'"}, { "user_query":"i've been everywhere"}, { "user_query":"i'm hurtin'"}, { "user_query":"i'm in the mood for love"}] }}
Lassen Sie uns zum fünften eingegebenen Zeichen springen: „i’m n“ – jetzt sind die Ergebnisse noch eingeschränkter:
{ "responseHeader":{ "status":0, "QTime":4}, "response":{"numFound":3,"start":0,"docs":[ { "user_query":"i'm not down"}, { "user_query":"i'm not your stepping stone"}, { "user_query":"i'm not in love"}] }}
Beachten Sie, dass die Abfrage als Phrase in Anführungszeichen eingeschlossen werden muss. Andernfalls kann es zu unvorhersehbaren und unerwünschten Übereinstimmungen kommen.
Fazit
Wir haben eine Möglichkeit zur Implementierung einer automatischen Vorschlagsfunktion gezeigt. Aber unabhängig davon, welchen Ansatz Sie wählen, ist es wichtig zu wissen, welche Arten von Abfragen Ihre Benutzer eingeben und wie Sie diese Daten so indizieren können, dass sie die Benutzerfreundlichkeit Ihrer Suchanwendung verbessern. Das Parsen von Protokolldateien zur Erfassung von Abfragen und deren Speicherung in einer Datenbank erleichtert nicht nur die Indizierung Ihrer Abfragedaten für automatische Vorschläge, sondern die Daten können auch für Berichte und Analysen sowie für die Erstellung von Abfragelisten für Lasttests nützlich sein.