Vorwort: wieso ein Blog zu PHP, Solr und Lucene?

Wieso ein Blog zu PHP, Solr und Lucene?
Gegenstand und Ausgangspunkt all unserer Aktivitäten auf diesem Gebiet war ein Projekt um ein Nachrichtenportal und die Aufgabe, Recherchen und Analysen im Nachrichtenbestand von über 10 Million News performant zu handeln. Die MySQL Volltextsuche kam da schnell an Ihre grenzen, Oracle war keine Alternative.
Es reifte also die Frage, wie können andere (etwa die Internetsuchmaschiene google) immense Datenmengen spielend handeln?
Wir lösten den MySQL volltext mit Lucene ab. Der Performancegewinn war dramatisch. Suchen im Datenbestand, die vorher über 10 Sekunden dauerten, brauchen mittels Lucene und Solr nur selten mehr als 20ms!
Eine neue Welt tat sich auf, die es zu erobern galt und schnell fiel auf, dass deutschsprachige Seiten zum Thema Mangelware sind. Dies soll sich mit diesem Blog ein wenig ändern.

Sie haben Fragen zu Solr/Lucene/PHP? Schreiben sie uns einen Kommentar!

Dienstag, 26. November 2013

Solr mongoDB import

Situation

Manchmal ändern sich Situationen und Aufgaben. Im aktuellen Fall geht es darum, mittels Solr die Daten einer mongoDB zu indexieren. Also Solr für die Suche/Volltextsuche in mongoDB zu nutzen. Was wir brauchen ist der Data Import Handler (DIH) für mongoDB, den es so nicht gibt. ut of the Box versteht der DIH nur klassische DB Verbindungen, wie etwa zu MySQL.

Erfreulicher Weise ist 10gen, als Entwickler der Mongo DB, daran interessiert, dass sich deren Datenbank etabliert und versorgt die Welt mit diversen Konnectoren, Tools, etc.
Um die Daten der mongoDB in Solr zu indexieren, kann man sich des sogenannten "mongo Connector" bedienen. Ein Python Paket, entwickelt von Praktikanten bei 10gen....

 

Installation Mongo Connector

Diese Python-Skripte müssen zunächst installiert werden. Laut Doku geht dies mittels 'pip install mongo-connector'
Wer pip nicht nutzt, oder -wie ich- einen Server hat, der nicht mit dem Internet verbunden ist, hat einen weitaus steinigeren Weg.
Zunächst muss neben python auch das Paket 'python-setuptools' installiert, sowie folgende Python-Erweiterungen: urllib3 1.6, requests 2.0.1, pymongo 2.6.3, pyes 0.20.1, pysolr 3.1.0, simplejson 3.3.1. - jewiels bezogen auf den  Mongo Connector 1.1.1.

Diese Erweiterungen müssen vorher aus dem Internet heruntergeladen werden. Jedes dieser (G)Zip Archive beinhaltet einen Ordner, in dem sich eine 'setup.py' befindet. In diesem Ordner stehend muss dann das Paket mittels 'python setup.py install' installiert werden.
Anschließend den Mongo Connector (1.1.1) runter laden, auf dem Zielhost entpacken, in das Verzeichnis wechseln und ebenfalls mittels 'python setup.py install' installieren.

Zum Schluss steht ein ausführbares Skript namens 'mongo-connector' bereit.

 

Funktionsweise  Mongo Connector

Diese Connector funktioniert nun so, dass er sich gleichermaßen mit der mongoDB und einem Solr Core verbindet und die Daten aus der mongo Datenbank nach Solr "dumpt".

Solr vorbereiten


Dazu habe ich vorher einen neuen Solr Core angelegt, in dem ich ganz trivial die Verzeichnisse eines bestehenden Cores Kopiert und dann das Verzeichnis 'data' geleert  habe.
Danach habe ich die 'solr-core/conf/schema.xml' angepasst und Felder analog zum MongoDB Schema angelegt. Auch wenn es ein Oxymoron ist.
Zum Schluss noch in der solr.xml den neuen Core eintragen und solr neu starten.

Solr ist soweit also bereit zur Aufnahme der mongoDB Daten.

Mongo Connector  nutzen


Der Mongo Connector arbeitet so, dass er sich mit dem oplog der mongoDB verbindet, diese durcharbeitet und die Daten über einen sogenannten Doc Manager ausgibt. Der Doc Manager ist in unserem Fall ein speziell für Solr geschriebenes Modul des Mongo Connector.

