Игорь Чакрыгин Игорь Чакрыгин

У любой задачи существует по крайней мере одно очевидное и невероятно простое для понимания неправильное решение

Sphinx - Источники данных в формате xml

В предыдущих статьях я рассказал о том, что в качестве источника данных для Sphinx можно использовать базы данных, в частности MS SQL Server. Во многих случаях этого наиболее удобный способ построения индекса, поскольку Sphinx может получить все данные одним запросом. Даже на очень больших объёмах данных этот запрос можно разбить на несколько ranged-запросов и получать данные из базы небольшими порциями.

Но данные в базе могут иметь сложную структуру со множеством связей, и получить их одним запросом может быть сложно. К тому же может потребоваться получать данные из источника, который Sphinx просто не поддерживает. Наиболее универсальным источником данных для Sphinx являются данные в формате xml. Именно об этом я и хочу рассказать в этой статье.

Пример 1: Индексация xml-файла

Для начала рассмотрим самый простой пример, когда индексируемые данные находятся в xml-файле, а схема индекса описывается в файле конфигурации.

Шаг 1. Создадим файл product.xml (я поместил его в папку c:\sphinx\data\source\) с документами, которые собираемся индексировать.

<?xml version="1.0" encoding="utf-8"?>
<sphinx:docset xmlns:sphinx="http://sphinxsearch.com/">

  <sphinx:document id="849">
    <name>Men's Sports Shorts</name>
    <price>24.7459</price>
  </sphinx:document>
  
  <sphinx:document id="852">
    <name>Women's Tights</name>
    <price>30.9334</price>
  </sphinx:document>
  
  <sphinx:document id="855">
    <name>Men's Bib-Shorts</name>
    <price>37.1209</price>
  </sphinx:document>
  
</sphinx:docset>

Как вы видите, корневым элементом xml-файла является элемент sphinx:docset, в котором при помощи элементов sphinx:document описаны индексируемые документы. Каждый документ имеет уникальный атрибут id и несколько вложенных элементов, названия которых соответствуют названиям полей и атрибутов в схеме индекса.

Шаг 2. Опишем источник данных и индекс в файле конфигурации.

searchd
{
    listen      = 9306:mysql41
    pid_file    = c:/sphinx/data/searchd.pid
    log         = c:/sphinx/data/log/log.txt
    query_log   = c:/sphinx/data/log/query_log.txt
    binlog_path = c:/sphinx/data/binlog/
}

source product
{
    type                 = xmlpipe2
    xmlpipe_command      = type c:\sphinx\data\source\product.xml
    xmlpipe_field_string = name
    xmlpipe_attr_float   = price
}

index product 
{
    source       = product 
    path         = c:/sphinx/data/index/product
    charset_type = utf-8
}

Блоки searchd и index product вряд ли нуждаются в пояснении. Они полностью аналогичны тому, что мы делали при индексации базы данных. Основной интерес представляет блок source product.

Опция type указывает тип источника данных. Sphinx поддерживает два типа источников данных в формате xml: xmlpipe и xmlpipe2. Однако тип xmlpipe считается уже устаревшим поэтому мы использует тип xmlpipe2.

Опция xmlpipe_command указывает команду для получения данных. Эта команда должна выводить xml-документ в стандартный выходной поток. Мы используем команду type, которая выводит содержимое файла, название которого передано ей в качестве аргумента. Убедиться в этом можно, если ввести эту команду в командную строку.

type c:\sphinx\data\source\product.xml

Опций xmlpipe_field_string и xmlpipe_attr_float определяют поле name и атрибуты name и price, которые будут содержаться в индексе. Полный список всех опций для определения полей и атрибутов можно найти в документации.

Шаг 3. Выполним индексацию.

c:\sphinx\bin\indexer product --config c:\sphinx\data\config.txt --rotate

После того, как индексация успешно завершится, можно проверить, что индекс действительно был создан, подключившись к Sphinx и выполнив простой запрос.

select id, name, price from product;

Пример 2: Описание схемы в индексируемом xml-файле

Отличительной особенностью источников данных в формате xml является то, что схема индекса (набор полей и атрибутов) может быть описана не только в файле конфигурации, но и в самих данных. Это весьма полезная возможность, поскольку она позволяет изменять схему индекса без изменения файла конфигурации и последующего перезапуска Sphinx.

Шаг 1. Добавим схему индекса в файл product.xml

