Verschiedene Möglichkeiten, einen Lucene-Suchindex via PHP einzubinden

Lucene – Ein Suchindex in der Praxis

“Gehen dem Menschen Hühner und Hunde verloren, so weiß er, wo er sie suchen soll. Geht ihm sein Herz verloren, so weiß er nicht, wo er es suchen soll.” (Mengzi, chines. Philosoph). Mit einer Suchmaschine findet man weder die Hühner noch das verloren gegangene Herz – dafür aber sehr schnell solche Weisheiten. Der Artikel beschreibt drei Möglichkeiten, um mit PHP einen Suchindex mittels Lucene aufzubauen und abzufragen. Auf Basis des so erzeugten Indexes kann man dann im Folgenden eine eigene Suchmaschine für Dokumente oder Webseiten betreiben. Jede der Möglichkeiten wird mit ihren Licht- und Schattenseiten beschrieben.

Nur allzu oft werden Suchmaschine und Suchindex nicht nur in einem Atemzug genannt, sondern auch fälschlicherweise als Synonyme verwendet. Vorab zur Begriffsklärung: Ein Suchindex ist nichts weiter als eine Sammlung von Verweisen auf anderswo gespeicherte Daten. Meist ist dies eine Datei, in der Informationen über Dokumente oder Webseiten abgelegt sind. Der Vorteil eines Indexes ist, dass man Daten sehr schnell wiederfinden kann und das Geheimnis liegt darin, wie die verschiedenen Algorithmen die Daten dort ablegen. Suchmaschinen hingegen bestehen aus primär drei Teilen: einem Suchindex, in dem die Daten gehalten werden, einer Abfrageschnittstelle, wie z. B. eine Webseite mit PHP, und einer Möglichkeit, dem Suchindex neue Daten zuzuführen, wobei die meisten hier an einen Crawler oder auch Spider denken, der Webseiten abruft und den Inhalt automatisch in dem Index ablegt. Man kann die Daten aber auch mittels eigener Routinen in den Suchindex einfügen (Programmbeispiele).

Hinweis

Auf der CD zu diesem Heft finden sich neben den kompletten Beispielprogrammen auch einige Installationsskripte, die auf einem Unix-basierten System eine PHP-Umgebung übersetzen und sie mit entsprechender Suchmaschine ablauffertig ins Home-Verzeichnis installieren.

Lucenes Anfänge

Doug Cutting ist der Erfinder des in Java entwickelten Open-Source-Projektes Lucene. Die ersten Schritte machte Lucene 1997, und ein erster Prototyp war im folgenden Jahr verfügbar. Seit 2001 ist Lucene bei der Apache Software Foundation angesiedelt und bildet dort eine der Top-Applikationen. Beruflich hat Cutting bei Apples Suchhilfe “Sherlock” und bei dem Suchmaschinenbetreiber eXite mitgewirkt, und am 13. März 2006 verkündete er in seinem Blog: I’m now a ‚Yahoo!

Noch echte Handarbeit

Lucene ist genau genommen ein Suchindexframework, und das bedeutet im Klartext nichts anderes, als dass es in den Händen des Entwicklers liegt, woher er seine Daten für den Index bekommt und was nach einer Suchanfrage damit passieren soll. Lucene stellt nur die Programmschnittstelle und die dahinterliegende Technologie zur Verfügung. Wer den Inhalt von Dateien in einem Lucene-Index abspeichern möchte, muss sich daher selber darum kümmern, wie er diesen Inhalt aus den Quellen, z. B. PDF-Dokumente oder HTML-Seiten, bekommt. Genau an dieser Stelle kommen dann diverse Konverter, Hilfsprogramme oder andere Programmbibliotheken ins Spiel (Tabelle 1): Möglichkeiten zur Erschließung von Dateiinhalten. Mithilfe von ein paar zusätzlichen Zeilen Code ist das aber kein Problem und ist schnell realisiert.

PDF

PdftotextpdfboxiTextPDF2HTML

MS-Word

Antiword oder Zend-Frameworkbereich Lucene

