Sphinx und SphinxSE ins Zend Framework integrieren

Nachdem Sphinx und SphinxSE installiert ist, geht es diesmal um die Integration in ein Zend Framework-Projekt. Dazu gehört die Erstellung einer Konfigurationsdatei und ein Kommandozeilen-Skript zum Starten und Stoppen des Sphinx-Suchdämons und zum Generieren des Suchindexes.

Den Code zum Artikel gibts auf github.

Das Zend Framework ist nicht enthalten und muss noch nachinstalliert werden. Entweder man lädt sich die aktuelle Version herunter und kopiert oder linkt den Zend-Ordner ins library-Verzeichnis oder man installiert es per PEAR. Den (inoffiziellen) PEAR Channel gibts unter http://pear.zfcampus.org.

Die Konfigurationsdatei

Die Konfigurationsdatei von Sphinx ist die Schaltzentrale und enthält alle Optionen für den Dämon, den Indexer, die Indizes und deren Datenquellen. Für unser Beispielprojekt nutzen wir eine einfache Variante mit Grundeinstellungen. Der Clou ist, dass die Konfigurationsdateien von jeder beliebigen Skriptsprachen verarbeitet werden kann. Einfach am Anfang der Datei den entsprechenden Shebang setzen.

Die Datei liegt in application/configs/sphinx.conf:

#!/usr/bin/env php
<?php
 
// Define path to application directory
defined('APPLICATION_PATH')
    || define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/..'));
 
// Define application environment
//
// Use `export APPLICATION_ENV=development` or edit /etc/environment
// to change the application environment
defined('APPLICATION_ENV')
    || define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'production'));
 
// Ensure library/ is on include_path
set_include_path(implode(PATH_SEPARATOR, array(
    realpath(APPLICATION_PATH . '/../../library'),
    get_include_path(),
)));
 
/** Zend_Application */
require_once 'Zend/Application.php';
 
// Create application, bootstrap, and run
$application = new Zend_Application(
    APPLICATION_ENV,
    APPLICATION_PATH . '/configs/application.ini'
);
$config = $application->getOptions();
?>
source posts
{
    type                    = mysql
 
    sql_host                = <?php echo $config['resources']['db']['params']['host'] . "\n"; ?>
    sql_user                = <?php echo $config['resources']['db']['params']['username'] . "\n"; ?>
    sql_pass                = <?php echo $config['resources']['db']['params']['password'] . "\n"; ?>
    sql_db                  = <?php echo $config['resources']['db']['params']['dbname'] . "\n"; ?>
    # sql_port               = 3306    # optional, default is 3306
 
    sql_query_pre           = SET NAMES utf8
 
    sql_query               = \
        SELECT posts.id, posts.category_id, posts.title, posts.content, UNIX_TIMESTAMP(posts.created) AS created, \
               categories.name AS category_name, \
               GROUP_CONCAT(tags.tag SEPARATOR ', ') AS tags \
          FROM posts \
          JOIN categories ON (categories.id = posts.category_id) \
     LEFT JOIN posts_tags ON (posts_tags.post_id = posts.id) \
     LEFT JOIN tags ON (tags.id = posts_tags.tag_id) \
      GROUP BY posts.id
 
    sql_attr_uint           = category_id
    sql_attr_timestamp      = created
 
    sql_query_info          = \
        SELECT posts.id, posts.category_id, posts.title, posts.content, posts.created, \
               categories.name AS category_name, \
               GROUP_CONCAT(tags.tag SEPARATOR ', ') AS tags \
          FROM posts \
          JOIN categories ON (categories.id = posts.category_id) \
     LEFT JOIN posts_tags ON (posts_tags.post_id = posts.id) \
     LEFT JOIN tags ON (tags.id = posts_tags.tag_id) \
         WHERE posts.id=$id
}
 
index posts
{
    source                  = posts
    path                    = <?php echo $config['sphinx']['index']['path']; ?>/posts
    docinfo                 = extern
    html_strip              = 1
    charset_type            = utf-8
    # Character table for german (from http://www.sphinxsearch.com/forum/view.html?id=19)
    charset_table           = 0..9, A..Z->a..z, _, a..z, U+C4->U+E4, U+D6->U+F6, U+DC->U+FC, U+DF, U+E4, U+F6, U+FC
}
 
indexer
{
    mem_limit               = 32M
}
 
searchd
{
    listen                  = <?php echo $config['sphinx']['searchd']['listen'] . "\n"; ?>
    log                     = <?php echo $config['sphinx']['searchd']['log'] . "\n"; ?>
    query_log               = <?php echo $config['sphinx']['searchd']['query_log'] . "\n"; ?>
    read_timeout            = 5
    max_children            = 30
    pid_file                = <?php echo $config['sphinx']['searchd']['pid_file'] . "\n"; ?>
    max_matches             = 1000
    seamless_rotate         = 1
    preopen_indexes         = 0
    unlink_old              = 1
}

Was die einzelnen Optionen bedeuten, kann man in der Dokumentation gut nachlesen. Ich will hier nur auf paar Besonderheiten eingehen:

Das Kommandozeilen-Skript für Sphinx

Um den Suchindex zu erstellen und den Suchdämon zu starten und zu stoppen, enthält das Projekt das Kommandozeilen-Skript in scripts/sphinx. Der Aufruf erfolgt so:

$ ./scripts/sphinx COMMAND ENVIRONMENT

COMMAND

Folgende Kommandos sind möglich:
start Startet den Suchdämon.
stop Stoppt den Suchdämon.
restart Startet den Suchdämon neu.
index-all Erstellt alle in application/configs/sphinx.conf definierten Suchindizes.

ENVIRONMENT

Gibt APPLICATION_ENV an, entspricht also define('APPLICATION_ENV', 'production'); in z. B. der index.php. Somit wird der entsprechende Abschnitt in application/configs/application.ini genutzt. Wenn nicht angegeben, wird standardmäßig production verwendet.

Beispiele

Startet den Suchdämon mit APPLICATION_ENV=production:

$ ./scripts/sphinx start production

Erstellt alle Suchindizes mit APPLICATION_ENV=development:

$ ./scripts/sphinx index-all development

Somit haben wir nun unser Zend Framework-Projekt präpariert um mit Sphinx zu arbeiten. Auf den Suchindex lässt sich nun ganz einfach, z. B. per Zend_Db_Select, zugreifen:

$query = 'Mein Suchbegriff';
$select = $db->select();
$select->from('posts')
       ->joinInner('sphinx', 'sphinx.id = posts.id')
       ->where('sphinx.query = ?', $query . ';mode=any;sort=extended:@weight desc, @id asc;limit=10;offset=0;index=posts');
$result = $select->query()->fetchAll();

Links zum Thema