Bei Start des Mongo Connectors werden nun folgende Parameter mit angegeben:
-m Adresse, unter der die mongoDB läuft
-t für den Solr Zielhost
-n für die zu indexierende mongoDB und Collection
-d für den Doc Manager, der die Daten für das Zielsystem (Solr) verarbeitet.

Wichtig: in der hier genutzten mongo Connector Version 1.1.1 gibt es einen Bug. Solr Umgebungen mit mehreren Cores werden nicht unterstützt. Dies stellt sich so dar, dass die Validierung des Zielsystems, in dem Fall der Solr URL, fehlschlägt. Es kommt folgende Meldung:
CRITICAL - MongoConnector: Bad target system URL!
Exception in thread Thread-1:
Traceback (most recent call last):
  File "/usr/lib64/python2.6/threading.py", line 522, in __bootstrap_inner
    self.run()
  File "build/bdist.linux-x86_64/egg/mongo_connector/connector.py", line 227, in run
    False, self.doc_manager,
AttributeError: 'Connector' object has no attribute 'doc_manager'

Der Workaround sieht so aus, dass man in der Datei '/mongo_connector/doc_managers/solr_doc_manager.py' die Validierung deaktiviert, in dem man folgende Zielen mittels '#' auskommentiert:
       # if verify_url(url) is False:
         #   raise SystemError

Nun kann der import auch mit einem Multi-Core Solr System realisiert werden. Ein beispielhafter Aufruf sieht dabei so aus aus:
# mongo-connector -m localhost:27017 -t http://localhost:8080/solr/mongo -n myMongoDb.myCollection -d ./mongo_connector/doc_managers/solr_doc_manager.py

Erstimport & inkrementeller Import

 Der MongoDB Connector legt eine Datei namens 'config.txt' an. Darin wird die oplog-Position vermerkt, bis zu welcher Daten bereits nach Solr eingeflossen sind. Startet man den mongo Connector zu einem späteren Zeitpunkt neu, werden nur die Daten in Solr indexiert, die seit dem letzten lauf das mongo Connectors hinzugekommen sind. Wünscht man einen initialen Neuimport, so muss man die 'config.txt' löschen und dann den connector neu starten.

Probleme

Beim mongo Connector gibt es folgende Nachteile zu beachten:
  1. Performance
    Jedes Dokument wird einzeln an Solr übergeben und mittels Commit bestätigt. Das ist sehr träge, gerade bei größeren Datenbanken. In einer Testumgebung werden pro Stunden 120.000 Dokumente nach Solr übergeben. Bei einer Datenbank mit bspw. 10 Millionen Dokumenten ist das System über 3 Tage mit dem import beschäftigt. Der klassische Weg über MySQL und Solr DIH (Data Import Handler) benötigt für die gleichen Daten 3 Stunden.
  2. oplog als Basis
    Das oplog ist bei mongoDB eine capped Collection. Das heißt, es ist ist davon auszugehen, dass in der DB selber mehr Daten sind, als im oplog. Ein Neuaufbau führt dann dazu, dass nicht alle Dokumente in Solr ankommen.
  3. Buggy
    Die Software wurde, vermutlich, mit einem kleinen, konkreten Fokus entwickelt und in die freie Wildbahn entlassen.Anschließend wurden verschiedene Balkone angebaut. In der aktuellen Version gibt es einige Probleme. Bspw. lässt sich das Programm nicht beenden. Nach dem initialen Import werden werden Änderungen in der Mongo-DB nicht Solr repliziert - hier ist ein Neustart erforderlich, etc.
Alles in allem arbeitet der mongo Connector ganz gut, ist aber aus meiner Sicht keine Option für den produktiven Einsatz.

Nachtrag: Wir haben nun einen eigenen Importer entwickelt, über den ich hier berichte: den mongoSolrImporter

Freitag, 21. Dezember 2012

PDF, Word, Text, HTML Dokumente im Solr / Lucene Index

Hin und wieder erreicht uns ein Sonderprojekt. So auch in den letzten Wochen. Die Anforderung war eine Dokumentensuchmaschine aufzubauen, mit der vorhandene Dokumente,  allen voran Adobe Acrobat PDF und Word Dateien, schnell und elegant durchsucht werden können. Inklusive Platzhaltersuche, unscharfe Suche, gewichtete Suche, etc.
Mit dabei: einige tausenden Bücher: Acrobat PDF Dokumente mit 300-700 Seiten und bis zu 3 GB groß.
Zum Einsatz kam Lucene / Solr 4.0, welches seit Herbst 2012 als finale Version (nicht mehr Beta) vorliegt.
Für die Indexierung ganzer Dokumente, kann man bei Solr auf Tika zurückgreifen. Dies ist bereits in der Solr 3.x Version verfügbar.
In der Solr 4.0 Beispielkonfiguration ist darüber hinaus bereits der extracting request handler vorhanden.