RFT

Unrtf

OpenOffice

OooPy

Txt

direkt

HTML

Html2txt oder einfach lynx –d

Tabelle 1: Möglichkeiten zur Erschließung von Dateiinhalten

Input – output – putput?

Nach wie vor gibt es einige Benutzer von Suchmaschinen, die meinen, dass ein Suchindex Daten nur unstrukturiert speichern kann. Dieses Vorurteil basiert oft auf einer eingeschränkten Sicht der Ausgabe von Suchmaschinen und an einem Mangel an Erfahrung. Ein Suchindex speichert sehr wohl strukturierte Daten, und in den Beispielen werden wir uns das auch zu Nutze machen. Zur Hierarchie sei gesagt, dass ein Lucene-Suchindex viele Dokumente speichern kann, die aus vielen Feldern bestehen und die wiederum aus vielen Wörtern zusammengesetzt sein können (Abb. 1). Bei den Feldern (Java-Originalbezeichung Field.Index,) kann der Benutzer aus einer Hand voll Möglichkeiten aussuchen, wie er die Daten im Index ablegen möchte, z. B. nicht durchsuchbar oder analysiert und durchsuchbar. Nun fragt man sich, was dieses “analysiert” bedeutet – man kann es mit einer Art Aufbereitung der Daten beschreiben. So gibt es Listen mit Stoppwörtern, die für gewöhnlich nicht in einem Index abgelegt werden. Aus dem Bibliothekskatalog kennt man dieses von dem Buchtitelanfang, wo “der”, “die”, “das” nicht als Wörter am Titelanfang für den Katalog verwendet werden.

Abb. 1: Struktureller Aufbau eines Suchindex

Falls man die Funktion zum Löschen eines Eintrags sucht, sollte man im Hinterkopf haben, dass man eigentlich nicht aus einem Suchindex löscht. Intern werden gelöschte Einträge in einer separaten Datei gehalten. Bei größeren Löschaktionen sollte man den Index neu aufbauen.

[ header = Seite 2: Differenziert suchen ]

Differenziert suchen

Wer sich für den Einsatz eines auf Lucene basierenden Suchindexes entscheidet, entscheidet im gleichen Zug auch, dass die Daten auf verschiedenste Art wieder gesucht werden können. Die meisten Nutzer kennen nur das Eingabefeld, in das man ein oder zwei Wörter eintippt, aber dass man mitunter auch sehr differenziert suchen kann, ist ihnen nicht bewusst. Hier hilft nur konsequente Aufklärung – denn Lucene kennt sowohl Term- und Phrasensuche mit Booleschen Operatoren als auch eine Wildcard-Suche, Fuzzy-Suche oder Bereichsanfragensuche. Eine Übersicht findet sich in Tabelle 2: Die Lucene Suchabfragesyntax.

Phrasensuche

php: Sucht genau das Wort php in allen indexierten Feldern und “PHP Java” sucht genau die Zeichenfolge PHP Java

Feldersuche

titel: Framework: Sucht nur in den Feldern titelnach dem Wort Framework

Boolesche Logikand, or, not, ()

titel:Zend AND autor:Müller oder +titel:Zend + autor:Müller: Dieser Ausdruck sucht in dem Feldtitel nach dem Wort Zend und in dem Feldautor nach dem Wort Müller, und nur wenn beides in einem Dokument gefunden wird, ist es ein Treffer

Wildcards

p?p: Ein Fragezeichen ist ein Platzhalter für genau einen Buchstaben, und ein p*p sucht nach allem, was mit p anfängt und mit paufhört. Achtung: Ein Wildcard darf nie am Anfang eines Wortes stehen.

Verstärkungsfaktor

Der Verstärkungsfaktor wirkt sich die Reihenfolge der Suchtreffer beim Retrieval aus:php^4 java bewirkt, dass die Treffer mit phpum den Faktor vier höher gewichtet werden als die mit java.

Fuzzy-Suche (nach dem Levenshtein-Distanz-Algorithmus)