<?xml version="1.0" encoding="utf-8"?>
<sphinx:docset xmlns:sphinx="http://sphinxsearch.com/">

  <sphinx:schema>
    <sphinx:field name="name" attr="string" />
    <sphinx:attr name="price" type="float"/>
  </sphinx:schema>
  
  <sphinx:document id="849">
    <name>Men's Sports Shorts</name>
    <price>24.7459</price>
  </sphinx:document>
  
  <sphinx:document id="852">
    <name>Women's Tights</name>
    <price>30.9334</price>
  </sphinx:document>
  
  <sphinx:document id="855">
    <name>Men's Bib-Shorts</name>
    <price>37.1209</price>
  </sphinx:document>
  
</sphinx:docset>

Схема индекса описывается при помощи элемента sphinx:schema, который может содержать элементы sphinx:field и sphinx:attr.

Элемент sphinx:field определяет поле с названием, указанным в атрибуте name. Также в этом элементе может содержаться необязательный атрибут attr, который указывает, что вместе с полем нужно создать одноимённый атрибут. Значение атрибута attr при этом указывает тип создаваемого атрибута (обычно это строковой тип).

Элемент sphinx:attr определяет атрибут с названием, указанным в атрибуте name и типом, указанным в атрибуте type.

Более подробно про элементы sphinx:field и sphinx:attr можно прочитать в документации.

Шаг 2. Изменим файл конфигурации, убрав определение полей и атрибутов из блока source.

source product
{
    type            = xmlpipe2
    xmlpipe_command = type c:\sphinx\data\source\product.xml
}

Тут всё очень просто. Поскольку схема теперь описана в xml-файле, то никакой необходимости дублировать её в файле конфигурации.

Шаг 3. Снова выполним индексацию командой:

c:\sphinx\bin\indexer product --config c:\sphinx\data\config.txt --rotate

Как мы видим, индексация снова прошла успешно.

Пример 3: Индексация потоковых данных

В предыдущих примерах мы индексировали готовые xml-файлы, используя для этого команду type. Однако создавать этот файл не обязательно. Вместо команды type можно указать любую программу, которая будет выводить данные в стандартный выходной поток.

Шаг 1. Воспользуемся Visual Studio и создадим консольное приложение SphinxSourceApp на языке C#, которое будет выводить данные из предыдущего примера при помощи класса XmlTextWriter.

public static class Program
{
    private const string sphinxns = "http://sphinxsearch.com";

    public static void Main(string[] args)
    {
        var products = new[]
        {
            new { ID = 849, Name = "Men's Sports Shorts", Price = 24.7459M },
            new { ID = 852, Name = "Women's Tights", Price = 30.9334M },
            new { ID = 855, Name = "Men's Bib-Shorts", Price = 37.1209M },
        };

        Console.OutputEncoding = Encoding.UTF8;
        using (var writer = new XmlTextWriter(Console.Out))
        {
            writer.WriteStartDocument();

            writer.WriteStartElement("sphinx", "docset", sphinxns);

            // <sphinx:schema>
            writer.WriteStartElement("sphinx", "schema", sphinxns);

            // <sphinx:field />
            writer.WriteStartElement("sphinx", "field", sphinxns);
            writer.WriteAttributeString("name", "name");
            writer.WriteAttributeString("attr", "string");
            writer.WriteEndElement();

            // <sphinx:attr />
            writer.WriteStartElement("sphinx", "attr", sphinxns);
            writer.WriteAttributeString("name", "price");
            writer.WriteAttributeString("type", "float");
            writer.WriteEndElement();

            writer.WriteEndElement();
            // </sphinx:schema>

            foreach (var product in products)
            {
                // <sphinx:document>
                writer.WriteStartElement("sphinx", "document", sphinxns);
                writer.WriteAttributeString("id", product.ID.ToString());
                writer.WriteElementString("name", product.Name);
                writer.WriteElementString("price", product.Price.ToString());
                writer.WriteEndElement();
                // </sphinx:document>
            }

            writer.WriteEndElement();
        }
    }
}

Скомпилируем это приложение и скопируем его в папку c:\sphinx\data\source\.

Шаг 2. Отредактируем файл конфигурации, изменив опцию xmlpipe_command.

source product
{
    type            = xmlpipe2
    xmlpipe_command = c:\sphinx\data\source\SphinxSourceApp.exe
}

Шаг 3. Ещё раз выполним индексацию командой:

c:\sphinx\bin\indexer product --config c:\sphinx\data\config.txt --rotate

Индексация снова прошла успешно.

Заключение

Источники данных в формате xml во многом являются более гибкими, чем источники, связанные с базами данных. Они позволяют индексировать как относительно небольшие xml-файлы, так и большие объёмы данных, генерируемые «на лету». При этом они не ограничивают вас жёстко заданной схемой, позволяя вместо этого передавать её вместе с данными.

Скачать материалы к этой статье (скрипты, файлы конфигурации, xml-файлы и приложение SphinxSourceApp)

© Игорь Чакрыгин. Все права защищены при помощи чёрной магии. Технологии Blogger.