Написал пакет olegpro/bitrix-sphinx для composer, который содержит набор классов для работы с выборками из индексов sphinx через ORM D7 в привычном виде.

В комплекте пока 4 класса:

  • \Olegpro\BitrixSphinx\DB\SphinxConnection - класс для указания в /bitrix/.settings.php в качестве подключения к sphinx (наследуется от Bitrix\Main\DB\MysqliConnection)
  • \Olegpro\BitrixSphinx\DB\SphinxSqlHelper - класс-хелпер (наследуется от Bitrix\Main\DB\MysqliSqlHelper)
  • \Olegpro\BitrixSphinx\Entity\SphinxDataManager - класс для работы с объектами данных (наследуется от Bitrix\Main\Entity\DataManager)
  • \Olegpro\BitrixSphinx\Entity\SphinxQuery - класс запросов (наследуется от Bitrix\Main\Entity\Query)

Установка

composer require olegpro/bitrix-sphinx

Настройка подключения к sphinx

В файле /bitrix/.settings.php секцию connections нужно изменить на что-то вроде:

'connections' =>
    array(
        'value' =>
            array(
                'default' =>
                        array(
                            // ...
                        )
                    ),
                'sphinx' =>
                    array(
                        'className' => '\\Olegpro\\BitrixSphinx\\DB\\SphinxConnection',
                        'host' => '127.0.0.1:9306',
                        'database' => '',
                        'login' => '',
                        'password' => '',
                        'options' => 1,
                    ),
            ),
        'readonly' => true,
    ),

Описываем класс-сущность

На примере индекса sample_index.

<?php
 
use Bitrix\Main;
use Bitrix\Main\Localization\Loc;
use Olegpro\BitrixSphinx\Entity\SphinxDataManager;
use Olegpro\BitrixSphinx\Entity\SphinxQuery;
 
Loc::loadMessages(__FILE__);
 
class SampleTable extends SphinxDataManager
{
 
    /**
     * Returns index sphinx name for entity.
     *
     * @return string
     */
    public static function getTableName()
    {
        return 'sample_index';
    }
 
    /**
     * Returns sphinx-connection name for entity
     *
     * @return string
     */
    public static function getConnectionName()
    {
        return 'sphinx';
    }
 
    /**
     * Creates and returns the Query object for the entity
     *
     * @return SphinxQuery
     */
    public static function query()
    {
        return new SphinxQuery(static::getEntity());
    }
 
    /**
     * Returns entity map definition.
     *
     * @return array
     */
    public static function getMap()
    {
        return [
            new Main\Entity\IntegerField('id', [
                'primary' => true,
            ]),
            new Main\Entity\StringField('name'),
            new Main\Entity\BooleanField('available', [
                'values' => [0, 1],
            ])
        ];
 
    }
 
}

Как вариант, можно в методе getMap получать все поля из информации о индексе в формировать массив полей на лету. Получится что-то вроде:

<?php
 
use Bitrix\Main;
use Bitrix\Main\Localization\Loc;
use Olegpro\BitrixSphinx\Entity\SphinxDataManager;
use Olegpro\BitrixSphinx\Entity\SphinxQuery;
 
Loc::loadMessages(__FILE__);
 
class SampleTable extends SphinxDataManager
{
 
    /**
     * Returns index sphinx name for entity.
     *
     * @return string
     */
    public static function getTableName()
    {
        return 'sample_index';
    }
 
    /**
     * Returns sphinx-connection name for entity
     *
     * @return string
     */
    public static function getConnectionName()
    {
        return 'sphinx';
    }
 
    /**
     * Creates and returns the Query object for the entity
     *
     * @return SphinxQuery
     */
    public static function query()
    {
        return new SphinxQuery(static::getEntity());
    }
 
    /**
     * Returns entity map definition.
     *
     * @return array
     */
    public static function getMap()
    {
        $fields = [];
 
        $connection = Main\Application::getConnection(self::getConnectionName());
 
        $result = $connection->query(
            sprintf(
                'DESCRIBE %s',
                $connection->getSqlHelper()->forSql(self::getTableName())
            )
        )->fetchAll();
 
        foreach ($result as $item) {
 
            $name = $item['Field'];
 
            switch ($item['Type']){
 
                case 'int':
                case 'uint':
                case 'bigint':
                case 'mva':
                    $fields[] = new Main\Entity\IntegerField($name, [
                        'primary' => ($name === 'id'),
                    ]);
                    break;
 
                case 'bool':
                    $fields[] = new Main\Entity\BooleanField($name, [
                        'values' => [0, 1],
                    ]);
                    break;
 
                case 'string':
                    $fields[] = new Main\Entity\StringField($name, [
                    ]);
                    break;
 
                default:
                    break;
 
            }
 
        }
 
        return $fields;
 
    }
 
}

Примеры выборок

1) Ищем по слову «книга», добавляем фильтр по атрибуту available, сортируем по весу, ограничиваем выборку лимитов в 10 записей.

<?php
 
use Bitrix\Main\Application;
use Bitrix\Main\Entity\ExpressionFieldd;
 
require($_SERVER['DOCUMENT_ROOT'] . '/bitrix/header.php');
 
Application::getConnection(SampleTable::getConnectionName())->startTracker(true);
 
$iterator = SampleTable::getList([
    'select' => [
        '*',
        new ExpressionField('weight', 'WEIGHT()', 'id'),
    ],
    'match' => 'книга',
    'filter' => [
        '=available' => 1,
    ],
    'limit' => 10,
    'order' => [
        'weight' => 'DESC',
    ],
    'option' => [
        'max_matches' => 50000,
    ],
]);
 
echo '<pre>';print_r($iterator->getTrackerQuery()->getSql());echo '</pre>';
 
echo '<pre>';print_r($iterator->fetchAll());echo '</pre>';

2) Тоже самое, но с постраничной навигацией

<?php
 
use Bitrix\Main\Application;
use Bitrix\Main\Entity\ExpressionFieldd;
use Bitrix\Main\UI\PageNavigation;
 
require($_SERVER['DOCUMENT_ROOT'] . '/bitrix/header.php');
 
Application::getConnection(SampleTable::getConnectionName())->startTracker(true);
 
$nav = new PageNavigation('s');
 
$nav->allowAllRecords(false)
    ->setPageSize(10)
    ->initFromUri();
 
$iterator = SampleTable::getList([
    'select' => [
        '*',
        new ExpressionField('weight', 'WEIGHT()', 'id'),
    ],
    'match' => 'книга',
    'filter' => [
        '=available' => 1,
    ],
    'count_total' => true,
    'offset' => $nav->getOffset(),
    'limit' => $nav->getLimit(),
    'order' => [
        'weight' => 'DESC',
    ],
    'option' => [
        'max_matches' => 50000,
    ],
]);
 
$nav->setRecordCount($iterator->getCount());
 
echo '<pre>';print_r($iterator->getTrackerQuery()->getSql());echo '</pre>';
 
echo '<pre>';print_r($iterator->fetchAll());echo '</pre>';
 
 
$APPLICATION->IncludeComponent(
    "bitrix:main.pagenavigation",
    "",
    array(
        "NAV_OBJECT" => $nav,
        "SEF_MODE" => "N",
    ),
    false
);



Пакет olegpro/bitrix-sphinx: