Индексация собственного контента

Модуль search может находить данные собственного модуля вместе со страницами сайта и материалами стандартных модулей. Для этого модуль-источник формирует поисковый документ и передает его в общий индекс через CSearch. В документ входят текст для поиска, адрес результата, сайты и права доступа.

Индекс хранит копию данных. Источник синхронизирует ее при создании, изменении и удалении объектов. Обработчик OnReindex восстанавливает все документы собственного модуля во время полной переиндексации.

Об устройстве индекса и полях документа читайте в статье Архитектура и поисковый индекс.

Подготовить поисковый документ

Сначала выберите ключ документа. Метод CSearch::Index() определяет запись по паре $MODULE_ID и $ITEM_ID.

  • $MODULE_ID содержит строковый идентификатор модуля-источника, например vendor.docs.

  • $ITEM_ID содержит стабильный строковый идентификатор исходного объекта. Идентификатор не должен меняться после редактирования заголовка, адреса или раздела.

Повторный вызов с той же парой обновляет существующую запись. Новый $ITEM_ID создаст другой документ, поэтому заголовок, URL и порядковый номер в выборке не подходят для этой роли.

Затем подготовьте данные, которые влияют на поиск и доступность результата. Таблица содержит только поля практического примера ниже. Дополнительные поля и связи перечислены в модели поискового документа.

Поле

Тип и форма

Когда нужно поле

Что передать

TITLE

string

Обязательно для нового документа

Заголовок документа

BODY

string

Обязательно для нового документа

Текст, по которому нужно искать

DATE_CHANGE

string в формате DD.MM.YYYY HH:MI:SS

Обязательно, если нет LAST_MODIFIED

Дату изменения исходного объекта

SITE_ID

string, список или ассоциативный массив

Обязательно для нового документа без LID

Идентификаторы сайтов или массив вида идентификатор сайта => URL

PERMISSIONS

array

Необязательно

Идентификаторы групп и строковые коды доступа

URL

string

Нужно для перехода, если SITE_ID не содержит адрес

Общий адрес документа

TAGS

string

Необязательно

Теги, которые используют поиск и компоненты проекта

PARAM1

string

Необязательно

Первый признак области источника, например тип документа

PARAM2

string

Необязательно

Второй признак области источника, например идентификатор раздела

Не копируйте исходный объект целиком. Добавьте в TITLE и BODY только содержимое, которое помогает найти материал. HTML-разметка, служебные значения и повторяющиеся элементы интерфейса засоряют индекс и ухудшают фрагмент текста в выдаче.

Передавайте права исходного объекта. Числовой идентификатор группы модуль преобразует в код с префиксом G. Например, группа 2 становится кодом G2 и открывает документ неавторизованным пользователям. Пустой массив не означает доступ для всех.

Для многосайтового проекта задайте URL каждого сайта внутри SITE_ID. Такой адрес имеет приоритет над общим полем URL и ведет пользователя на подходящую версию материала.

Выбирайте метод по тому, что изменилось в источнике.

Задача

Метод

Результат

Добавить документ или обновить его текст, теги и дату

CSearch::Index()

Создает запись или обновляет документ с тем же ключом

Заменить права без обработки текста

CSearch::ChangePermission()

Записывает новый набор кодов доступа

Заменить сайты и адреса без обработки текста

CSearch::ChangeSite()

Обновляет привязки документа к сайтам

Удалить документ

CSearch::DeleteIndex()

Удаляет запись и связанные с ней данные

Добавить документ после создания объекта

Вызывайте CSearch::Index() после успешного сохранения исходного объекта. Если проект использует транзакцию, сначала зафиксируйте изменения. Такой порядок не оставит в индексе документ после отката исходных данных.

Метод принимает пять параметров.

Параметр

Тип

Обязательность и значение по умолчанию

Назначение

$MODULE_ID

string

Обязательный

Идентификатор модуля-источника

