Volltextsuche à la Lucene

Mit Apache Lucene verfügt die Open-Source- und Java-Welt über eine hochwertige Volltext-Suchmaschine, die sogar einfach zu bedienen ist.

Artikel erschienen in Swiss IT Magazine 2006/18

     

Lucene ist ein Apache-Projekt, das sicher nicht so im Rampenlicht steht wie beispielsweise myFaces, Struts oder der Apache Webserver, aber dennoch als Perle im Apache-Softwarepool angesehen werden kann. Bei Lucene handelt es sich um ein Framework zur (Volltext-) Indizierung von Datenbeständen sowie zur effizienten und flexiblen Suche in den Indizes. Es richtet sich somit ausschliesslich an Entwickler, die Such-Funktionalität in ihren Systemen implementieren möchten, und hat sich wegen seiner einfachen Anwendbarkeit, Flexibilität und Leistungsfähigkeit zum De-facto Standard entwickelt. So setzen auch verschiedene andere Open-Source-Projekte wie das Cocoon Framework Lucene ein, in dessen Rahmen es als Suchmaschine verwendet werden kann.


Gute Vorbereitung

Möchte man Anwendungen entwickeln, die grössere Datenbestände effizient durchsuchen, so besteht dieses Vorhaben typischerweise aus drei Schritten:



- Zunächst sollte eine gewisse Struktur in die Daten gebracht werden. Für hochstrukturierte Datenbestände besteht das in der Regel darin, dass ein relationales Datenbankmodell erstellt wird und die Daten in einer entsprechenden Datenbank verwaltet werden. Ist der Datenbestand aber weniger gut strukturiert beziehungsweise heterogen und steht die Suche im Vordergrund, was die Domäne von Werkzeugen wie Lucene ist, definiert man in der Regel neben den Nutzdaten Metadaten, die auch die heterogenen Daten einigermassen gut beschreiben können. Ausserdem muss definiert werden, welche Daten überhaupt indiziert und welche Daten im Index mit abgelegt werden. Dies ist wichtig, da der Datenumfang einen erheblichen Einfluss auf den Umfang des Indexes sowie die Dauer des Indizierungsvorganges hat.




- Der zweite Schritt besteht darin, den Index zu erstellen. Dieser Schritt ist unbedingt erforderlich, wenn in grossen Datenmengen schnell gesucht werden soll. Genau an dieser Stelle liegt auch eine Menge an Know-how, das erforderlich ist, um einen Index so zu erstellen, dass verschiedenartige Suchanfragen effizient bearbeitet werden können.
Beim Indizieren sind verschiedene Dinge zu bedenken: Zunächst wird normalerweise (längerer) Text in einzelne Worte zerlegt (Tokenizing). Dann werden sogenannte Stoppworte wie «und» oder «aber» ausgeschlossen und die Gross- und Kleinschreibung vereinheitlicht. Dann erfolgt das sogenannte Stemming, bei dem Worte in eine einheitliche Form gebracht werden. Dabei werden Abwandlungen eines Wortes wie zum Beispiel «lesen», also «gelesen», «liest» oder «las», in eine einheitliche Form wie «lesen» gebracht, die besser zum Durchsuchen geeignet ist, wobei die tatsächlich ausgeführten Umformungen vom jeweils verwendeten Algorithmus abhängen. Dies gilt auch für die anderen Schritte, die zudem stark von der jeweiligen Sprache und dem Anwendungsbereich abhängig sind.



- Der dritte Schritt besteht letztendlich darin, eine geeignete Such-Schnittstelle für die Benutzer zur Verfügung zu stellen beziehungsweise auch verschiedene Möglichkeiten für unterschiedliche Nutzergruppen anzubieten. Beispielsweise könnte man bei einem Dokumenten-Managementsystem für interne Nutzer eine Schnittstelle anbieten, die unscharfes oder Wildcard-Suchen erlaubt, während das externe Web-Interface diese Möglichkeit beispielsweise wegen Missbrauchsgefahr nicht bietet.


Das Lucene-Framework implementiert alle oben genannten Funktionen und bietet dem Entwickler eine sehr einfach zu verwendende API an, um die jeweiligen Vorgänge anzustossen.


Alles ein Dokument

Im Gegensatz zu Datenbanken arbeitet Lucene immer mit «Dokumenten», wobei der Dokumentbegriff nicht zu wörtlich zu nehmen ist. Im Prinzip ist ein Dokument ein Container, der Metadaten enthält, und besteht technisch gesehen aus Feldern. Jedes Feld hat einen Namen und einen textuellen Inhalt. Wenn man nun alle Fotos auf einer Festplatte indizieren möchte, so könnte man ein Programm schreiben, das zunächst alle JPEG-Dateien sucht und dann mit einer geeigneten Bibliothek die EXIF-Header (Kameradaten) und IPTC-Metadaten (Stichworte, Ort, Name des Fotografen usw.), die in der Bilddatei gespeichert werden können, aus jedem Bild extrahiert.



