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 Fields. Ein Lucene Document ist eine logische Gruppierung eines Inhalts. Documents repräsentieren typischerweise eine Datei oder einen Datensatz in einer Datenbank, aber das ist keine Voraussetzung. Documents bestehen aus einem oder mehreren Fields. Ein Field enthält den eigentlichen Inhalt zusammen mit Metadaten, die beschreiben, wie der Inhalt von Lucene behandelt werden soll. Fields sind oft Dinge wie ein Dateiname, der Inhalt einer Datei oder eine bestimmte Zelle in einer Datenbank. Fields 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.
  • Feld.Store.YES
  • Feld.Speicher.NO
  • Field.Store.COMPRESS – Komprimiert den Inhalt unter Verwendung der in Java integrierten Komprimierungseinstellungen auf der höchsten Stufe. Nicht empfohlen. Verwenden Sie stattdessen eine binäre Field mit Ihrer eigenen Komprimierung.
Index Gibt an, ob der Inhalt durchsuchbar sein soll oder nicht. Legt auch fest, ob der Inhalt analysiert werden soll oder nicht.
  • Field.Index.NO – Den Inhalt nicht indizieren. Wird oft verwendet, wenn eine Field mit Lucene gespeichert wird, die aber nicht durchsuchbar ist.
  • Field.Index.ANALYZED – Indexiert den analysierten Inhalt. Verwendet den Analyseprozess, um den Inhalt in Tokenaufzuteilen, die dann dem Index hinzugefügt werden. Dies ist das wichtigste Mittel für die Suche in freiem Text.
  • Field.Index.NOT_ANALYZED – Indizieren Sie den Inhalt, aber analysieren Sie ihn nicht. Der Inhalt wird als eine einzige Zeichenfolge hinzugefügt. Er ist also auffindbar, aber nur als exakte Übereinstimmung.
  • Field.Index.NOT_ANALYZED_NO_NORMS, Field.Index.ANALYZED_NO_NORMS – Varianten der beiden vorherigen Einträge, die die Verwendung von Normalisierungsfaktoren in Lucene steuern. Diese sind etwas fortgeschrittener und ich werde sie hier nicht behandeln.


Schließlich können sowohl ein Document als auch die Fields 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 Fields eines Document. Das bedeutet, dass ein Document zwanzig Fields 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 Fields 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 Fields
Erstellen Sie eine Field, die sowohl den Titel als auch den Text enthält, um beide Fieldzu durchsuchen. Beachten Sie, dass der Inhalt nicht gespeichert wird.


In diesem Beispiel erstelle ich zunächst eine Document und füge dann mehrere Fieldhinzu. 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 Fieldhinzugefü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 Documents in die internen Datenstrukturen von Lucene. Während des Indizierungsprozesses verwendet Lucene die Document, Fields 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 Documents und wird durch die abstrakte Klasse Directory dargestellt. Directorys 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 Documentzum 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();//

2

IndexWriter writer = new IndexWriter(directory, analyzer, true, IndexWriter.MaxFieldLength.LIMITED);//

3

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 Tokenverwendet 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 Documenthinzugefü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 Queryaus 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 Tokenverwendet 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 Documents. 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 Arrays ScoreDocs von TopDocs bestimmt wird.
  • Die Eingabeabfrage von body:Vikings AND Minnesota ergibt eine BooleanQuery mit zwei erforderlichen Begriffen, einen gegen den body Field und einen gegen den all Field, da es sich um den Standard Field handelt. Das „+“-Zeichen bedeutet, dass der Begriff in der Document 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 TokenFilters. 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 Analyzers, Tokenizers und TokenFilters 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 Analyzers 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

Theorie der Informationsbeschaffung

Im Folgenden finden Sie eine unvollständige Liste von Ressourcen, die Ihnen helfen, mehr über Information Retrieval (IR) zu erfahren.

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

You Might Also Like

Vom Suchunternehmen zum praktischen KI-Pionier: Unsere Vision für 2025 und darüber hinaus

CEO Mike Sinoway gibt Einblicke in die Zukunft der KI und stellt...

Read More

Wenn KI schief geht: Fehlschläge in der realen Welt und wie man sie vermeidet

Lassen Sie nicht zu, dass Ihr KI-Chatbot einen 50.000 Dollar teuren Tahoe...

Read More

Lucidworks Kernpakete: Branchenoptimierte KI-Such- und Personalisierungslösungen

Entdecken Sie unsere umfassenden Core Packages, die Analytics Studio, Commerce Studio und...

Read More

Quick Links