$ITEM_ID

string

Обязательный

Стабильный идентификатор документа

$arFields

array

Обязательный

Поля поискового документа

$bOverWrite

bool

Необязательный, false

Управляет принудительной обработкой текста. Значение false позволяет пропустить обработку, если дата не изменилась

$SEARCH_SESS_ID

string

Необязательный, пустая строка

Связывает запись с сеансом полной переиндексации. Для одиночного обновления оставьте значение по умолчанию

Метод возвращает внутренний идентификатор записи при успешной индексации. Значение false означает, что новую запись добавить не удалось.

Значение 0 возвращается при пустом списке сайтов или одновременно пустых TITLE и BODY. Новый документ с пустыми TITLE и BODY не сохранится, а существующий будет удален. Исключение может сообщить об ошибке базы данных, неверной дате или недоступности внешнего поискового движка.

Одиночная и полная индексация должны формировать одинаковый набор полей. Вынесите преобразование исходного объекта в отдельный класс и вызывайте его во всех сценариях.

Класс из примера получает нормализованный массив исходных данных. Это входной формат SearchDocumentBuilder, а не набор полей CSearch. Класс сопоставляет поля проекта с поисковым документом. Названия входных полей можно изменить под модель своего модуля.

Обязательные поля входного массива:

  • ID — стабильный идентификатор объекта,

  • TITLE и HTML — заголовок и HTML-текст,

  • CHANGED_AT — дата изменения в формате DD.MM.YYYY HH:MI:SS,

  • SITE_URLS — непустой массив вида идентификатор сайта => URL,

  • PERMISSION_CODES — массив идентификаторов групп и строковых кодов доступа.

Необязательные поля TAGS, URL и SECTION_ID задают теги, общий адрес и идентификатор раздела для PARAM2. Для каждого отсутствующего поля класс использует пустую строку.

Пример. SearchDocumentBuilder очищает HTML и готовит единый массив. Функция indexHelpArticle() отделяет поле ID и передает остальные данные в CSearch::Index().

namespace Vendor\Docs\Search;

use Bitrix\Main\Loader;

final class SearchDocumentBuilder
{
    public static function build(array $document): array
    {
        // Проверить наличие полей до чтения исходных данных
        $requiredFields = [
            'ID',
            'TITLE',
            'HTML',
            'CHANGED_AT',
            'SITE_URLS',
            'PERMISSION_CODES',
        ];
        foreach ($requiredFields as $field)
        {
            if (!array_key_exists($field, $document))
            {
                throw new \InvalidArgumentException(
                    'Не заполнено обязательное поле ' . $field
                );
            }
        }

        // Удалить HTML-разметку и проверить, что для поиска остался текст
        $body = \CSearch::KillTags((string)$document['HTML']);
        if (
            (string)$document['ID'] === ''
            || (string)$document['TITLE'] === ''
            || $body === ''
            || (string)$document['CHANGED_AT'] === ''
            || !is_array($document['SITE_URLS'])
            || $document['SITE_URLS'] === []
            || !is_array($document['PERMISSION_CODES'])
        )
        {
            throw new \InvalidArgumentException(
                'Не заполнены обязательные данные документа'
            );
        }

        // Преобразовать поля источника в общий формат одиночной и полной индексации
        return [
            'ID' => (string)$document['ID'],
            'DATE_CHANGE' => (string)$document['CHANGED_AT'],
            // Не ограничивать период активности документа
            'DATE_FROM' => false,
            'DATE_TO' => false,
            'TITLE' => (string)$document['TITLE'],
            'BODY' => $body,
            'TAGS' => (string)($document['TAGS'] ?? ''),
            'SITE_ID' => $document['SITE_URLS'],
            'PERMISSIONS' => $document['PERMISSION_CODES'],
            'URL' => (string)($document['URL'] ?? ''),
            'PARAM1' => 'article',
            'PARAM2' => (string)($document['SECTION_ID'] ?? ''),
            'PARAMS' => [],
            'CUSTOM_RANK' => 0,
        ];
    }
}