Im Verständnis von Lucene könnte man also zum Beispiel die Felder Titel, Ort, Fotograf, Stichworte, Kurzbeschreibung, Aufnahmedatum, Dateiname und Ort der JPEG-Datei definieren. Diese werden pro Foto in einem Dokument zusammengefasst und dieses Dokument der Lucene-Indexing-Komponente übergeben. Diese ergänzt dann den jeweiligen Index um dieses «Dokument». Dabei kann man sich unter anderem noch entscheiden, ob ein Feld wie Stichworte oder Kurzbeschreibung in Tokens (einzelne Worte) zerlegt oder ob es ohne weitere Veränderung indiziert werden soll, wie man das bei Einzel-Informationsfeldern wie Datum oder Ort machen würde.



Der Dokument-Begriff muss sich aber nicht unbedingt auf ein tatsächliches Dokument oder eine real existierende Datei beziehen, wie das bei typischen Indizierungen von Office-Dokumenten oder PDFs der Fall ist. Man kann beispielsweise Ergebnisse einer SQL-Abfrage aus einer Datenbank in Lucene-«Dokumente» abfüllen und indizieren lassen oder die Webseiten, die ein Content-Management-System dynamisch erstellt und die somit gar nicht als reale Dokumente vorliegen. Ebenso in Frage kommen Daten aus XML Streams oder E-Mails eines Mailservers. Wichtig ist nur, dass es irgendeine eindeutige Möglichkeit gibt, auf die entsprechende Ressource zu verweisen. Andernfalls wäre es nicht möglich, von der Suche in den Metadaten auf die eigentlichen Nutzdaten zuzugreifen.
Im Beispiel der Fotoindizierung könnte der eindeutige Identifikator das Verzeichnis und der Dateiname sein, im Falle des CMS die URL und im Falle von Datenbankabfragen der Wert des Primärschlüssels.


Indexpflege

keit, neue Dokumente in den Index aufzunehmen, alte zu löschen oder Dokumente zu ändern. Die Kernklassen sind hier IndexWriter, Document, Field sowie Analyzer für die verschiedenen Sprachen. Interessant ist hierbei, dass ein Index nicht unbedingt in einem Verzeichnis auf der Festplatte liegen muss. Die Index-Speicherung ist so abstrahiert, dass neben der Festplatte verschiedene Speicherorte wie RAM oder eine relationale Datenbank als Index-Speicher verwendet werden können.
Ein zweiter wichtiger Teil der Lucene-API kümmert sich um die eigentliche Suche und bietet verschiedenste Möglichkeiten, Suchen zu formulieren. Die wesentlichen Klassen sind:



- IndexSearcher




- Query und deren Subklassen



- QueryParser



- Hits



IndexSearcher stellt die Verbindung zum Index her. Die eigentliche Abfrage findet in den verschiedenen Query-Klassen statt, die sich auf einzelne Felder beziehen können. Hier existieren neben dem Einsatz von boolschen Operatoren folgende Abfragemöglichkeiten:



- Suche nach einem bestimmten Begriff («Java»)



- Suche mit Wildcards («Java*»)



- Unscharfe Suche («Java~» findet auch «Lava»)



- Suche nach benachbarten Begriffen («Java Applikationsserver~4» findet Dokumente, in denen diese beiden Begriffe in unmittelbarer Nähe von vier Worten gemeinsam vorkommen)



- Bereichsabfragen («1.1.1986 bis 31.12.1995»)
Mehrere solche Abfragen können ausserdem gruppiert werden. Dies lässt sich am besten anhand des Foto-Beispiels erklären, bei dem man folgende Suche mit Hilfe der API formulieren könnte: Im Aufnahmeort des Fotos soll «Nationalpark» und «Kalkalpen» vorkommen, im Stichwort «Sommer» oder «Herbst», das Aufnahmejahr liegt zwischen 1986 und 1995 und die Brennweite des Obektivs war grösser als 300 mm.
Hat man die Query erstellt, wird ein Hits-Objekt instanziert, aus dem die Ergebnisse ausgelesen werden können.


Eigene Sprache

