Erste Schritte bei der Einrichtung von Lucene
Apache Lucene ist eine schnelle, voll funktionsfähige Bibliothek für die Volltextsuche, die in zahlreichen Produktionsumgebungen eingesetzt wird. In diesem Artikel…
Apache Lucene ist eine schnelle, voll funktionsfähige Bibliothek für die Volltextsuche, die in zahlreichen Produktionsumgebungen eingesetzt wird. In diesem Artikel erläutert Grant Ingersoll, Lucene-Committer und Schöpfer des Lucene Boot Camp Trainingsprogramms, die grundlegenden Konzepte von Lucene und zeigt Ihnen, wie Sie die Lucene-API nutzen können, um umfassende Suchfunktionen für Ihre nächste Anwendung zu entwickeln.
Hallo, Lucene
Apache Lucene ist eine Java-basierte, leistungsstarke Bibliothek, die es Entwicklern ermöglicht, auf einfache Weise Suchfunktionen zu Anwendungen hinzuzufügen. Lucene wurde in vielen verschiedenen Anwendungen eingesetzt, von der groß angelegten Internetsuche mit Hunderten von Millionen von Dokumenten über eCommerce-Shops, die große Mengen von Benutzern bedienen, bis hin zu eingebetteten Geräten.
Eine kurze Geschichte von Lucene
Lucene wurde ursprünglich von Doug Cutting im Jahr 1997 entwickelt und im Jahr 2000 auf SourceForge verfügbar gemacht. Nach dem Übergang zur Apache Software Foundation im Jahr 2001 hat Lucene einen stetigen Anstieg an Beiträgen, Committern, Funktionen und der Übernahme in Anwendungen erlebt. Lucene wird in zahlreichen Produktions- und Forschungssystemen eingesetzt, wie sowohl die Anzahl der Downloads als auch die Anzahl der Benutzer, die sich selbst identifiziert haben, belegen.
Suche 101
Bevor Sie mit der Verwendung von Lucene beginnen, ist es wichtig, einige Grundlagen darüber zu verstehen, wie das Suchsystem funktioniert und wie man Inhalte auffindbar macht. Sie können zwar auch ohne dieses Wissen eine Suchanwendung mit Lucene erstellen, aber die folgende kurze Einführung in die Suchkonzepte wird es Ihnen ermöglichen, Lucene besser zu nutzen, was letztendlich zu einer besseren Suchanwendung führen wird.
Im Grunde genommen ist eine Suchanwendung für vier Dinge zuständig:
- Indizierung von Inhalten – Der Prozess des Hinzufügens von Inhalten in das System, so dass sie durchsucht werden können.
- Erfassung und Darstellung der Benutzereingaben – Während Google und Yahoo! hauptsächlich einen einfachen Textbereich für die Eingabe von Schlüsselwörtern und Phrasen bereitstellen, können Suchanwendungen „über den Tellerrand“ hinausschauen und andere Eingabefunktionen anbieten, die es dem Benutzer ermöglichen, umfangreichere Suchanfragen zu erstellen. So können beispielsweise Datumsbereiche, Sammlungsfilter und andere Widgets auf der Benutzeroberfläche verwendet werden, um die Suche enger zu fokussieren und damit sowohl die Geschwindigkeit als auch die Qualität der Ergebnisse zu verbessern.
- Suche – Der Prozess der Identifizierung und Einordnung von Dokumenten in Bezug auf die Suchanfrage des Benutzers.
- Ergebnisanzeige – Nachdem die Dokumente eingestuft wurden, muss die Anwendung entscheiden, welche Merkmale des Dokuments angezeigt werden sollen. Einige Anwendungen zeigen kurze Titel und Zusammenfassungen in einer Rangliste an, während andere mehr Informationen in reichhaltigen und ausdrucksstarken Oberflächen bereitstellen. Clevere Benutzeroberflächen mit viel Schnickschnack mögen zwar Spaß machen, aber stellen Sie sicher, dass Ihre Benutzer diese Funktionen auch wirklich brauchen. Letztendlich spricht viel für die Einfachheit.
Bevor die Indizierung erfolgen kann, muss der Inhalt verstanden werden, worauf ich im nächsten Abschnitt eingehe. Danach gehe ich darauf ein, wie die Benutzer in die Suche einbezogen werden, und schließe mit einer Diskussion über den Suchprozess.
Ihr Inhalt
Es wird oft gesagt, dass Inhalt König ist. Doch um diese Inhalte nutzen zu können, müssen Sie sie verstehen. Natürlich haben Sie wahrscheinlich Tausende, wenn nicht sogar Millionen von Dokumenten, so dass es unmöglich ist, sie alle zu verstehen. Wäre dies nicht der Fall, bräuchten Sie keine Suche, oder? Das Problem, Ihre Inhalte zu verstehen, besteht also darin, so viele Merkmale der Inhalte wie möglich zu verstehen. Unabhängig von der Anzahl der Dokumente gibt es viele Tipps und Techniken, die dabei helfen können. Ich werde hier nur die wichtigsten Punkte ansprechen, werde aber in meinen Artikeln über Auffindbarkeit und Verbesserung der Relevanz noch mehr ins Detail gehen.
Um Ihre Inhalte zu verstehen, sollten Sie sich zunächst Gedanken über das Format der Inhalte machen. Wenn es sich um eine Datei handelt, was ist dann ihr Mime-Typ? Ist die Datei Text, PDF, Word, HTML oder ein anderes Format? Wenn es sich um etwas anderes als reinen Text handelt, muss die Anwendung den Text aus der Originaldatei extrahieren, um ihn durchsuchbar zu machen. Dies liegt zwar außerhalb des Bereichs der Lucene-Kernfunktionen, aber das verwandte Apache Tika-Projekt bietet Extraktionsfunktionen für viele gängige Mime-Typen. In dem Artikel des Committers Sami Siren finden Sie Informationen zur Arbeit mit Tika. Wenn sich der Inhalt in einer Datenbank oder an einem anderen Ort befindet, bestimmen Sie den bestmöglichen Weg, um an ihn heranzukommen.
Sobald Sie die Möglichkeit haben, Text zu extrahieren, ist es wichtig, die Struktur und die Metadaten der Dateien zu verstehen. Zu diesem Zweck empfehle ich Ihnen, eine Reihe von Dokumenten aus dem Satz zu prüfen und nach Merkmalen wie diesen zu suchen:
- Struktur des Dokuments: Enthält das Dokument Dinge wie: einen Titel, einen Hauptteil, Absätze, Autoren, Preis, Inventar, Gewinnspanne, Tabellen oder Listen? Es kann sein, dass Sie etwas zusätzliche Arbeit leisten müssen, um diese Dinge zu nutzen, aber wenn es richtig gemacht wird, kann die Benutzererfahrung viel besser sein.
- Besondere Begriffe: Gibt es besondere Wörter in den Dokumenten, die hervorgehoben werden sollten? Wörter, die fett, kursiv oder Teil von Links sind, sind einige Beispiele für besondere Begriffe. Auch Phrasen und Eigennamen verbessern oft die Suchergebnisse.
- Synonyme und Jargon: Ähnlich wie bei den Fachbegriffen kann ein intelligenter Umgang mit Synonymen, Fachausdrücken, Akronymen und Abkürzungen zu besseren Ergebnissen führen. Vielleicht haben Sie bereits eine Synonymliste oder Abkürzungserweiterungen, die Sie nutzen können
- Prioritäten/Bedeutung: Wissen Sie a priori etwas über die Priorität oder Wichtigkeit eines Dokuments? Vielleicht handelt es sich bei dem Dokument um ein unternehmensweites Memo Ihres CEO oder es wird von Ihren Lesern hoch bewertet oder es hat einen sehr hohen Rang nach Maßstäben wie PageRank oder einem anderen Linkanalyse-Algorithmus. Wenn Sie diese Dinge wissen, können Sie die Relevanz wirklich verbessern.
Natürlich können Ihre Inhalte auch andere Funktionen haben, die Ihnen helfen, Ihre Anwendung zu verbessern. Nehmen Sie sich im Vorfeld die Zeit, sie zu verstehen, und überprüfen Sie sie von Zeit zu Zeit, wenn neue Inhalte in das System aufgenommen werden. Sobald die Funktionen identifiziert sind, können Sie diese Funktionen iterativ extrahieren und in Ihrer Anwendung verwenden. Wie Sie vielleicht bemerkt haben, habe ich in diesem letzten Satz das Wort iterieren verwendet. Oft ist es am besten, einen kleinen Index zu erstellen, einige Suchvorgänge durchzuführen und dann die Indizierungs- und Suchprozesse zu ergänzen oder anzupassen, wenn neue Merkmale entdeckt werden. Was nützt natürlich ein Inhalt ohne Benutzer? Ich werde im nächsten Abschnitt darüber sprechen, wie Sie die Bedürfnisse Ihrer Benutzer erfüllen können.
Ihre Benutzer
Ah, Benutzer. Sie sind Ihr Lebenselixier. Gleichzeitig ist es oft schwierig, ihre Suchbedürfnisse vollständig zu verstehen. Ein Nutzer weiß zum Beispiel oft genau, was er braucht, aber ein einfaches Suchfeld, das auf die Eingabe von Schlüsselwörtern ausgerichtet ist, kann diesem Bedarf nicht gerecht werden. Auf der anderen Seite kennen Benutzer manchmal nur einen oder zwei vage Begriffe und erwarten dennoch hochrelevante Ergebnisse. Ihre Aufgabe ist es natürlich, so viele Erwartungen Ihrer Nutzer wie möglich zu verstehen und zu erfüllen, und das alles innerhalb des Zeit- und Budgetrahmens.
Der Schlüssel zur erfolgreichen Bewältigung Ihrer Aufgabe liegt darin, zu verstehen, mit welcher Art von Benutzern Sie es überhaupt zu tun haben. Die Gestaltung von Eingabemasken für hochqualifizierte Geheimdienstanalysten ist etwas ganz anderes als die Gestaltung für „Otto Normalverbraucher“ im Internet. Geheimdienstanalysten sind oft mit ausgefeilten Schnittstellen vertraut, von denen sie wissen, dass sie bessere Ergebnisse liefern, während der Durchschnittsnutzer oft so sehr an Google und Yahoo! gewöhnt ist, dass alles, was über ein einfaches Suchfeld mit Unterstützung für Schlüsselwörter und Phrasen hinausgeht, eine Verschwendung von Mühe ist.
Um ein besseres Verständnis Ihrer Benutzer zu erhalten, sind Fokusgruppen und Umfragen hilfreich (d.h. die Befragung Ihrer Benutzer), ebenso wie die Analyse der Abfrageprotokolle. Die Analyse von Abfrageprotokollen kann Ihnen konkrete Daten darüber liefern, wonach die Benutzer gesucht haben, aber sie leidet unter dem klassischen Henne-Ei-Problem, da Sie erst ein echtes System einrichten müssen, bevor Sie die Ergebnisse sehen können, die Sie für die Gestaltung des Systems benötigen. Im Idealfall können Sie Ihre Anwendung iterativ einsetzen und weiterentwickeln, um mehr Feedback zu erhalten. Weitere Überlegungen zur Analyse von Abfrageprotokollen finden Sie in meinem Artikel über die Verbesserung der Relevanz.
Wie die Suche in Lucene funktioniert
Die Suche bzw. das Information Retrieval (IR) ist weithin bekannt und wird praktiziert. Es gibt zahlreiche Theorien darüber, wie man den Informationsbedarf eines Benutzers am besten mit den relevanten Dokumenten in Einklang bringen kann. Eine eingehende Erörterung dieser Theorien würde den Rahmen dieses Artikels sprengen (siehe einige der unten aufgeführten Referenzen). Es ist jedoch sinnvoll, die Grundlagen der Theorie zu erörtern, die Lucene verwendet, um ein besseres Verständnis dafür zu bekommen, was unter der Haube vor sich geht.
Lucene implementiert in seinem Kern ein modifiziertes Vektorraummodell (VSM) für die Suche. Im VSM werden sowohl Dokumente als auch Abfragen als Vektoren in einem n-dimensionalen Raum dargestellt, wie in der folgenden Abbildung zu sehen ist.
Abbildung 1. 2-dimensionales Beispiel eines Vektorraummodells
Eine einfache 2-dimensionale Zeichnung des Vektorraummodells
Im 2-dimensionalen Beispiel des Vektorraummodells ist dj das j-te Dokument in der Sammlung und ein Tupel von Wörtern (eigentlich sind es Gewichte für die Wörter). Die Eingabeabfrage q wird ebenfalls auf der Grundlage ihrer Wörter in diesen Raum abgebildet und bildet somit einen Winkel zwischen den beiden Vektoren (unter der Annahme entsprechender Transformationen), der in der Abbildung mit dem griechischen Buchstaben Theta gekennzeichnet ist. Wie in der Schule gelernt, kann der Kosinus von Theta zwischen 1 und -1 liegen. Am wichtigsten ist, dass der Kosinus von Null eins ist. Daher sind bei der Suche die relevantesten Dokumente für eine Abfrage diejenigen, die für die Kosinusfunktion den Wert Eins ergeben.
Sie erinnern sich vielleicht daran, dass ich sagte, Lucene verwende eine „modifizierte“ VSM. Bei einer reinen VSM wird die Abfrage mit allen Dokumenten in der Sammlung verglichen. In der Praxis wird das nie gemacht, weil es zu teuer ist. Stattdessen sucht Lucene zunächst alle Dokumente, die die Begriffe in der Abfrage enthalten (basierend auf der booleschen Logik der Abfrage) und führt dann die Relevanzberechnung nur für diese Dokumente durch. Diese Suche kann in Lucene dank der invertierten Indexdatenstruktur sehr schnell durchgeführt werden. Ein invertierter Index ist dem Index auf der Rückseite des Buches sehr ähnlich. Er enthält nämlich eine Zuordnung zwischen einem Begriff und den Dokumenten, die diesen Begriff enthalten, und ermöglicht so ein schnelles Nachschlagen in allen Dokumenten, die einen Begriff enthalten. Lucene speichert auch Positionsinformationen, die es dann zur Auflösung von Phrasenabfragen verwenden kann.
Natürlich gibt es noch viel mehr, aber dafür ist Lucene zuständig, nicht Sie (zumindest anfangs). Wenn Sie jedoch mehr wissen möchten, lesen Sie die Links zu den Dateiformaten von Lucene sowie die Links zur Information Retrieval Theorie. Machen wir weiter und beginnen Sie mit der Verwendung von Lucene.
Lucene Einrichtung
Da Lucene eine Java-Bibliothek und keine Anwendung ist, müssen Sie nur die Voraussetzungen erfüllen, die JAR-Dateien herunterladen und sie zu Ihrem Projekt hinzufügen. Für Lucene gibt es eigentlich nur eine einzige Voraussetzung: Java JDK 1.4 oder höher (obwohl ich empfehle, mindestens JDK 1.5 zu verwenden). Außerdem sollten Sie sicherstellen, dass Sie über eine gute CPU und genügend Arbeitsspeicher verfügen, aber diese Faktoren hängen von Ihrer Anwendung ab und sind schwer explizit zu nennen. Ich habe Anwendungen gesehen, die sehr wenig CPU und Speicher benötigen, und ich habe Anwendungen gesehen, die immer mehr zu wollen scheinen. Lesen Sie den Artikel von Mark Miller über Skalierung, um zu verstehen, wie man den Rechner dimensioniert.
Für dieses Beispiel werde ich die zertifizierte Version von Apache Lucene von Lucid verwenden, die Sie im Download-Bereich der Lucid-Website finden. Wenn Sie den Download zur Hand haben, starten Sie ein neues Projekt in Ihrer IDE und fügen Sie die Lucene-Bibliotheken hinzu.
Erste Schritte mit Lucene
Um Lucene zu verstehen, müssen Sie einige Schlüsselkonzepte begreifen. Diese Konzepte lassen sich in vier Bereiche unterteilen, die in den nächsten vier Abschnitten beschrieben werden. Ich habe auch einige Codeschnipsel eingefügt, die die Konzepte veranschaulichen.
Dokumente und Felder
In Lucene dreht sich fast alles um das Konzept eines Document
und seine Field
s. Ein Lucene Document
ist eine logische Gruppierung eines Inhalts. Document
s repräsentieren typischerweise eine Datei oder einen Datensatz in einer Datenbank, aber das ist keine Voraussetzung. Document
s bestehen aus einem oder mehreren Field
s. Ein Field
enthält den eigentlichen Inhalt zusammen mit Metadaten, die beschreiben, wie der Inhalt von Lucene behandelt werden soll. Field
s sind oft Dinge wie ein Dateiname, der Inhalt einer Datei oder eine bestimmte Zelle in einer Datenbank. Field
s können durch eine String
, Reader
, Byte-Array oder ein TokenStream
(Die mit einem Feld verknüpften Metadaten sagen Lucene, ob der Rohinhalt gespeichert werden soll oder nicht und ob der Inhalt analysiert werden soll oder nicht. Lucene unterstützt die Optionen, die in Lucene Field Metadata Options beschrieben sind.
Tabelle 1. Lucene Feld Metadaten Optionen
Typ | Beschreibung | Werte |
---|---|---|
Lagerung | Lucene kann verwendet werden, um den ursprünglichen Inhalt einer Field zu speichern, ähnlich wie eine Datenbank. Dies ist unabhängig von der Indizierung. |
|
Index | Gibt an, ob der Inhalt durchsuchbar sein soll oder nicht. Legt auch fest, ob der Inhalt analysiert werden soll oder nicht. |
|
Schließlich können sowohl ein Document
als auch die Field
s für ein Document
angehoben werden, um die Wichtigkeit eines Document
gegenüber einem anderen oder eines Field
gegenüber einem anderen anzuzeigen. Lucene verlangt kein striktes Schema für die Field
s eines Document
. Das bedeutet, dass ein Document
zwanzig Field
s haben kann, während ein anderes Document
in derselben Sammlung nur ein Field
haben kann.
Wenn ich zum Beispiel HTML-Seiten durchsuchen würde, hätte ich wahrscheinlich eine Document
pro HTML-Datei, wobei jede Document
die folgenden Field
s hätte:
- Titel – Der Titel der Seite. Ich würde den Titel wahrscheinlich erhöhen, da Übereinstimmungen im Titel oft bessere Ergebnisse anzeigen.
- Body – Der Hauptinhalt der Seite, wie er im HTML-Tag <body> enthalten ist.
- Schlüsselwörter – Die Liste der Schlüsselwörter aus dem HTML-Tag <meta>, sofern vorhanden.
Beispiel 1. Beispiel für die Erstellung eines Lucene-Dokuments
HTMLDocument doc1 = new HTMLDocument("vikings.html", "Minnesota Vikings Make Playoffs!", "The Minnesota Vikings made the playoffs by" + " beating the New York Giants in the final game of the regular season.", "Minnesota Vikings, New York Giants, NFL");//
Document vikesDoc = new Document(); vikesDoc.add(new Field("id", doc1.getId(), Field.Store.YES, Field.Index.NOT_ANALYZED)); //
Field titleField = new Field("title", doc1.getTitle(), Field.Store.YES, Field.Index.ANALYZED);//
titleField.setBoost(5);//
vikesDoc.add(titleField); vikesDoc.add(new Field("body", doc1.getBody(), Field.Store.YES, Field.Index.ANALYZED)); vikesDoc.add(new Field("all", doc1.getTitle() + " " + doc1.getBody(), Field.Store.NO, Field.Index.ANALYZED));//
Erstellen Sie eine einfache Darstellung eines HTML-Dokuments. | |
Speichern und indizieren Sie die ID, aber lassen Sie nur exakte Übereinstimmungen zu | |
Fügen Sie den Titel zum Document |
|
Geben Sie an, dass der Titel Field wichtiger ist als die anderen Field s |
|
Erstellen Sie eine Field , die sowohl den Titel als auch den Text enthält, um beide Field zu durchsuchen. Beachten Sie, dass der Inhalt nicht gespeichert wird. |
In diesem Beispiel erstelle ich zunächst eine Document
und füge dann mehrere Field
hinzu. Sobald ich eine Document
habe, kann ich sie indizieren, wie unter Indizierung beschrieben.
Während der Code zur Erstellung von Document
relativ einfach ist, besteht der schwierige Teil darin, Ihre Inhalte zu kennen, damit Sie entscheiden können, welche Field
hinzugefügt werden sollen und wie sie behandelt werden sollen (indiziert, gespeichert, verstärkt usw.). Versuchen Sie schließlich, ein Gleichgewicht zwischen der Abdeckung aller möglichen Suchszenarien und der Notwendigkeit, Ihr Projekt abzuschließen, zu finden!
Indizierung
Indizierung ist der Prozess des Mappings Document
s in die internen Datenstrukturen von Lucene. Während des Indizierungsprozesses verwendet Lucene die Document
, Field
s und Metadaten, um zu bestimmen, wie der Inhalt zum Index hinzugefügt werden soll. In Lucene ist ein Index eine Sammlung von einem oder mehreren Document
s und wird durch die abstrakte Klasse Directory
dargestellt. Directory
s in Lucene können speicherbasiert (RAMDirectory
) oder dateibasiert (FSDirectory
und andere) sein. Die meisten großen Indizes werden eine dateibasierte Directory
Implementierung erfordern.
Die wichtigste Klasse für die Interaktion mit dem Indizierungsprozess von Lucene ist IndexWriter
. Sie bietet mehrere Konstruktoren für die Instanziierung und zwei Optionen zum Hinzufügen von Document
zum Index. Die IndexWriter
kann einen Directory
erstellen oder an ihn anhängen, aber es kann immer nur ein IndexWriter
geöffnet sein und in einen Directory
schreiben. Ein Beispiel für die Indizierung finden Sie im Beispielcode.
Beispiel 2. Beispiel IndexWriter
Verwendung
RAMDirectory directory = new RAMDirectory();//
1
Analyzer analyzer = new StandardAnalyzer();//
IndexWriter writer = new IndexWriter(directory, analyzer, true, IndexWriter.MaxFieldLength.LIMITED);//
writer.addDocument(vikesDoc); //
writer.addDocument(luceneDoc); //
for (int i = 0; i < 10; i++){//
Document doc = new Document(); doc.add(new Field("id", "page_" + i + ".html", Field.Store.YES, Field.Index.NOT_ANALYZED)); doc.add(new Field("title", "Vikings page number: " + i, Field.Store.YES, Field.Index.ANALYZED)); doc.add(new Field("body", "This document is number: " + i + " about the Vikings.", Field.Store.YES, Field.Index.ANALYZED)); doc.add(new Field("all", "Vikings page number: " + i + " This document is number: " + i + " about the Minnesota Vikings.", Field.Store.NO, Field.Index.ANALYZED)); writer.addDocument(doc); } writer.optimize();//
writer.close();//
Instanziieren Sie einen speicherbasierten Directory . Dieser Index ist flüchtig. |
|
Erstellen Sie ein Analyzer , das von IndexWriter für die Erstellung von Token verwendet wird. |
|
Erstellen Sie die IndexWriter , die für die Indizierung des Inhalts zuständig ist. |
|
Fügen Sie das Vikings-Dokument dem Writer hinzu. | |
Fügen Sie dem Writer ein zweites Dokument hinzu | |
Fügen Sie zur Sicherheit noch einige andere Dokumente hinzu. | |
Optional. Optimieren Sie den Index für die Suche. | |
Schließen Sie den IndexWriter. Der Inhalt ist nicht mehr durchsuchbar, bis close() aufgerufen wird (in diesem Szenario). |
In diesem Beispiel erstelle ich zunächst eine RAMDirectory
, um die internen Lucene-Datenstrukturen zu speichern. Als nächstes erstelle ich eine StandardAnalyzer
für die Analyse des Inhalts. Kümmern Sie sich vorerst nicht darum, was die StandardAnalyzer
tut. Ich werde das im Abschnitt über die Analyse weiter unten behandeln. Danach erstelle ich eine IndexWriter
und übergebe Directory
, Analyzer
und einen booleschen Parameter, der dem Writer mitteilt, dass er den Index erstellen soll (im Gegensatz zum Anhängen) und schließlich einen Parameter, der eine Obergrenze für die Anzahl der Token (in diesem Fall die Vorgabe von 10.000) pro Field
festlegt.
Nachdem ich im Beispiel ein paar Document
hinzugefügt habe, optimiere ich den Index und schließe dann den IndexWriter
. Durch das Schließen des Writers werden alle Dateien in den Directory
geleert und alle verwendeten internen Ressourcen geschlossen. Dies wird später wichtig werden, wenn ich die Suche bespreche. Was den Optimierungsaufruf betrifft, so schreibt Lucene seine Datenstrukturen in kleine Stücke, die Segmente genannt werden. Der optimize-Aufruf fasst diese Segmente zu einem einzigen Segment zusammen. Dies ist eine Optimierung für die Suche, da es schneller ist, ein einzelnes Segment zu öffnen und daraus zu lesen. Unabhängig davon, ob Sie optimieren oder nicht, sollten die Suchergebnisse für eine bestimmte Abfrage genau die gleichen sein.
Das ist wirklich alles, was Sie wissen müssen, um mit der Indizierung zu beginnen. Es gibt natürlich noch mehr Möglichkeiten, den Indizierungsprozess zu optimieren und zu optimieren. Wenn Sie mehr über diese und andere Indizierungsoptionen erfahren möchten, lesen Sie die unten stehenden Ressourcen, insbesondere Lucene in Aktion. Für den Moment möchte ich mich jedoch auf den Suchprozess konzentrieren.
Suchen Sie
Die Suche in Lucene wird von der Funktion Searcher
abstrakte Klasse und ihre verschiedenen Implementierungen. Die Searcher
nimmt eine Query
Instanz auf und gibt eine Reihe von Ergebnissen zurück, die normalerweise durch die TopDocs
Klasse repräsentiert werden, aber es gibt auch andere Optionen. Die Klasse Query
ist eine abstrakte Darstellung der Eingabeabfrage des Benutzers. Es gibt viele abgeleitete Klassen von Query
, die mit Lucene geliefert werden, die am häufigsten verwendeten sind TermQuery
, BooleanQuery
und PhraseQuery
. Sie können Abfragen erstellen, indem Sie die verschiedenen Instanzen miteinander kombinieren, indem Sie die BooleanQuery
und andere ähnliche Gruppierungsabfragen verwenden, auf die ich hier nicht eingehen werde. In den Javadocs finden Sie weitere Informationen zu den verschiedenen Implementierungen von Query
. Das Erstellen von Abfragen wird oft von einer Klasse übernommen, die diesen Prozess automatisiert. In Lucene ist QueryParser
eine JavaCC-basierte Grammatik, mit der Sie Query
aus Benutzereingaben unter Verwendung einer bestimmten Syntax erstellen können. (Siehe Abfrageparser-Syntax.) Solr wird auch mit einigen alternativen Abfrageparsern ausgeliefert und es kann gut sein, dass Sie je nach der Syntax, die Sie unterstützen möchten, Ihre eigenen Parsing-Funktionen implementieren müssen. Genug geredet, denn ein Beispiel wird Ihnen helfen, die Konzepte zu veranschaulichen:
Beispiel 3. Beispiel für die Suche
Searcher searcher = new IndexSearcher(directory);//
QueryParser qp = new QueryParser("all", analyzer);//
Query query = qp.parse("body:Vikings AND Minnesota"); //
TopDocs results = searcher.search(query, 10);//
printResults(query, searcher, results); query = qp.parse("Lucene"); results = searcher.search(query, 10); printResults(query, searcher, results);
Konstruieren Sie eine Searcher für die Suche im Index. |
|
Die Lucene QueryParser kann verwendet werden, um die Eingabeabfrage eines Benutzers in eine Query zu konvertieren. Der Konstruktor nimmt eine Standardsuche Field und die Analyzer , die zur Erstellung von Token verwendet werden soll. |
|
Die parse übernimmt die eigentliche Arbeit des Aufbaus einer Query . |
|
Führen Sie die Suche aus und fordern Sie höchstens 10 Ergebnisse an. |
Als Fortsetzung des Indizierungsbeispiels erstelle ich eine IndexSearcher
unter Verwendung derselben Directory
, die ich bei der Indizierung verwendet habe. Als Nächstes erstelle ich eine QueryParser
Instanz und übergebe denselben Analyzer, den ich für die Indizierung verwendet habe, sowie den Namen des Standard Field
, der durchsucht werden soll, wenn in der Abfrage kein Field
angegeben ist. Sobald ich eine Query
habe, sende ich die Suchanfrage ab und drucke die Ergebnisse aus. Nur so zum Spaß übermittle ich dann eine weitere Query
und drucke die Ergebnisse aus. Wenn ich diesen Code ausführe, erhalte ich die folgende Ausgabe (zur Anzeige abgekürzt):
Abfrage: +body:vikings +all:minnesota
Total Hits: 11
Doc: Dokument<stored/uncompressed,indexed,tokenized<body:Dieses Dokument ist Nummer: 0 über die Vikings.> >
Doc: Dokument<gespeichert/unkomprimiert,indiziert,tokenisiert<body:Dieses Dokument ist Nummer: 1 über die Wikinger.> >
Doc: Dokument<gespeichert/unkomprimiert,indiziert,tokenisiert<body:Dieses Dokument ist Nummer: 2 über die Vikings.> >
Doc: Dokument<gespeichert/unkomprimiert,indiziert,tokenisiert<body:Dieses Dokument ist Nummer: 3 über die Wikinger.> >
Doc: Dokument<gespeichert/unkomprimiert,indiziert,tokenisiert<body:Dieses Dokument ist Nummer: 4 über die Vikings.> >
Doc: Dokument<gespeichert/unkomprimiert,indiziert,tokenisiert<body:Dieses Dokument ist Nummer: 5 über die Vikings.> >
Doc: Dokument<gespeichert/unkomprimiert,indiziert,tokenisiert<body:Dieses Dokument ist Nummer: 6 über die Wikinger.> >
Doc: Dokument<gespeichert/unkomprimiert,indiziert,tokenisiert<body:Dieses Dokument ist Nummer: 7 über die Wikinger.> >
Doc: Dokument<gespeichert/unkomprimiert,indiziert,tokenisiert<body:Dieses Dokument ist Nummer: 8 über die Vikings.> >
Doc: Dokument<gespeichert/unkomprimiert,indiziert,tokenisiert<body:Dieses Dokument ist Nummer: 9 über die Wikinger.> >
Query: all:lucene
Total Hits: 1
Doc: Document<stored/uncompressed,indexed,tokenized<body:Apache Lucene ist eine schnelle, Java-basierte Suchbibliothek.>>Beachten Sie in meinem Beispiel, dass ich für diese zweite Query
keine neue Searcher
erstellt habe. Dies ist in der Tat ein wichtiges Konzept in Lucene. Wenn Sie einen Searcher
erstellen, öffnen Sie eine Momentaufnahme des Indexes zu einem bestimmten Zeitpunkt. (Eigentlich delegiert Searcher
das Öffnen des Indexes an die Klasse IndexReader
.) Wenn Sie beispielsweise einen neuen Index erstellen und an einem bestimmten Tag um 15.00 Uhr 10 Dokumente hinzufügen, unmittelbar danach eine Searcher
öffnen und dann um 15.01 Uhr 10.000 Dokumente hinzufügen, hat diese Searcher
keine Kenntnis von diesen 10.000 neuen Document
s. Der Grund dafür ist, dass das Öffnen eines Indexes darauf zurückzuführen ist, dass Lucene einen zeitlichen Schnappschuss öffnet, was teuer sein kann. Die Suche ist jedoch sehr schnell, sobald der Index geöffnet ist. Daher ist es am besten, den Searcher
zu öffnen und ihn dann in den Cache zu stellen. Je nach Ihren geschäftlichen Anforderungen öffnen Sie Searcher
bei Bedarf erneut. Für viele Anwendungen ist dies völlig ausreichend, da sich der Index nicht öfter als etwa alle fünf Minuten ändert und eine kleine Verzögerung bei der Aktualisierung akzeptabel ist, da die Benutzer dies nicht bemerken werden. Für diejenigen, die Echtzeitanforderungen haben, gibt es Techniken, aber diese Diskussion ist für diesen Artikel nicht geeignet, da sie Entwicklungsarbeit erfordert.
Andere Dinge, die Sie in dem Suchbeispiel beachten sollten, sind:
- Die Gesamtzahl der Treffer unterscheidet sich oft von der Anzahl der zurückgegebenen Ergebnisse. Die Gesamtzahl der Treffer ist eine Eigenschaft von
TopDocs
und steht für alle Übereinstimmungen, während die Anzahl der zurückgegebenen Ergebnisse durch die Länge des ArraysScoreDocs
vonTopDocs
bestimmt wird. - Die Eingabeabfrage von body:Vikings AND Minnesota ergibt eine
BooleanQuery
mit zwei erforderlichen Begriffen, einen gegen den bodyField
und einen gegen den allField
, da es sich um den StandardField
handelt. Das „+“-Zeichen bedeutet, dass der Begriff in derDocument
erscheinen muss.
Irgendwann während der Entwicklung Ihrer Indizierung und Suche werden Sie eine bessere Vorstellung davon bekommen wollen, was sich in Ihrem Index befindet. Zu diesem Zweck hat Andrzej Bialecki ein Tool namens Luke entwickelt. Mit Luke können Sie einen Index öffnen und Dokumente, Token und Dateiformate untersuchen sowie Suchvorgänge zum Testen einreichen.
Abbildung 2. Luke Screenshot
Ein Beispiel-Screenshot von Luke zeigt die Liste der am häufigsten vorkommenden Begriffe
An dieser Stelle habe ich Ihnen die Grundlagen der Lucene-API erläutert. Von hier aus werde ich einen Blick auf die Rolle der Analyse bei der Indizierung und Suche werfen. Wenn Sie mehr über die Suche erfahren möchten, lesen Sie die unten stehenden Ressourcen.
Analyse
Die Analyse wird sowohl bei der Indizierung als auch bei der Suche verwendet, um Token zu erstellen, die hinzugefügt oder im Index nachgeschlagen werden sollen. Die Analyse ist auch der Ort, an dem Operationen zum Hinzufügen, Löschen oder Ändern von Token stattfinden. Im Kern ist die Analyse der Prozess, der Ihre Inhalte in etwas Durchsuchbares verwandelt.
Der Analyseprozess wird von der Klasse Analyzer
gesteuert. Eine Analyzer
besteht aus einer Tokenizer
und null oder mehr TokenFilter
s. Die Tokenizer
arbeitet mit der Eingabe und zerlegt sie in Token. Lucene wird zum Beispiel mit der WhitespaceTokenizer
ausgeliefert, die, wie Sie sich denken können, die Eingabe an Leerzeichen aufteilt. Nach der Tokenisierung werden die Token an die Kette von TokenFilter
übergeben, die ein Token hinzufügen, ändern oder löschen kann. LowerCaseFilter
wandelt beispielsweise alle Zeichen in einem Token in Kleinbuchstaben um, so dass die Groß- und Kleinschreibung bei der Suche nicht berücksichtigt wird. Als Beispiel für eine vollständige Analyzer
besteht die StopAnalyzer
aus:
- LowerCaseTokenizer – Teilt auf Nicht-Buchstaben und dann Kleinbuchstaben
- StopFilter – Entfernt alle Stoppwörter. Stoppwörter sind häufig vorkommende Wörter, die für die Suche wenig wertvoll sind, z. B. „der“, „ein“ und „an“.
Mit zunehmender Lucene-Erfahrung werden Sie wahrscheinlich eine eigene Erweiterung der Klasse Analyzer
für Ihre Bedürfnisse entwickeln, aber für den Anfang empfehle ich die StandardAnalyzer
, da sie in den meisten Freitext-Situationen ziemlich gute Arbeit leistet. Eine vollständige Liste der verfügbaren Analyzer
s, Tokenizer
s und TokenFilter
s finden Sie in den Lucene Javadocs.
Um einige der verschiedenen Analyseergebnisse zu demonstrieren, habe ich das folgende Beispiel zusammengestellt:
Beispiel 4. Analyse Beispiel
String test1 = "Dear Sir, At Acme Widgets, our customer's happiness is priority one.";//
Analyzer analyzer = new WhitespaceAnalyzer();//
printTokens("Whitespace", analyzer.tokenStream("", new StringReader(test1))); analyzer = new SimpleAnalyzer();//
printTokens("Simple", analyzer.tokenStream("", new StringReader(test1))); analyzer = new StandardAnalyzer();//
printTokens("Standard", analyzer.tokenStream("", new StringReader(test1))); analyzer = new SnowballAnalyzer("English");//
printTokens("English Snowball", analyzer.tokenStream("", new StringReader(test1)));
Erstellen Sie eine Zeichenfolge für die Analyse, die einige Satzzeichen, Großbuchstaben usw. enthält, und sehen Sie, wie die verschiedenen Analyseprogramme damit umgehen. | |
Erstellen Sie den WhitespaceAnalyzer, der Leerzeichen aufspaltet und sonst nichts tut | |
Probieren Sie den SimpleAnalyzer aus, der auf Nicht-Buchstaben und dann auf Kleinbuchstaben trennt. | |
Instanziieren Sie den StandardAnalyzer, der eine JavaCC-basierte Grammatik verwendet, um die Tokenisierung zu bestimmen und dann die Kleinschreibung und das Entfernen von Stoppwörtern vornimmt. | |
Der SnowballAnalyzer ähnelt dem StandardAnalyzer, fügt aber einen Schritt hinzu, um die Token zu stemmen. |
Im Analysebeispiel konstruiere ich mehrere verschiedene Analyzer
s und verarbeite dann dieselbe Zeichenkette durch jede von ihnen und gebe die Token aus. Die Ausgabe von SimpleAnalyzer
sieht zum Beispiel so aus:
Analyzer: Simple
Token[0] = (dear,0,4)
Token[1] = (sir,5,8)
Token[2] = (at,10,12)
Token[3] = (acme,13,17)
Token[4] = (widgets,18,25)
Token[5] = (our,27,30)
Token[6] = (customer,31,39)
Token[7] = (s,40,41)
Token[8] = (happiness,42,51)
Token[9] = (is,52,54)
Token[10] = (priority,55,63)
Token[11] = (one,64,67)Inzwischen sieht die Ausgabe von StandardAnalyzer
so aus:
Analysator: Standard
Token[0] = (teuer,0,4,type=<ALPHANUM>)
Token[1] = (sir,5,8,type=<ALPHANUM>)
Token[2] = (acme,13,17,type=<ALPHANUM>)
Token[3] = (Widgets,18,25,type=<ALPHANUM>)
Token[4] = (unser,27,30,typ=<ALPHANUM>)
Token[5] = (Kunde,31,41,Typ=<APOSTROPHE>)
Token[6] = (Glück,42,51,Typ=<ALPHANUM>)
Token[7] = (Priorität,55,63,Typ=<ALPHANUM>)
Token[8] = (eins,64,67,typ=<ALPHANUM>)Beachten Sie, dass die StandardAnalyzer
entfernte Stoppwörter wie „bei“ und „ist“ und behandelte auch das Apostroph in „customer’s“ angemessen, während die SimpleAnalyzer
die Token kleingeschrieben und „customer’s“ in „customer“ und „s“ aufgeteilt. Die vollständige Analyseausgabe finden Sie unten.
Wie ich bereits sagte, wird die Analyse sowohl bei der Indizierung als auch bei der Suche verwendet. Bei der Indizierung erzeugt die Analyse Token, die im Index gespeichert werden. Auf der Abfrageseite erzeugt die Analyse Token, die im Index nachgeschlagen werden. Daher ist es sehr wichtig, dass die Art der bei der Indizierung erzeugten Token mit denen übereinstimmt, die bei der Suche erzeugt werden. Wenn Sie z.B. alle Token bei der Indizierung klein schreiben, aber nicht bei der Suche, dann werden Suchbegriffe mit Großbuchstaben keine Treffer erzielen. Das bedeutet jedoch nicht, dass die Analyse sowohl bei der Indizierung als auch bei der Suche genau gleich sein muss. Viele Benutzer entscheiden sich beispielsweise dafür, Synonyme während der Suche hinzuzufügen, aber nicht während der Indizierung. Aber selbst in diesem Fall sind die Token in ihrer Form ähnlich.
Und schließlich empfehle ich Ihnen, bei der Auswahl oder Erstellung eines Analyzer
so viele Daten wie möglich (in Form von Zufallsstichproben) durch Ihren Analyzer
laufen zu lassen und die Ausgabe zu prüfen. Tools wie Luke können Ihnen auch dabei helfen, zu verstehen, was sich in Ihrem Index befindet. Sehen Sie sich außerdem im Laufe der Zeit Ihre Abfrageprotokolle an und führen Sie eine Analyse aller Abfragen durch, die keine Ergebnisse geliefert haben, um festzustellen, ob es Unstimmigkeiten bei der Analyse gab.
Das sollte Ihnen einen guten Start in das Verständnis der Analyse und einiger der damit verbundenen Probleme geben. Ab hier werde ich die Sache mit einigen abschließenden Gedanken abschließen und Sie Lucene erkunden lassen.
Nächste Schritte
In diesem Artikel bin ich durch die Grundlagen von Lucene gegangen und habe Ihnen gezeigt, wie Sie Ihre Inhalte indizieren, analysieren und durchsuchen können. Hoffentlich habe ich Ihnen auch ein paar Ideen mit auf den Weg gegeben, die Ihre Entwicklung mit Lucene einfach und effektiv machen. Da es sich hier um einen Artikel über die ersten Schritte handelt, habe ich nicht näher darauf eingegangen, wie Sie Lucene in die Produktion bringen. Als nächsten Schritt in Ihrer Entwicklung mit Lucene empfehle ich Ihnen, eine einfache Anwendung zu entwickeln, die Ihre Inhalte durchsucht. Während dieses Prozesses können Sie hier und in den unten aufgeführten Ressourcen nachschlagen, um Ihr Wissen zu erweitern.
Lucene Ressourcen
-
- Lernen Sie, wie Sie Ihre Inhalte auffindbar machen.
- Lernen Sie Tipps und Techniken zur Verbesserung der Relevanz.
- Erfahren Sie in Einführung in Apache Tika, wie Sie gängige Dateiformate aus Sami Siren extrahieren können.
- Erfahren Sie mehr über die Apache Software Foundation
- Besuchen Sie die Lucene-Website, um weitere Lucene-Ressourcen zu finden.
- Erfahren Sie mehr über Lucene Dateiformate
- Erfahren Sie mehr über die Syntax des Lucene Query Parsers
- Besuchen Sie das Lucene-Wiki für weitere Informationen zur Community.
- Lernen Sie Luke zum Debuggen Ihrer Lucene-Entwicklung kennen und verwenden.
- In den Lucene Javadocs finden Sie Hilfe zum Verständnis der Lucene-APIs
- Erfahren Sie auf der Seite Powered By Lucene, wie andere Lucene nutzen.
- Erhalten Sie Tipps und Diskussionen zur Skalierung von Lucene und Solr.
- Erfahren Sie mehr über Lucene in der Lucene-Bibel: Lucene in Aktion (2. Auflage) von Erik Hatcher, Mike McCandless und Otis Gospodnetic.
- Besuchen Sie das nächste Lucene Boot Camp von Grant Ingersoll auf der ApacheCon.
- Erfahren Sie mehr über die Terminologie von Lucene und Solr im Lucid-Glossar.
Theorie der Informationsbeschaffung
Im Folgenden finden Sie eine unvollständige Liste von Ressourcen, die Ihnen helfen, mehr über Information Retrieval (IR) zu erfahren.
- Wikipedia-Artikel zum Vektorraummodell
- SIGIR – Die herausragende Gesellschaft für Forschung im IR.
- Einführung in Information Retrieval von Christopher D. Manning, Prabhakar Raghavan, Hinrich Schütze
- Information Retrieval: Algorithmen und Heuristiken (The Information Retrieval Series)(2. Auflage) von David Grossman und Ophir Frieder
LuceneExample.java
Beispiel 5. Lucene Beispielcode
import org.apache.lucene.store.RAMDirectory; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.TopDocs; import org.apache.lucene.search.Searcher; import org.apache.lucene.queryParser.QueryParser; import org.apache.lucene.queryParser.ParseException; import java.io.IOException; public class LuceneExample { public static void main(String[] args) throws IOException, ParseException { //<start id="LE.doc.creation"/> HTMLDocument doc1 = new HTMLDocument("vikings.html", "Minnesota Vikings Make Playoffs!", "The Minnesota Vikings made the playoffs by" + " beating the New York Giants in the final game of the regular season.", "Minnesota Vikings, New York Giants, NFL");//
Document vikesDoc = new Document(); vikesDoc.add(new Field("id", doc1.getId(), Field.Store.YES, Field.Index.NOT_ANALYZED)); //
Field titleField = new Field("title", doc1.getTitle(), Field.Store.YES, Field.Index.ANALYZED);//
titleField.setBoost(5);//
vikesDoc.add(titleField); vikesDoc.add(new Field("body", doc1.getBody(), Field.Store.YES, Field.Index.ANALYZED)); vikesDoc.add(new Field("all", doc1.getTitle() + " " + doc1.getBody(), Field.Store.NO, Field.Index.ANALYZED));//
/* <calloutlist> <callout arearefs="LE.doc.creation.seed"><para>Create a simple representation of an HTML document.</para></callout> <callout arearefs="LE.doc.creation.id"><para>Store and Index the id, but only allow for exact matches</para></callout> <callout arearefs="LE.doc.creation.title"><para>Add the title to the <classname>Document</classname></para></callout> <callout arearefs="LE.doc.creation.title.boost"><para>Indicate that the title <classname>Field</classname> is more important than the other <classname>Field</classname>s</para></callout> <callout arearefs="LE.doc.creation.all"><para>Create a <classname>Field</classname> that contains both the title and the body for searching across both <classname>Field</classname>s. Notice the content is not stored.</para></callout> </calloutlist> */ //<end id="LE.doc.creation"/> HTMLDocument doc2 = new HTMLDocument("lucene.html", "Apache Lucene: The fun never stops!", "Apache Lucene is a fast, Java-based search library.", "Apache, Lucene, Java, search, information retrieval"); Document luceneDoc = new Document(); luceneDoc.add(new Field("id", doc2.getId(), Field.Store.YES, Field.Index.NOT_ANALYZED)); titleField = new Field("title", doc2.getTitle(), Field.Store.YES, Field.Index.ANALYZED); titleField.setBoost(5); luceneDoc.add(titleField); luceneDoc.add(new Field("body", doc2.getBody(), Field.Store.YES, Field.Index.ANALYZED)); luceneDoc.add(new Field("all", doc2.getTitle() + " " + doc2.getBody(), Field.Store.NO, Field.Index.ANALYZED)); //<start id="idx"/> RAMDirectory directory = new RAMDirectory();//
Analyzer analyzer = new StandardAnalyzer();//
IndexWriter writer = new IndexWriter(directory, analyzer, true, IndexWriter.MaxFieldLength.LIMITED);//
writer.addDocument(vikesDoc); //
writer.addDocument(luceneDoc); //
for (int i = 0; i < 10; i++){//
Document doc = new Document(); doc.add(new Field("id", "page_" + i + ".html", Field.Store.YES, Field.Index.NOT_ANALYZED)); doc.add(new Field("title", "Vikings page number: " + i, Field.Store.YES, Field.Index.ANALYZED)); doc.add(new Field("body", "This document is number: " + i + " about the Vikings.", Field.Store.YES, Field.Index.ANALYZED)); doc.add(new Field("all", "Vikings page number: " + i + " This document is number: " + i + " about the Minnesota Vikings.", Field.Store.NO, Field.Index.ANALYZED)); writer.addDocument(doc); } writer.optimize();//
writer.close();//
/* <calloutlist> <callout arearefs="idx.ram"><para>Instantiate a memory-based <classname>Directory</classname>. This index is transient.</para></callout> <callout arearefs="idx.analyzer"><para>Create an <classname>Analyzer</classname> to be used by the <classname>IndexWriter</classname> for creating <classname>Token</classname>s.</para></callout> <callout arearefs="idx.index.writer"><para>Create the <classname>IndexWriter</classname> responsible for indexing the content.</para></callout> <callout arearefs="idx.vikes"><para>Add the Vikings document to the writer.</para></callout> <callout arearefs="idx.lucene"><para>Add a second document to the writer</para></callout> <callout arearefs="idx.other"><para>For good measure, add in some other documents.</para></callout> <callout arearefs="idx.optimize"><para>Optional. Optimize the index for searching.</para></callout> <callout arearefs="idx.close"><para>Close the IndexWriter. Content won't be searchable until close() is called (in this scenario).</para></callout> </calloutlist> */ //<end id="idx"/> //<start id="search"/> Searcher searcher = new IndexSearcher(directory);//
QueryParser qp = new QueryParser("all", analyzer);//
Query query = qp.parse("body:Vikings AND Minnesota"); //(16) TopDocs results = searcher.search(query, 10);//(17) printResults(query, searcher, results); query = qp.parse("Lucene"); results = searcher.search(query, 10); printResults(query, searcher, results); /* <calloutlist> <callout arearefs="search.srchr"><para>Construct a <classname>Searcher</classname> for searching the index.</para></callout> <callout arearefs="search.qp"><para>The Lucene <classname>QueryParser</classname> can be used for converting a user's input query into a <classname>Query</classname>. The constructor takes a default search <classname>Field</classname> and the <classname>Analyzer</classname> to use to create <classname>Token</classname>s.</para></callout> <callout arearefs="search.parse"><para>The <methodname>parse</methodname> does the actual work of constructing a <classname>Query</classname>.</para></callout> <callout arearefs="search.search"><para>Execute the search, requesting at most 10 results</para></callout> </calloutlist> */ //<end id="search"/> } private static void printResults(Query query, Searcher searcher, TopDocs results) throws IOException { System.out.println("Query: " + query); System.out.println("Total Hits: " + results.totalHits); for (int i = 0; i < results.scoreDocs.length; i++){ System.out.println("Doc: " + searcher.doc(results.scoreDocs[i].doc)); } } }
Hinweis: Dieses Beispiel enthält Kommentare, die von einem automatischen System zur Dokumentenerstellung verwendet werden. Ignorieren Sie daher bitte Tags wie <callout> und <co>, usw. Um es zu kompilieren, benötigen Sie die Bibliothek lucene-core-2.4.0.jar.
AnalyseBeispiel.java
Beispiel 6. Analyse Beispiel Code
import java.io.IOException; import org.apache.lucene.analysis.SimpleAnalyzer; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.Token; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.WhitespaceAnalyzer; import org.apache.lucene.analysis.snowball.SnowballAnalyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; import java.io.StringReader; public class AnalysisExample { public static void main(String[] args) throws IOException { //<start id="analysis"/> String test1 = "Dear Sir, At Acme Widgets, our customer's happiness is priority one.";//
Analyzer analyzer = new WhitespaceAnalyzer();//
printTokens("Whitespace", analyzer.tokenStream("", new StringReader(test1))); analyzer = new SimpleAnalyzer();//
printTokens("Simple", analyzer.tokenStream("", new StringReader(test1))); analyzer = new StandardAnalyzer();//
printTokens("Standard", analyzer.tokenStream("", new StringReader(test1))); analyzer = new SnowballAnalyzer("English");//
printTokens("English Snowball", analyzer.tokenStream("", new StringReader(test1))); /* <calloutlist> <callout arearefs="analysis.str"><para>Create a string for analysis that has some punctuation, capital letters, etc., and see how different analyzers treat it.</para></callout> <callout arearefs="analysis.white"><para>Create the WhitespaceAnalyzer, which splits on whitespace and does nothing else</para></callout> <callout arearefs="analysis.simple"><para>Try out the SimpleAnalyzer, which splits on non-letters and then lowercases.</para></callout> <callout arearefs="analysis.standard"><para>Instantiate the StandardAnalyzer, which uses a JavaCC-based grammar to determine tokenization and then lowercases and removes stopwords.</para></callout> <callout arearefs="analysis.snow"><para>The SnowballAnalyzer is similar to the StandardAnalyzer, but adds a step to stem the tokens.</para></callout> </calloutlist> */ //<end id="analysis"/> } private static void printTokens(String name, TokenStream tokenStream) throws IOException { System.out.println("Analyzer: " + name); Token token = new Token(); int i = 0; while ((token = tokenStream.next(token)) != null) { System.out.println("Token[" + i + "] = " + token); i++; } } }
Hinweis: Dieses Beispiel enthält Kommentare, die von einem automatischen System zur Dokumentenerstellung verwendet werden. Ignorieren Sie daher bitte Tags wie <callout> und <co>, usw. Um es zu kompilieren, benötigen Sie die Bibliotheken lucene-core-2.4.0.jar, lucene-analyzers-2.4.0.jar und lucene-snowball-2.4.0.jar.
Ausgabe von AnalysisExample.java
Ich erhalte die folgende Ausgabe, wenn ich das Analysebeispiel ausführe.
Analyzer: Whitespace
Token[0] = (Liebe,0,4)
Token[1] = (Herr,,5,9)
Token[2] = (At,10,12)
Token[3] = (Acme,13,17)
Token[4] = (Widgets,,18,26)
Token[5] = (unser,27,30)
Token[6] = (Kunde,31,41)
Token[7] = (Glück,42,51)
Token[8] = (ist,52,54)
Token[9] = (Priorität,55,63)
Token[10] = (eins.,64,68)
Analyzer: Einfach
Token[0] = (teuer,0,4)
Token[1] = (Sir,5,8)
Token[2] = (at,10,12)
Token[3] = (acme,13,17)
Token[4] = (Widgets,18,25)
Token[5] = (unser,27,30)
Token[6] = (Kunde,31,39)
Token[7] = (s,40,41)
Token[8] = (Glück,42,51)
Token[9] = (ist,52,54)
Token[10] = (Priorität,55,63)
Token[11] = (eins,64,67)
Analysator: Standard
Token[0] = (teuer,0,4,type=<ALPHANUM>)
Token[1] = (sir,5,8,type=<ALPHANUM>)
Token[2] = (acme,13,17,type=<ALPHANUM>)
Token[3] = (Widgets,18,25,type=<ALPHANUM>)
Token[4] = (unser,27,30,typ=<ALPHANUM>)
Token[5] = (Kunde,31,41,Typ=<APOSTROPHE>)
Token[6] = (Glück,42,51,Typ=<ALPHANUM>)
Token[7] = (Priorität,55,63,Typ=<ALPHANUM>)
Token[8] = (eins,64,67,typ=<ALPHANUM>)
Analyzer: Englischer Schneeball
Token[0] = (teuer,0,4,type=<ALPHANUM>)
Token[1] = (sir,5,8,type=<ALPHANUM>)
Token[2] = (at,10,12,type=<ALPHANUM>)
Token[3] = (acm,13,17,type=<ALPHANUM>)
Token[4] = (widget,18,25,type=<ALPHANUM>)
Token[5] = (unser,27,30,typ=<ALPHANUM>)
Token[6] = (custom,31,41,type=<APOSTROPHE>)
Token[7] = (happi,42,51,type=<ALPHANUM>)
Token[8] = (ist,52,54,typ=<ALPHANUM>)
Token[9] = (prioriti,55,63,type=<ALPHANUM>)
Token[10] = (eins,64,67,typ=<ALPHANUM>)Kommentare? Mailen Sie uns an comments@lucidimagination.com