function indexHelpArticle(array $document): int
{
    if (!Loader::includeModule('search'))
    {
        throw new \RuntimeException('Модуль search не установлен');
    }

    // Передать ID отдельным аргументом Index(), остальные поля — в массиве
    $searchDocument = SearchDocumentBuilder::build($document);
    $documentId = $searchDocument['ID'];
    unset($searchDocument['ID']);

    // Пустые права нужно очистить отдельно, в том числе при повторном запуске
    if ($searchDocument['PERMISSIONS'] === [])
    {
        \CSearch::ChangePermission('vendor.docs', [], $documentId);
    }

    // Принудительно обновить документ, даже если дата не изменилась после сбоя
    $indexId = \CSearch::Index(
        'vendor.docs',
        $documentId,
        $searchDocument,
        true
    );

    // Успешная индексация возвращает положительный внутренний идентификатор
    if ((int)$indexId <= 0)
    {
        throw new \RuntimeException('Не удалось добавить документ в поисковый индекс');
    }

    return (int)$indexId;
}

$indexId = indexHelpArticle([
    'ID' => '154',
    'TITLE' => 'Настройка уведомлений',
    'HTML' => '<p>Как подключить уведомления и выбрать получателей.</p>',
    'TAGS' => 'справка, уведомления',
    'CHANGED_AT' => '26.08.2026 12:30:00',
    'SITE_URLS' => [
        's1' => '/help/notifications/',
    ],
    'PERMISSION_CODES' => [2],
    'URL' => '/help/',
    'SECTION_ID' => 'notifications',
]);

Пример использует одинаковый строковый ITEM_ID = '154' при одиночной и полной индексации. Пустые DATE_FROM и DATE_TO не ограничивают активность, PARAMS не добавляет признаков, а CUSTOM_RANK = 0 задает исходный вес до применения правил. Эти поля заполнены явно, поскольку внешние движки читают их при добавлении документа.

Функция принудительно передает документ движку при каждом вызове, чтобы повтор после сбоя не завершился только из-за прежней даты. Это увеличивает объем повторной обработки. Пустой набор прав функция сначала очищает через ChangePermission().

Пример использует демонстрационные идентификаторы, адреса и права. Замените их данными исходного объекта. Формируйте CHANGED_AT по дате изменения объекта, а не по времени запуска фонового задания. Стабильная дата нужна для сортировки и для других сценариев, в которых $bOverWrite равен false.

Обновить индекс вместе с источником

Следующие короткие примеры предполагают, что код уже подключил модуль search, как в первом примере.

После изменения заголовка, текста, тегов или даты снова вызовите CSearch::Index() с прежними $MODULE_ID и $ITEM_ID. Передайте новую DATE_CHANGE. Метод обновит запись, а стабильный ключ не даст создать дубль.

При значении false в $bOverWrite метод может не перестраивать текст, если новая дата совпала с сохраненной. Передайте true, когда нужно принудительно обработать документ с прежней датой. Значение false сокращает повторную обработку неизменного текста. Для восстановления после сбоя внешнего движка и синхронизации связей при прежней дате используйте true.

Связи с сайтами, именованные параметры и непустой набор прав метод применяет к основному хранилищу до сравнения дат. Если дата совпала, метод возвращает результат до обновления Sphinx или OpenSearch. Для изменения прав используйте ChangePermission(). Изменение сайтов для OpenSearch передавайте полным документом с $bOverWrite = true. CSearch::Index() не очищает прежние коды при пустом PERMISSIONS — даже при принудительной обработке.

Изменить права доступа

Используйте CSearch::ChangePermission(), если у исходного объекта изменились только права или новый набор кодов пуст.

Метод принимает параметры области, в которой нужно заменить права.

Параметр

Тип