Neben der konkreten programmatischen Formulierung solcher Queries über die entsprechenden Klassen bietet Lucene eine QueryParser-Klasse an, die eine Lucene-spezifische Abfragesprache implementiert. Damit können von Menschen geschriebene Anfragen in das interne Lucene-Format übersetzt werden. So ist es möglich, schnell ein sehr mächtiges Such-Interface in eigenen Anwendungen anzubieten.
Aus der genannten Funktionalität lassen sich somit durchaus Parallelen zu Datenbanksystemen ziehen. So entspricht ein Dokument mit Feldern im Prinzip einer Tabelle einer relationalen Datenbank, wenngleich einige Einschränkungen existieren. Tatsächlich gibt es mittlerweile einige Anwender, die in bestimmten Anwendungsgebieten ihre Objekte gar nicht mehr in eine relationale Datenbank speichern, sondern beispielsweise als XML-Dokument auf der Festplatte ablegen und den Index mit Lucene erstellen lassen. Dies ist vor allem dann eine sehr einfache und schnelle Methode, um Daten verfügbar zu machen, wenn man einen vergleichsweise statischen Datenbestand mit wenig Änderungen und keine Transaktionen hat und der Schwerpunkt auf schneller Suche und effizientem Zugriff liegt.


Projekte mit Lucene

Im Umfeld des Lucene-Frameworks sind viele weitere Projekte zu nennen, die Lucene verwenden oder den Einsatz von Lucene einfacher machen. Besonders erwähnenswert ist das Tool Luke (www.getopt.org/luke). Luke ist eine Java-Anwendung, die es erlaubt, beliebige Lucene-Indizes zu öffnen und zu analysieren. Man kann sich die Dokument- und Feldstruktur ansehen, die häufigsten Suchbegriffe anzeigen lassen oder Suchen absetzen.
Dieses Werkzeug ist ganz besonders für Entwickler von Lucene-Anwendungen empfehlenswert. Denn Lucene speichert Indizes aus Effizienzgründen in einem Binärformat ab, das mit normalen Editoren nicht vernünftig analysiert werden kann.
Mit Luke hat man aber die Möglichkeit, zu überprüfen, ob der Index tatsächlich so aussieht, wie man ihn geplant hat. Damit wird die Fehlersuche im Falle von Problemen vereinfacht.
Daneben existieren verschiedenste Open-Source-Anwendungen wie Eclipse oder die Groupware-Plattform Zimbra, die Lucene zur Suche verwenden.


Bröckelige Dokumentation

Das Lucene-Projekt wurde im Jahr 2000 als Sourceforge-Projekt gestartet und ist 2002 zur Apache Software Foundation übergesiedelt. Im Augenblick liegt Lucene in der Version 2.0 vor. Es ist ein sehr stabiles Projekt und kann unbedingt für die genannten Anwendungsfälle empfohlen werden.
Die Dokumentation von Lucene besteht aus der Webseite, dem Wiki und der Javadoc-Dokumentation. Leider entspricht die Dokumentation nicht der Qualität des Projekts und dem Standard vieler anderer Apache-Projekte. Dies gilt besonders im Hinblick auf Einsteiger. Die Javadoc-Dokumentation ist zwar ausreichend, aber für den Start gibt es nur ein einfaches, aber schlecht beschriebenes Beispiel.
Überblick-Dokumentation, Beschreibung der Abfragesprache, Erklärung der Konzepte, Feature-Listen und einige andere Dinge sucht man in der offiziellen Distribution leider vergeblich. Allerdings existieren mittlerweile einige gute Artikel und Tutorials, die im Lucene-Wiki verlinkt sind, sowie zwei Bücher. Besonders «Lucene in Action» (ISBN 1-932394-28-1) ist empfehlenswert, wenn man Lucene ernsthaft einsetzen möchte.
Weiter ist erwähnenswert, dass der grosse Erfolg von Lucene in der Java-Welt dazu geführt hat, dass mittlerweile eine Unzahl an Portierungen auf andere Plattformen existiert, darunter Perl, Python, C++, .Net und Ruby. Dies alleine zeigt die Beliebtheit des Projektes. Allerdings muss man einschränkend anmerken, dass noch nicht alle Portierungen dieselbe Qualität aufweisen wie die ursprüngliche Java-Variante.





Anwendungszenario von Lucene


Der Autor

Alexander Schatten ( alexander@schatten.info ) ist Assistent am Institut für Softwaretechnik und interaktive Systeme der Technischen Universität Wien.




Artikel kommentieren
Kommentare werden vor der Freischaltung durch die Redaktion geprüft.

Anti-Spam-Frage: Wieviele Zwerge traf Schneewittchen im Wald?
GOLD SPONSOREN
SPONSOREN & PARTNER