Der Algorithmus ermittelt die Anzahl der Operationen, die nötig sind, um von einem Wort auf ein anderes zu kommen. Also findet die Suche Framework auch Flamework oderFrammwerk. (Die Fuzzy-Suchergebnisse werden mit dem Verstärkungsfaktor 0,2 gewichtet.)

Distanzsuche

php java~7: Sucht einen Treffer, bei dem die Wörter php und java nicht weiter als sieben Wörter entfernt sind.

Bereichssuche

mod_date:[20010101 TO 20030101] sucht in einem Bereich inklusiv der beiden Bereichswerte und z.B. title:{Apfel TO Birne}sucht exklusiv der beiden Werte

Syntaxausschluss

(1+1):2: Findet genau den Ausdruck (1+1):2. Reserviert sind: esc + – || ! ( ) { } [ ] ^ ” ~ * ? : }

Tabelle 2: Die Lucene-Suchabfragesyntax

Die drei Fragezeichen

Derzeit existieren drei Möglichkeiten, um sich Lucene zu Nutze zu machen, und die drei Möglichkeiten könnten nicht unterschiedlicher sein. Dabei sollte noch erwähnt werden, dass hier keine Lösungen berücksichtigt werden, bei denen eine Middleware-Kommunikation via SOAP oder XML-PRC eingesetzt wird, denn diese Art des Datenaustausches ist zwar nach wie vor möglich, stellt aber aufgrund des nicht zu vernachlässigenden Kommunikations-Overheads zwischen den Objekten die langsamste und somit auch die schlechteste Alternative dar. Im Folgenden sollen die drei sinnvollen Wege kurz erläutert werden.

Original Lucene mit PHP-Java-Bridge

Die PHP-Java-Bridge (PJB) ist eine Entwicklung, mit der man Java-Programme starten kann und die dann zu diesen Prozessen eine Kommunikation via JSR 223 herstellt. Dabei existiert die PJB schon sehr lange und wurde früher als Extension in PHP eingebunden. Inzwischen ist das nicht mehr notwendig und die Software installiert sich komplett neben einer PHP-Installation. Dazu baut die Brücke einen Kommunikationsserver auf localhost:8080 (oder einem anderen Port) auf, und auf der PHP-Seite werden über ein PHP-include() entsprechende Kommunikationsroutinen bereitgestellt. Die PJB als solche benötigt mindestens Java 1.4 und kann auch ohne Apache oder Tomcat laufen. Die Anwendung von Lucene ist nur eine Einsatzmöglichkeit. Für Windows-Benutzer gibt es einen kleinen “HowTo”-Film auf der Homepage der Entwickler und im Programmpaket haben die Entwickler zwei Beispielprogramme für den Einsatz von Lucene beigefügt.

Die Vorteile dieser Lösung sind, dass die Installation parallel zu einem schon existierenden System vorgenommen werden kann und man Zugriff auf alle von Lucene bereitgestellten Java-Klassen hat. Ferner kann man somit immer das aktuelle Lucene-Release verwenden. Der Nachteil ist, dass man sich doch ein wenig in Java auskennen muss und die Kommunikation über einen Zwischenserver läuft. Als etwas aufwändigere Alternative existiert noch die freie PHP-Java-Brücke der Firma CosmoCode, die aber mehr eine Interprozesskommunikation zwischen Java- und PHP-Objekten darstellt. Hier muss der Benutzer sich selber um die Programmierung und den Aufbau des PHP-Frontend und des Java-Backends kümmern.

YES;
$FI=java('org.apache.lucene.document.Field$Index')->ANALYZED;

$doc = new Java('org.apache.lucene.document.Document');

$field = new Java('org.apache.lucene.document.Field','Autor','Manfred Hardt', $FS, $FI);
$doc->add($field);
$field = new Java('org.apache.lucene.document.Field','Titel','Suchmaschinen entwickeln mit Apache Lucene', $FS, $FI);
$doc->add($field);

$writer->addDocument($doc);


$writer->optimize();
$writer->close();