Обязательность и значение по умолчанию

Назначение

$MODULE_ID

string

Обязательный

Задает модуль-источник

$arGroups

array

Обязательный

Содержит новый набор идентификаторов групп и кодов доступа. Пустой массив удаляет все сохраненные коды у выбранных документов

$ITEM_ID

string или false

Необязательный, false

Ограничивает изменение одним документом

$PARAM1

string или false

Необязательный, false

Ограничивает документы по первому признаку области источника

$PARAM2

string или false

Необязательный, false

Ограничивает документы по второму признаку области источника

$SITE_ID

string или false

Необязательный, false

Не передавайте идентификатор сайта. Пропустите аргумент или оставьте false. Для отбора объектов по сайту используйте API источника

$PARAMS

array или false

Необязательный, false

Добавляет фильтр по именованным параметрам

Всегда передавайте $ITEM_ID, если требуется изменить один объект. Вызов без этого параметра может заменить права у всего модуля.

Если права нужно изменить для объектов определенного сайта, выберите их средствами источника и вызовите ChangePermission() для каждого ITEM_ID. Права поискового документа общие для всех его сайтов.

\CSearch::ChangePermission(
    'vendor.docs',
    ['G5', 'U42'],
    '154'
);

Числовые идентификаторы групп и готовые строковые коды можно передать вместе. Источник должен сам получить актуальные коды по своим правилам доступа, потому что поисковый индекс не запрашивает права исходного объекта.

Метод не возвращает статус изменения. Проверьте результат двумя поисковыми запросами. Пользователь с новым кодом должен увидеть документ, а пользователь без этого кода не должен получить его в выдаче.

Изменить привязки к сайтам

Для изменения только сайтов и адресов при поиске Bitrix, MySQL, PostgreSQL или Sphinx используйте CSearch::ChangeSite(). При OpenSearch передайте полный документ в CSearch::Index() с $bOverWrite = true: вызов ChangeSite() обновляет основное хранилище, но не переносит документ между индексами сайтов внешнего сервиса.

Метод ChangeSite() не перестраивает таблицу поиска по заголовкам b_search_content_title. Если используете встроенный поиск CSearchTitle, после смены сайтов передайте полный документ с заголовком и новым набором сайтов в CSearch::Index() с $bOverWrite = true. Не отключайте индексацию заголовка через INDEX_TITLE = false. Затем отдельно проверьте обычную выдачу и поиск по заголовкам.

Параметр

Тип

Обязательность и значение по умолчанию

Назначение

$MODULE_ID

string

Обязательный

Задает модуль-источник

$arSite

array

Обязательный

Содержит ассоциативный массив вида идентификатор сайта => URL

$ITEM_ID

string или false

Необязательный, false

Ограничивает изменение одним документом

$PARAM1

string или false

Необязательный, false

Сужает набор документов по первому признаку области источника

$PARAM2

string или false

Необязательный, false

Сужает набор документов по второму признаку области источника

$SITE_ID

string или false

Необязательный, false

Ограничивает изменение прежней привязкой к сайту

Параметр $SITE_ID выбирает документы по прежней привязке. Метод заменяет весь набор сайтов каждого выбранного документа массивом $arSite. Чтобы сохранить другие сайты, также перечислите их в этом массиве.

Передавайте каждый сайт в ключе массива. Если отдельный URL не нужен, задайте пустую строку, например ['s1' => '']. Обычный список вида ['s1', 's2'] метод не преобразует в привязки сайтов.

\CSearch::ChangeSite(
    'vendor.docs',
    [
        's1' => '/help/notifications/',
        's3' => '/knowledge/notifications/',
    ],
    '154'
);

Метод не возвращает статус изменения. Выполните поиск на каждом добавленном и удаленном сайте. Документ должен открываться по URL из новой привязки и исчезнуть из выдачи удаленного сайта.

