Social Software powered by Instant Communities
Springe direkt: zur Navigationzum Inhaltzur Sidebar
Jul27

Zend_Search_Lucene und Symfony – Teil 2

gepostet von Johannes Schmidt

Die Daten, die in unserem Anwendungsfall indexiert werden, sind Daten, die für grundsätzliche Zwecke innerhalb der Applikation in einer Datenbank gehalten werden, z. B. der Vor- und Nachname eines Nutzers.

Das inkrementelle Aufbauen bzw. Updating des Indexes hat den Vorteil, dass die Datenbank und der Index nicht durch umfangreiche Reindexing-Prozesse zu bestimmten Zeiten zwecks Aktualisierung belastet werden. Zu indexierende Daten werden, wie im ersten Teil erwähnt, in einer Queue, eine Tabelle in der Datenbank, geparkt und von einem Batch-Script in regelmäßigen kurzen Abständen weiterverarbeitet. Somit sind lediglich kleine Datenmengen zu lesen und zu schreiben. Der Prozess ist also folgender:

  1. Daten in die Queue schreiben.
  2. Queue auslesen.
  3. Lucene document mit ausgelesenen Daten erstellen.
  4. Anhand einer definierten ID prüfen, ob document für ausgelesene Daten bereits existiert.
  5. Document ggf. aus dem Index entfernen (bzw. als deletable markieren).
  6. Document dem Index hinzufügen.
  7. Ausgelesene Daten aus der Queue entfernen.

Zusätzlich wird der Index in regelmäßigen Intervallen optimiert (siehe auch Teil 1).

Die Queue wird mit einer Tabelle in der Datenbank realisiert. Die Daten in der Queue bestehen lediglich aus serialisierten Objekten; deren Attribute werden letztendlich indexiert. Die Tabelle ist entsprechend simpel und bietet durch die Serialisierung dennoch absolute Flexibilität bzgl. irgendwelcher Daten:

CREATE TABLE `index_user_queue` (
`id` bigint(20) NOT NULL auto_increment,
`serialized` text collate utf8_unicode_ci,
PRIMARY KEY (`id`)
)

Da Symfony einen OR-Mapper benutzt, wir haben statt Propel jedoch Doctrine gewählt, steht für obige Tabelle im model eine entsprechende Klasse zur Verfügung: class IndexUserQueue extends BaseIndexUserQueue{}. Hier ist lediglich eine Methode platziert:

public static function addUserQueue($user)
{
$dataStr = serialize($user);
$q = new IndexUserQueue();
$q->serialized = $dataStr;
$q->save();
}

Sollen Daten indexiert bzw. im Index aktualisiert werden, wird, wie in diesem Fall, einfach das zu Grunde liegende User-Objekt an addUserQueue übergeben, z. B. in einer action: IndexUserQueue::addUserQueue($user).

Die Queue wird von einem Batch-Script abgearbeitet. Mit Symfony ist es sehr einfach, PHP-Scripte im gewünschten Kontext und in der gewünschten Applikation via Kommandozeile oder Cronjob zu rufen. Nachschauen kann der Leser dazu z. B. in der Dokumentation auf der Symfony-Seite oder bei Oxyscripts im Manual zu Symfony.

Der Ordner batch wird per default auf der Root-Ebene eines Symfony-Projektes mit angelegt. In einem Script writeUserIndex.php, welches obige Queue abarbeiten soll, sind lediglich folgende Zeilen definiert:

$q = new IndexUserQueue();
$users = $q->getTable()->findAll(); //alle Einträge aus der Queue auslesen
foreach($users AS $user)
{
$userdata = $user->serialized; //serialisiertes User-Objekt
$sUser = unserialize($userdata); //User-Objekt wieder erstellen
t8dindex::updateUserIndexDocument($sUser); //indexieren oder ggf. Dok. aktualisieren
$user->delete(); //Eintrag aus der Queue löschen
}

Der Aufruf dieses PHP-Scriptes wird in einem Shell-Script festgelegt (sudo -u www-data php writeUserIndex.php), das per Cron-Job minütlich aufgerufen wird.

Analog zu diesem Procedere wird der Index regelmäßig in weniger kurzen Abständen in gleicher Vorgehensweise optimiert. So kann der gesamte Prozess des Indexierens jenseits des Frontends automatisiert im Hintergrund ablaufen, wobei die Belastung der Datenbank und des Indexes minimal gehalten wird. Zusätzlich erfolgt die Initialisierung des Schreibzugriff auf den jeweiligen Index, im Beispiel für den User-Index, von einem Ort in der Applikation aus, was Kontrolle und Debugging erleichtert.

Die beschriebene Implementierung einer Indexierungsstrategie (auf Prozessebene) zeigt die weiterreichenden Möglichkeiten von Symfony. Verblüffend geradezu ist die geringe Komplexität, die hier bewältigt werden musste. Ich hoffe, dass die gezeigten (Code)Beispiele dies verdeutlichen können.

Die Möglichkeit, Komponenten des Zend Frameworks auf einfache Weise integrieren zu können, ist ein kluger Schachzug der Symfony-Entwickler. Neben dem in Teil 1 erwähnten äußerst simplen Weg der Integration von Zend_Search_Lucene bietet Symfony für Zend allgemein die Zend Framework Bridge an. Somit können sich die Symfony-Entwickler auf ihre Kernkompetenzen konzentrieren, MVC Framework, während die Symfony-Anwender gleichsam notwendige Komponenten bzw. Features erhalten können, die Symfony nicht bereitstellt wie in diesem Fall Zend_Search_Lucene. Da Zend dadurch höhere Downloadzahlen verzeichnen kann, was sich bspw. in zahlreicheren Anwenderfeedbacks auszahlen wird, lässt sich doch durchaus eine allgemeine Win-Win-Situation feststellen.

So ist abschließend der Sammlung der Pro-Argumente von Symfony von Andreas Stephan als ein weiteres die einfache Integration eines state of the art Suchindexes hinzuzufügen.

p.s. Auch im Web 2.0 werden die Nutzer klassische Suchfelder bzw. Suchen benutzen…

2 Antworten

Kommentare zu diesem Beitrag als RSS 2.0 feed.

  • Am October 24th, 2007 schrieb Jirka Schaefer

    Hi, wir setzen Symfony für grosse Produktivsysteme ein und hatten versucht Zend Lucene einzusetzen. Der Entwickler von Zend hat uns dabei ganz wunderbar unterstützt. Allerdings krankt das ganze an PHP und ist alles andere als performant (für grosse Datenmengen). Sphinx ist da eine weit bessere Empfehlung.

  • Am October 24th, 2007 schrieb Johannes Schmidt

    Hallo Jirka,
    danke für den Tipp. Wir sind noch nicht bei großen Datenmengen angekommen, werde mir Sphinx jedoch schon einmal anschauen.

Kommentar abgeben

Folgendes HTML ist erlaubt: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

* Pflichtfelder

Creative Commons License
This work is licensed under a
Creative Commons Attribution-Share Alike 2.5 License.
t8d blogged mit WordPress