Konfiguration Solr zur Indexierung von Dokumenten

 
 <requestHandler name="/update/extract" 
                  startup="lazy"
                  class="solr.extraction.ExtractingRequestHandler" >
    <lst name="defaults">
      <str name="lowernames">true</str>
      <str name="uprefix">ignored_</str>

      <!-- capture link hrefs but ignore div attributes -->
      <str name="captureAttr">true</str>
      <str name="fmap.a">links</str>
      <str name="fmap.div">ignored_</str>
    </lst>
  </requestHandler>
 
Dieser extracting request handler nutzt im Hintergrund Tika, um die an ihn übergebenen Dokumente zu analysieren. Dabei ist es egal, ob wir PDF Dokumente, Word (doc), Textdateien, Webseiten, also HTML Dokumente, u.s.w. übergeben. Das erleichtert die Arbeit erheblich, denn es müssen vorher die Dateien nicht mühsam in den einheitliches Format umgewandelt werden.
Tika, hinter dem  exreacting request handler, analysiert darüber hinaus die Metadaten der Dokumente, bspw. die Metainformationen eines PDF Dokumentes (Autor, Datum, etc) oder einer HTML Datei.
Damit dies alles funktioniert, müssen in der Schema.xml entsprechende Felder vorhanden sein. Auch hier macht es uns Solr recht einfach, denn die Klassiker wie Author und co sind bereits vorhanden.
   <field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false" />
   <field name="title" type="text_general" indexed="true" stored="true" multiValued="true"/>
   <field name="subject" type="text_general" indexed="true" stored="true"/>
   <field name="description" type="text_general" indexed="true" stored="true"/>
   <field name="comments" type="text_general" indexed="true" stored="true"/>
   <field name="author" type="text_general" indexed="true" stored="true"/>
   <field name="keywords" type="text_general" indexed="true" stored="true"/>
   <field name="category" type="text_general" indexed="true" stored="true"/>
   <field name="resourcename" type="text_general" indexed="true" stored="true"/>
   <field name="url" type="text_general" indexed="true" stored="true"/>
   <field name="content_type" type="string" indexed="true" stored="true" multiValued="true"/>
   <field name="links" type="string" indexed="true" stored="true" multiValued="true"/>
   <field name="content" type="text_de" indexed="true" stored="true" multiValued="true"/>

Das passt vor allem deshalb so gut, da klassisch erstellte PDF's gleiche Bezeichnungen bei den Metainformationen haben. Also auch dort gibt es ein Feld author. Zusätzlich möchten wir noch weitere Informationen verarbeiten. Bspw. das Erstellungsdatum. Dazu fügen wir ein eigenes Feld hinzu:
<field name="datum" type="tdate" indexed="true" stored="true" multiValued="false" /> 


Curl zur Indexierung von Dokumenten mit Sol / Lucene

Interessant wird's nun mit der Frage, wie man die Dokumente indexiert, also nach Solr / Lucene importiert.
Das ist abhängig von der Anwendung und den persönlichen Vorlieben und Fähigkeiten.
In unserem Fall nutzen wir CURL. Das geht aus der Linux Konsole heraus und auch aus PHP Skripten.
Wichtig ist dabei die Syntax. Damit definieren wir die Datei, die analysiert und indexiert werden soll sowie zusätzliche Felder, die wir für jede Datei noch speichern/verarbeiten wollen (bspw. datum und id)
 curl "http://localhost:8080/solr/collection1/update/extract/
                        ?literal.id=AH3DB2
                        &literal.datum=2012-01-01T09:22:38.00Z"
                        -data-binary "@/pfad/zu/meinemDokuemnt.pdf" 
                        -H "Content-type:application/pdf" 