Проверяйте права и сайты после каждого изменения, чтобы в выдаче не остались прежние ограничения и ссылки. Поисковый индекс не читает эти данные из исходного объекта во время пользовательского запроса.

Удалить документ вместе с объектом

После удаления исходного объекта вызовите CSearch::DeleteIndex(). Не оставляйте запись до следующей полной переиндексации. Иначе пользователь может получить устаревший результат, который ведет на несуществующую страницу.

Метод принимает пять параметров.

Параметр

Тип

Обязательность и значение по умолчанию

Назначение

$MODULE_ID

string

Обязательный

Задает модуль, документы которого нужно удалить

$ITEM_ID

string или false

Необязательный, false

Выбирает документ по идентификатору; при наличии % используется как шаблон

$PARAM1

string или false

Необязательный, false

Ограничивает удаление по первому признаку области источника

$PARAM2

string или false

Необязательный, false

Ограничивает удаление по второму признаку области источника

$SITE_ID

string или false

Необязательный, false

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

Для удаления одного объекта обязательно передайте $ITEM_ID. Без него метод удалит все документы модуля, которые подходят под остальные фильтры. Используйте групповое удаление только с заранее проверенной областью.

Если $ITEM_ID содержит %, метод применяет поиск по шаблону и может удалить несколько документов. Для точечного удаления через DeleteIndex() используйте ключи источника без % и проверяйте их формат перед вызовом.

$documentId = '154';

\CSearch::DeleteIndex(
    'vendor.docs',
    $documentId
);

Метод не возвращает статус удаления. Найдите документ по прежним $MODULE_ID и $ITEM_ID. Метод Fetch() должен вернуть false.

Подключить модуль к полной переиндексации

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

Разместите регистрацию в методе DoInstall() файла /local/modules/vendor.docs/install/index.php. Удалите регистрацию в DoUninstall() того же класса. Материал Структура модуля поможет подготовить файл и распределить код между этими методами.

Пример использует еще два файла.

  • /local/modules/vendor.docs/lib/Search/SearchDocumentBuilder.php содержит класс подготовки документа.

  • /local/modules/vendor.docs/lib/Search/IndexManager.php содержит обработчик переиндексации.

Сохраните для этих классов пространство имен Vendor\Docs\Search. Модуль должен быть зарегистрирован до вызова обработчика, чтобы автозагрузка могла найти классы в каталоге /lib/. Правила именования и размещения классов приведены в разделе Классы модуля.

Для OnReindex нужен совместимый обработчик, потому что модуль search передает ему три отдельных аргумента.

use Bitrix\Main\EventManager;

class vendor_docs extends \CModule
{
    public $MODULE_ID = 'vendor.docs';

    public function DoInstall()
    {
        RegisterModule($this->MODULE_ID);

        EventManager::getInstance()->registerEventHandlerCompatible(
            'search',
            'OnReindex',
            $this->MODULE_ID,
            '\\Vendor\\Docs\\Search\\IndexManager',
            'onReindex'
        );
    }
}

При удалении модуля передайте те же идентификаторы, класс и метод.

use Bitrix\Main\EventManager;

class vendor_docs extends \CModule
{
    public $MODULE_ID = 'vendor.docs';

    public function DoUninstall()
    {
        EventManager::getInstance()->unRegisterEventHandler(
            'search',
            'OnReindex',
            $this->MODULE_ID,
            '\\Vendor\\Docs\\Search\\IndexManager',
            'onReindex'
        );

        UnRegisterModule($this->MODULE_ID);
    }
}

Обработчик получает три параметра.

  • Массив $state передает данные текущего шага. Ключ MODULE указывает модуль продолжения, ID содержит последний обработанный идентификатор, а SITE_ID может ограничить переиндексацию одним сайтом.

  • $callback содержит объект обратного вызова модуля search. Объект добавляет документ и следит за временем шага.

  • $callbackMethod содержит имя метода объекта $callback.