#---- Abfrage über alle Felder ----

$indexer = new Java('org.apache.lucene.search.IndexSearcher',$index_dir);

# Lese alle indizierten Felder aus dem Index aus
$Felder_ind = java('org.apache.lucene.index.IndexReader$FieldOption')->INDEXED;
$IndexReader = java('org.apache.lucene.index.IndexReader')->open($index_dir);
$AlleSuchfelder=java_values($IndexReader->getFieldNames($Felder_ind)->toArray());
$IndexReader->close();

# Eine Suche über alle im Index vorhandenen Suchfelder
$parser = new Java('org.apache.lucene.queryParser.MultiFieldQueryParser', $AlleSuchfelder,$analyzer);

$query = $parser->parse(‘Apache'); // Sucht nach ‘Titel:Apache’ OR ‘Autor:Apache’

$hits = $indexer->search($query);

$num=java_values($hits->length());

printf("3) Hits: %dn", $num);

for ($i = 0; $i doc($i);
printf("Fount %s n", $i.": ".$found->get('Titel'));
}
?>

In dem Beispiel wird ein Buchdatensatz in den Index aufgenommen und wieder gesucht. Sehr deutlich ist bei der Programmierung die Nähe zu Java zu erkennen.

Zend-Framework – Bereich Lucene-Suche

Das Zend-Framework ist eine von vielen Routinen-Sammlungen im Bereich von PHP, aber der Name “Zend” bürgt hier für eine gute Qualität. Die Entwickler von Zend haben die Routinen vonLucene in einen 100-prozentigen PHP-Code portiert und ganz im Stil von PHP vereinfacht – getreu dem Motto: “Keep it simple”. Wer nicht das gesamte Framework installieren will, kann auch dieLucene- und Exception-Ordner herauskopieren. Beschränken wir den Blick nur auf den Lucene-Teil, so beruht die Lauffähigkeit auf vier weiteren Komponentenabhängigkeiten, die bei Windows entweder in der php.ini als DLL eingebunden werden oder bei Unix-Systemen beim Übersetzen des PHP-Sourcecodes mit einkompiliert werden müssen: Als Abhängigkeiten sind hier bitset, dom, icon und ctype zu nennen. Letzteres ist inzwischen in PHP enthalten. Bitset ist eine Extension, die auch noch zur Laufzeit hinzu geladen werden kann.

Die Installation des eigentlichen Frameworks ist vergleichsweise einfach – man kopiert den Ordner Zend in den Include-Bereich von PHP oder legt zur Laufzeit den Ort fest. Egal wie – wichtig ist, dass PHP die Dateien bei einem include() oder require() findet. Der Vorteil dieses Wegs ist, dass die Lösung komplett in PHP geschrieben ist und somit auch PHP-Cache-Projekte einen Geschwindigkeitsvorteil bringen. Zend stellt eine sehr gute deutschsprachige Übersetzug der Dokumentation zur Verfügung, die auch mit reichlich Programmbeispielen aufwartet, wodurch einem der Einstieg in die Materie sehr leicht fällt. Ferner bietet Zend auch in diesem Bereich Routinen an, um MS-Powerpoint-, Excel- oder Word-Dokumente einzulesen. Als kleiner Nachteil sind die Abhängigkeiten von anderen Programmpaketen und die Tatsache, dass es sich lediglich um eine Portierung des Lucene-Java-Codes handelt, zu nennen.

addField(Zend_Search_Lucene_Field::Text('Autor', 'Manfred Hardt',’ISO-8859-1’));
$doc->addField(Zend_Search_Lucene_Field::Text('Titel', 'Suchmaschinen entwickeln mit Apache Lucene',’ISO-8859-1’));

$index->addDocument($doc);

$index->commit();
$index->optimize();

#---- Abfrage ----

$index = Zend_Search_Lucene::open($index_dir);

try {
$query = Zend_Search_Lucene_Search_QueryParser::parse('Titel:Lucene',’ISO-8859-1’);
} catch (Zend_Search_Lucene_Search_QueryParserException $e) {
echo "Query syntax error: " . $e->getMessage() . "n";
}

$count=0;
$hits = $index->find($query);

echo "Index enthält ".$index->count()." Dokumente.nn";

foreach ($hits as $hit)
{
$document = $hit->getDocument();
printf("Fount %d %s %s n", $count++,$document->getFieldValue('Autor'),$document->getFieldValue('Titel'));
# oder printf("Fount %d %s %s n", $count++,$hit->Autor, $hit->Titel);
}

?>

Das Zend-Beispiel ist deutlich intuitiver und die Programmierung ist auch mehr PHP-like.

[ header = Seite 3: CLucene mit PHP-Extension ]

CLucene mit PHP-Extension

CLucene ist eine Portierung des Lucene-Java-Quellcodes in die Programmiersprache C++, wodurch man einen hochperformanten Programmcode zum Zugriff auf den Index bekommt. Die Anbindung an PHP erfolgt über eine Extension. Im Gegensatz zu den ersten beiden Möglichkeiten ist diese jedoch nur eingeschränkt nutzbar, da nur die SearchIndex- und Hit-Klasse von der Extension zur Verfügung gestellt wird. Man kann also mit der Erweiterung nur einen Index aus PHP heraus abfragen, nicht aber einen aufbauen oder manipulieren. Der als alpha gekennzeichnete Programmcode für die Erweiterung scheint auch nicht mehr gepflegt zu werden, worauf das letzte Änderungsdatum von 2005 hindeutet.

search("mowgli");

$length = $hits->length();

if ($length > 0) {
for ($i = 0; $i get($i, "path"));
var_dump($hits->id($i));
var_dump(round($hits->score($i), 3));
}
} else {
echo "Keine Treffer.";
}