Was passier hier?
Mit literal.id=AH3DB2 setzen wir die unique ID (Feld "id" in der schema.xml) des Dokumentes. Dieser Wert ist optional als CURL Parameter. Alternativ hätte Solr bzw. Tika einen Wert gesetzt, in aller Regel den Dateinamen. Mit dem literal. wird markiert, dass es sich hierbei nachfolgend um eine Feldbezeichnung handelt.
Mit literal.datum="2012-01-01T09:22:38.00Z" setzen wir unser zusätzliches Feld datum. Abschließend definieren wir die Datei, die zu solr gesendet werden soll: -data-binary "@/pfad/zu/meinDokuemnt.pdf" und definieren abschließend den mime-typ des Dokumentes.
Zum Schluss noch ein commit, damit die Daten im Index auch sichtbar sind. Auch das erreichen wir mittels curl
curl "http://localhost:8080/solr/collection1/update/extract?softCommit=true"
Fertig

Freitag, 2. November 2012

SolrCloud einrichten / Migration zu SolrCloud

Ergänzend zu diesem Artikel SolrCloud Grundlagen möchte ich nun darauf eingehen, wie wir unsere SolrCloud eingerichtet haben. Betroffen ist unser Solr Archiv Index: 40 GB historische Daten in einigen Millionen Dokumenten, welcher doch recht träge ist. Diesen Solr Index migrieren wir nun zur SolrCloud, wobei wir erstmal mit nur einem Host anfangen. Auf diesem Host laufen dann aber später 3 Shards, also 3 Scheiben eines Solr-Index. Dies alleine bringt einen Geschwindigkeitsgewinn, da der Solr Index nun über 3 CPU kerne skalieren kann, statt wie bisher auf einem Kern fest hängt.

Freitag, 28. September 2012

Realtime Suche vs. Performance

Unser Datenbestand unterliegt dauernden Änderungen. In der Ausprägung, dass täglich mehrere tausend Dokumente hinzu kommen. Das ist für Solr per se kein Problem. Unser Ziel ist jedoch, dass neue Dokumente quasi zeitgleich auch in Solr gefunden werden. Das heißt: permanentes Hinzufügen neuer Dokumente. Bei uns passiert das als Dienst alle 10 Sekunden.
Alle 10 Sekunden führen wir ein Dataimport durch, mit dem Parameter commit=true (oder autocommit) und optimize=false,  Commit ist wichtig, damit die Daten auffindbar sind. Optimize ist wichtig, damit die Suche performant ist. Und genau hier hängt das Problem.

Montag, 24. September 2012

SolrCloud Grundlagen

Mit wachsendem Datenbestand und mit der wachsenden Verbreitung einer Anwendung werden 2 Faktoren immer wichtiger: Performance und Ausfallsicherheit. Solr bot dafür bisher (und noch immer) Solr Distributed Search und Solr Replication. Nimmt man beide Features zusammen und packt noch etwas Konfigurationsmanagment hinzu, erhält man in etwa das, was Solr Cloud ausmacht.
Früher hätte man es einfach Solr Cluster genannt, heute heißt es dann natürlich Cloud. SolrCloud!
Vorab ein paar Einschränkungen / wichtige Punkte / Probleme zu Solr Cloud:

Solr Probleme mit Umlauten

Während Lucene eine Java Bibliothek ist, ist Solr die Java Anwendung, die diese Bibliothek nutzt. Wie für jede Java Anwendung wird auch für Solr eine Laufzeitumgebung benötigt.
Idealer Weise gibt es seit einigen Versionen von Solr beim offiziellen Download eine All-in-one Variante.
Darin werden geliefert: sämtliche Bibliothken, die man für besondere Solr Funktionen (Clustering, Dataimport Handler, Velocity, ...) benötigt, die Solr Anwendung selber und Jetty. Jetty ist ein kleiner Java Anwendungsserver, in dessen man die Solr Anwendung deployen, also installieren & laufen lassen kann.
Das funktioniert in aller Regel super...
...so lange man nicht versucht, deutsche Umlaute, also äöü und ß zu verarbeiten.

Freitag, 14. September 2012

mehere Solr Suchen / Abfragen in einem Request / URL Aufruf

Bei der Arbeit mit Solr /Lucene ergibt sich oft die Anforderung, dass für mehrere Suchbegriffe eine Solr Abfrage ausgelöst werden muss. Oftmals ändern sich dabei noch nicht einmal die FilterQuery Parameter, sondern lediglich der Suchbegriff. Jede Suchanfrage beginnt dabei mit dem Verbindungsaufbei vom Client / Applikation zum Solr Server. Es läppern sich Latzenzzeiten und Overhead. Die Frage ist also: Kann man in einem einzelnen Solr Request mehrere Suchen verarbeiten / durchführen?
Ja.