Передавайте объекту $callback массив документа с дополнительным полем ID. Объект использует ID как $ITEM_ID, сам подставляет идентификатор зарегистрированного модуля и передает сеанс переиндексации в CSearch::Index().

Объект $callback возвращает false, когда время шага закончилось. В этом случае обработчик должен вернуть идентификатор последнего переданного документа. На следующем шаге модуль search положит его в $state['ID']. После обработки всех документов верните false.

Пример. Обработчик читает активные документы собственного ORM-объекта по возрастанию ID. Пример предполагает, что DocumentTable содержит поля ID, ACTIVE, SITE_ID, TITLE, BODY, TAGS, TIMESTAMP_X, URL, PERMISSIONS и SECTION_ID. Замените выборку и подготовку прав на API своего модуля. Основные понятия раскрывает статья ORM.

namespace Vendor\Docs\Search;

use Vendor\Docs\DocumentTable;

final class IndexManager
{
    public static function onReindex(
        array $state,
        object $callback,
        string $callbackMethod
    )
    {
        // Использовать позицию продолжения только для своего модуля
        $lastId = 0;
        if (($state['MODULE'] ?? '') === 'vendor.docs')
        {
            $lastId = (int)($state['ID'] ?? 0);
        }

        // Выбрать активные документы после последнего обработанного ID
        $siteId = (string)($state['SITE_ID'] ?? '');
        $filter = [
            '=ACTIVE' => 'Y',
            '>ID' => $lastId,
        ];

        // Учитывать сайт, если переиндексация ограничена одним сайтом
        if ($siteId !== '')
        {
            $filter['=SITE_ID'] = $siteId;
        }

        // Читать не более 1000 записей в стабильном порядке для продолжения
        $documents = DocumentTable::getList([
            'select' => [
                'ID',
                'SITE_ID',
                'TITLE',
                'BODY',
                'TAGS',
                'TIMESTAMP_X',
                'URL',
                'PERMISSIONS',
                'SECTION_ID',
            ],
            'filter' => $filter,
            'order' => ['ID' => 'ASC'],
            'limit' => 1000,
        ]);

        $processed = 0;
        $lastProcessedId = '';
        while ($document = $documents->fetch())
        {
            // Подготовить входные данные для общего построителя документа
            $sourceDocument = [
                'ID' => (string)$document['ID'],
                'TITLE' => $document['TITLE'],
                'HTML' => $document['BODY'],
                'TAGS' => $document['TAGS'],
                'CHANGED_AT' => $document['TIMESTAMP_X']->format('d.m.Y H:i:s'),
                'SITE_URLS' => [
                    $document['SITE_ID'] => $document['URL'],
                ],
                'PERMISSION_CODES' => $document['PERMISSIONS'],
                'URL' => $document['URL'],
                'SECTION_ID' => $document['SECTION_ID'],
            ];
            $searchDocument = SearchDocumentBuilder::build($sourceDocument);
            $documentId = $searchDocument['ID'];

            // Index() не очищает прежние права при пустом массиве PERMISSIONS
            if ($searchDocument['PERMISSIONS'] === [])
            {
                \CSearch::ChangePermission('vendor.docs', [], $documentId);
            }

            // Передать документ через callback: он задает сеанс и проверяет время шага
            $canContinue = call_user_func(
                [$callback, $callbackMethod],
                $searchDocument
            );

            // Документ уже передан; следующий шаг начнется после его ID
            if (!$canContinue)
            {
                return $documentId;
            }

            $processed++;
            $lastProcessedId = $documentId;
        }

        // Лимит выборки достигнут: проверить оставшиеся документы на следующем шаге
        if ($processed === 1000)
        {
            return $lastProcessedId;
        }

        // Все документы источника обработаны
        return false;
    }
}

Сортируйте выборку по стабильному и уникальному значению, которое обработчик возвращает для продолжения. Фильтр следующего шага должен исключать уже обработанные документы. В примере эту роль выполняет числовой ID и условие >ID.

