Analyzer in Apache Solr
In diesem Artikel betrachten wir verschiedene Analyzer von Apache Solr genauer und erklären die Unterschiede, Einsatzmöglichkeiten und wie man eigene Analyzer entwickeln und verwenden kann.
Was ist ein Analyzer in Apache Solr?
Analyzer werden verwendet, um textuelle Daten in Solr zu analysieren und sie für die Suche vorzubereiten.
Typischerweise möchte man einen Text in einzelne Bestandteile – die Tokens – zerlegen, bestimmte Teile, wie z.B. Satzzeichen, entfernen und verschiedene Schreibweisen desselben Wortes vereinheitlichen. Dadurch erhöht man die Flexibilität beim Suchen und verbessert die Ergebnisse.
Analyzer werden sowohl während der Indexierung, als auch bei der Suche verwendet:
Bei der Indexierung normalisieren sie den Text, so dass in einem vereinheitlichten Datenbestand gesucht werden kann.
Bei der Suche wird der Analyzer auf die Suchbegriffe angewendet, um diese auf die gleiche Art und Weise zu normalisieren. So lassen sich auch dann Treffer finden, wenn die Suchbegriffe nur eine Abwandlung der indexierten Inhalte sind.
Hier kommt eine Besonderheit von Solr zum Vorschein:
Durchsucht wird nicht der ursprüngliche Text, sondern immer nur das Ergebnis des Analyzers. Dieses Ergebnis wird allerdings unabhängig vom ursprünglich Text gespeichert, so dass dieser unverändert erhalten bleibt. Beim Auslesen eines Dokuments liefert Solr immer den ursprünglichen Text.
Die Konfiguration erfolgt pro Feldtyp im Schema von Solr:
<fieldtype name="german_text" class="solr.TextField"> <analyzer class="org.apache.lucene.analysis.de.GermanAnalyzer" /> </fieldtype>
[call-to-action-consulting /]
Existierende Analyzer
Für die gängisten Anwendungsfälle gibt es bereits vorgefertigte Analyzer, die die gesamte Arbeit übernehmen und brauchbare Ergebnisse liefern:
- WhitespaceAnalyzer
- KeywordAnalyzer
- GermanAnalyzer
- EnglishAnalyzer
WhitespaceAnalyzer
Der WhitespaceAnalyzer zerlegt den Text in einzelne Bestandteile, indem er ihn an den Leerzeichen zerschneidet. Die Bestandteile selbst bleiben dabei unverändert.
Für vollständige Sätze ist dieser Analyzer eher ungeeignet, da Satzzeichen nicht entfernt werden.
Es kann allerdings gut für Namen von Personen, Gegenständen und Orten oder ähnliche Wortgruppen eingesetzt werden.
KeywordAnalyzer
Der KeywordAnalyzer erhält den gesamten Text als einen zusammenhängenden Teil, ohne ihn zu verändern.
Er eignet sich für Werte, die nur als Ganzes sinnvoll sind, wie z.B. Artikelnummern, Postleitzahlen und ähnliche Schlüsselwörter.
GermanAnalyzer
Der GermanAnalyzer verwendet die Unicode Text Segmentation, um einen Text in einzelne Wörter zu zerlegen. Diese Wörter werden anschließend auf ihre Grundform zurückgeführt und vollständig auf Kleinschreibung umgestellt.
Häufige Füllworte, wie z.B. Artikel und Präpositionen werden ebenfalls entfernt.
Ein Text aus deutschen Sätzen lässt sich damit gut für eine Suche aufbereiten, so dass später nach einzelnen Worten und deren Abwandlungen gesucht werden kann.
EnglishAnalyzer
Der EnglishAnalyzer funktioniert wie der GermanAnalyzer, ist allerdings auf Englisch ausgelegt.
Er verwendet andere Algorithmen, da die Rückführung auf die Grundform im Englischen anderen Regeln folgt als im Deutschen und auch andere Füllworte entfernt werden müssen.
Ich benötige einen Analyzer, den es gar nicht gibt!
Neben den 4 oben genannten Analyzern gibt es noch einige mehr, es kann allerdings trotzdem passieren, dass keiner davon für den konkreten Anwendungsfall geeignet ist. In diesem Fall kann ein Analyzer durch eine TokenizerChain ersetzt werden. Bei einer TokenizerChain kombiniert man einzelne Komponenten so, dass sie wie ein Analyzer funktionieren, d.h. einen Text in Tokens zerlegen.
Dabei stehen 3 verschiedene Komponentenarten zur Verfügung:
- CharFilter
- Tokenizer
- TokenFilter
Jede Komponente arbeitet dabei mit den Änderungen, die ihre Vorgänger bereits vorgenommen haben, weiter. Auf diese Weise lassen sich auch komplexere Analysen schrittweise zusammensetzen.
Die einzelnen Komponente werden jeweils über eine zugehörige Factory erzeugt, die für die korrekte Instanziierung und Konfiguration der Komponente zuständig ist.
CharFilter
Der CharFilter arbeitet direkt auf den einzelnen Buchstaben des Textes und kann diese ersetzen, entfernen oder zusätzliche Buchstaben einfügen. Der wichtigste Vertreter ist der PatternReplaceCharFilter, der Texte über reguläre Ausdrücke verändern kann.
Es können beliebig viele CharFilter verwendet werden, allerdings müssen diese immer vor dem Tokenizer stehen.
Tokenizer
Der Tokenizer zerlegt den Text in einzelne Tokens.
Hier gibt es eine Vielzahl an Implementierungen, wie z.B.
- WhitespaceTokenizer – Trennt Texte an den Leerzeichen
- KeywordTokenizer – Erstellt ein einziges Token aus dem gesamten Text
- NGramTokenizer – Erstellt Tokens aus den n-Grammen des Textes
- StandardTokenizer – Erstellt Token nach Unicode Text Segmentation
Ein Tokenizer ist zwingend erforderlich und es kann auch nur ein Tokenizer eingesetzt werden.
Wurde zuvor ein CharFilter konfiguriert, arbeitet der Tokenizer auf den Änderungen des CharFilters. So kann man z.B. mittels PatternReplaceCharFilter alle Satzzeichen entfernen und dann einen WhitespaceTokenizer verwenden, um Tokens aus den verbleibenden Worten zu erstellen:
<analyzer> <charFilter class="solr.PatternReplaceCharFilterFactory" pattern="\p{Punct}" replacement="" /> <tokenizer class="solr.WhitespaceTokenizerFactory" /> </analyzer>
TokenFilter
Ein TokenFilter wird verwendet, um die Tokens, die von einem Tokenizer erstellt wurden, noch nachträglich zu verändern. TokenFilter können also immer nur nach einem Tokenizer eingesetzt werden.
Genau wie beim CharFilter können Tokens geändert, entfernt und hinzugefügt werden. Es können ebenfalls beliebig viele TokenFilter konfiguriert werden, wobei jeder auf den Änderungen seiner Vorgänger aufbaut. Die Reihenfolge ist also entscheidend!
Wichtige TokenFilter sind z.B.
- ASCIIFoldingFilter – Zeichen, die nicht aus dem einfachen ASCII-Satz stammen, werden auf diesen abgebildet
- StopFilter – Entfernt Tokens, wenn sie in einer Stopwordliste vorkommen
- LowerCaseFilter – Ändert alle Tokens in Kleinschreibung
- WordDelimiterFilter – Zerlegt Tokens in mehrere Tokens, wenn diese Worttrenner enthalten
- GermanStemFilter – Ändert Tokens auf ihre Grundform. Speziell für deutsche Texte.
Ich benötige immer noch etwas anderes!
Sollten auch die Kombinationsmöglichkeiten aus den bestehenden Filtern und Tokenizern nicht ausreichen, können solche Komponenten auch selbst implementiert werden. Zu diesem Zweck muss immer die eigentliche Komponente und die dazugehörige Factory erstellt werden.
CharFilter
Für einen CharFilter leitet man von der Klasse org.apache.lucene.analysis.CharFilter – oder besser noch von org.apache.lucene.analysis.charfilter.BaseCharFilter – ab und überschreibt die read Methoden, um die Buchstabenfolge zu manipulieren.
Die zugehörige Factory erbt von der Klasse org.apache.lucene.analysis.util.CharFilterFactory.
Wichtig ist, bei Einfügungen oder Löschungen die entsprechenden Verschiebungen in der Buchstabenfolge zu propagieren. Andernfalls kann es zu Problemen beim Highlighting kommen.
Tokenizer
Für einen Tokenizer leitet man von der Klasse org.apache.lucene.analysis.Tokenizer ab und überschreibt die Methode incrementToken.
Die zugehörige Factory erbt von der Klasse org.apache.lucene.analysis.util.TokenizerFactory.
TokenFilter
Für einen TokenFilter leitet man von der Klasse org.apache.lucene.analysis.TokenFilter ab und überschreibt die Methode incrementToken.
Die zugehörige Factory erbt von der Klasse org.apache.lucene.analysis.util.TokenFilterFactory.
Die so erstellten Klassen werden in eine JAR-Datei verpackt und Solr als zusätzliche Bibliothek, z.B. über das sharedLib im solr.xml, bereitgestellt.
Nach einem Neustart von Solr kann man die eigenen Klassen verwenden, indem man sie mit dem vollqualifizierten Namen im Schema referenziert:
<fieldtype name="colognePhoneticText" class="solr.TextField" positionIncrementGap="100"> <analyzer> <tokenizer class="solr.WhitespaceTokenizerFactory" /> <filter class="solr.LowerCaseFilterFactory" /> <filter class="com.indoqa.solr.extensions.filter.ColognePhoneticTokenFilterFactory" inject="true" /> </analyzer> </fieldtype>