$searcher->close();

?>

Das Beispiel verwendet den von der CLucene-Erweiterung mitgelieferten Datenindex. In dem Beispielcode wird nur gesucht und anschließend werden die Treffer ausgegeben.

Fazit

Alle hier vorgestellten Lösungen kommen mit Vor- und Nachteilen daher – wobei eigentlich nur Lucene mit der PHP-Java-Bücke und das Zend-Framework vollwertige Lösungen sind. Als Empfehlung kann man sagen: Wer nur ein wenig probieren möchte und Java nicht scheut, ist mit der ersten Lösung gut bedient, denn diese kann problemlos zu einer existierenden PHP-Umgebung installiert werden. Wer jedoch eher unerfahren im Umgang mit Java ist und auch tiefer einsteigen möchte, dem sei das Zend-Framework ans Herz gelegt. Egal für welche Lösung man sich entscheidet, die Ergebnisse überzeugen schon bei einfachen Anwendungen und werden beide im einen oder anderen Fall mit Sicherheit die bisher verwendete MySQL-Datenbank ablösen.

 

PHP-Java-Brige

Zend-Framework

CLucene

Programmiersprache

Java

PHP

C++

PHP-Abhängigkeiten

Nein

Iconv, dom, ctype, bitype, (libz)

Nein

Kommunikation

Zwischenserver

Direkt

Extension

Programmstatus

stabil

stabil

alpha

Einbindung als Extension

Nein 1*

Nein

Ja

Java-Kenntnisse nötig

Ja

Nein

Nein

Lucene JAR-Datei nötig

Ja

Nein

Nein

Indexaufbau

Ja

Ja

Nein

Indexabfrage

Ja

Ja

Ja

Inhaltserschließung aus MS-Dokumenten

(Ja) 2*

Ja

-

Analyser für deutsche Sprache

(Ja) 3*

(Ja) 4*

-

Beispielcode

Ja

Ja

Ja

Geschwindigkeit

+

o

+++

Voraussetzungen – jeweils mit “oder besser”

PHP 5.1.2

Java 1.4

PHP 5.2.4

PHP 5.0

C++-Compiler

Tabelle 3: Vergleich der drei Lösungen PHP-Java-Bridge, Zend Framework und CLucene

Kommentare

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *


+ sechs = 7

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>