Не вызывайте CSearch::Index() напрямую внутри обработчика, когда модуль передал объект $callback. Прямой вызов обойдет контроль времени шага и не передаст идентификатор сеанса. После завершения переиндексации модуль может удалить документ, который не получил метку текущего сеанса.

Этот обработчик рассчитан на пошаговый ReIndexAll() с положительным лимитом времени. Не вызывайте его через ReindexModule() или ReIndexAll() без лимита. После 1000 записей обработчик возвращает позицию продолжения, но эти режимы не запускают следующий шаг автоматически.

Лимит выборки защищает процесс от лишнего расхода памяти, но не заменяет объект $callback. Объект останавливает шаг по времени. Если выборка достигла лимита, обработчик также возвращает последний ID и продолжает работу на следующем шаге.

Не отключайте одиночную синхронизацию на время полного прохода. Если исходный объект изменится после своего шага, обычный обработчик изменения должен снова передать документ с тем же ключом. Общий SearchDocumentBuilder сохранит одинаковый состав полей при обоих способах индексации.

Чтобы проверить регистрацию, установите модуль и откройте страницу Настройки > Поиск > Переиндексация. Запустите полный проход. Модуль search вызовет зарегистрированный обработчик OnReindex и передаст ему состояние шага. После завершения найдите контрольный документ по уникальному слову.

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

Индексировать статический файл

Метод CSearch::ReindexFile() подходит для физического файла публичной части. Он проверяет доступность файла, разрешенный путь и ограничение размера из настроек модуля. Затем метод получает заголовок и содержимое, вычисляет права на чтение файла и передает документ в CSearch::Index() от имени модуля main.

Для вызова подготовьте два параметра.

Параметр

Тип

Обязательность и значение по умолчанию

Назначение

$path

array

Обязательный

Содержит идентификатор сайта и путь относительно document root этого сайта. Путь должен включать каталог сайта: например, /en/help/page.php для сайта в /en/. Строковый путь метод не обрабатывает

$SEARCH_SESS_ID

string

Необязательный, пустая строка

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

Метод возвращает int|false. Положительное число — идентификатор записи после успешной индексации. Значение 0 означает, что файл не прошел проверку или из него не удалось получить заголовок. Значение false сообщает, что CSearch::Index() не смог добавить запись.

use Bitrix\Main\Loader;

if (!Loader::includeModule('search'))
{
    throw new \RuntimeException('Модуль search не установлен');
}

$indexId = \CSearch::ReindexFile([
    's1',
    '/help/notifications/index.php',
]);

if ((int)$indexId <= 0)
{
    throw new \RuntimeException('Не удалось проиндексировать файл');
}

Используйте ReindexFile() для статической страницы, содержимое которой связано с физическим файлом. Если страница строится из данных собственного модуля, сформируйте документ из исходного объекта и вызовите CSearch::Index(). Такой подход дает источнику контроль над текстом, адресом и правами.

Событие OnSearchGetFileContent позволяет подключить отдельный разбор содержимого файла. Не добавляйте обработчик только ради обычного HTML или PHP-файла. Модуль сначала вызывает обработчики события, а при отсутствии подходящего результата использует стандартное получение содержимого.

Восстановить синхронизацию после ошибки

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

Стабильная пара $MODULE_ID и $ITEM_ID предотвращает дубли. Для восстановления внешнего индекса после частичного сбоя повторно передайте полный актуальный документ и $bOverWrite = true, иначе совпадение даты может остановить обновление движка.

Очередь повторной синхронизации должна различать три кода действий.

  • index — заново формирует актуальный документ и вызывает CSearch::Index().

  • permissions — получает текущие права и вызывает CSearch::ChangePermission().

  • delete — вызывает CSearch::DeleteIndex(), даже если исходный объект уже отсутствует.

Не сохраняйте весь поисковый документ в очереди надолго. К моменту повтора текст или права могут измениться еще раз. Храните идентификатор и собирайте актуальные данные из источника непосредственно перед вызовом API.

Полная переиндексация восстанавливает весь набор документов, но не заменяет ежедневную синхронизацию. Между изменением объекта и полным проходом пользователи будут видеть прежнее состояние.

Проверить документ в поисковой выдаче

После индексации ищите документ через CSearch или стандартный компонент. Не проверяйте запись прямым запросом к таблицам или к внешнему движку. Такой запрос не подтверждает, что обычный пользователь пройдет фильтры сайта и прав.

Для контрольного запроса передайте четыре поля в массиве CSearch::Search().

Поле

Тип

Обязательность для проверки

Назначение

QUERY

string

Обязательное

Содержит уникальное слово из заголовка или текста документа

SITE_ID

string

Обязательное

Ограничивает поиск сайтом, с которым связан документ

MODULE_ID

string

Обязательное

Ограничивает поиск документами собственного модуля

ITEM_ID

string

Обязательное

Ограничивает поиск одним исходным объектом

Метод Search() выполняет запрос с правами текущего пользователя. После вызова свойство errorno содержит код ошибки, а error — ее текст. Метод Fetch() возвращает найденный документ или false.

Пример. Функция ищет проиндексированную статью по уникальному слову и проверяет ошибку запроса.

use Bitrix\Main\Loader;

function findIndexedDocument(
    string $query,
    string $siteId,
    string $documentId
): array
{
    if (!Loader::includeModule('search'))
    {
        throw new \RuntimeException('Модуль search не установлен');
    }

    // Искать конкретный документ своего модуля на выбранном сайте
    $search = new \CSearch();
    $search->Search([
        'QUERY' => $query,
        'SITE_ID' => $siteId,
        'MODULE_ID' => 'vendor.docs',
        'ITEM_ID' => $documentId,
    ]);

    // Отделить ошибку поискового запроса от отсутствия документа в выдаче
    if ((int)$search->errorno !== 0)
    {
        throw new \RuntimeException($search->error);
    }

    // Результат учитывает права текущего пользователя
    $result = $search->Fetch();
    if ($result === false)
    {
        throw new \RuntimeException('Документ не найден');
    }

    return $result;
}

$result = findIndexedDocument(
    'уведомления',
    's1',
    '154'
);

О формате запроса, навигации и безопасном выводе читайте в статье Поисковые запросы через CSearch.

Проведите проверку по шагам.

  1. Добавьте в тестовый документ уникальное слово и запомните $MODULE_ID, $ITEM_ID, сайты, URL и коды доступа.

  2. Выполните индексацию и проверьте положительный идентификатор результата.

  3. Найдите уникальное слово от имени пользователя с разрешенным доступом.

  4. Повторите запрос от имени пользователя без нужного кода. Документ не должен попасть в выдачу.

  5. Проверьте результат на каждом сайте. Ссылка должна вести на URL из соответствующей привязки SITE_ID.

  6. Измените уникальное слово и дату исходного объекта. После синхронизации новый запрос должен находить документ, а прежний не должен.

  7. Удалите только поисковую запись через CSearch::DeleteIndex(), сохранив исходный объект. Запустите переиндексацию собственного модуля и убедитесь, что документ восстановлен и находится по новому слову.

  8. Удалите исходный объект и вызовите CSearch::DeleteIndex(). Убедитесь, что документ исчез из выдачи и не появляется после повторной переиндексации модуля.

Внешний движок может применить изменение не мгновенно. Если документ не появился сразу, проверьте состояние движка и повторите запрос после ожидаемой задержки. Статья Выбор и настройка поискового движка объясняет различия между вариантами.

Проверка полного цикла показывает больше, чем успешный возврат CSearch::Index(). Она подтверждает текст, права, сайт, адрес, обновление, удаление и восстановление документа.