Ранжирование, теги и поисковые подсказки
В модуле search ранжирование определяет порядок найденных документов, теги помогают уточнять запросы, а подсказки предлагают посетителям поисковые фразы. Статистика запросов позволяет оценить, что ищут пользователи и получают ли они результаты.
Как формируется порядок результатов
Модуль сначала находит документы, которые соответствуют запросу и доступны пользователю. Затем он применяет поля сортировки по порядку. Первое поле имеет наибольший приоритет.
Если второй аргумент CSearch::Search() пуст, модуль использует такой порядок:
-
CUSTOM_RANK DESCставит выше документ с большим пользовательским весом. -
RANK DESCсравнивает вычисленную релевантность. -
DATE_CHANGE DESCставит более новый документ выше, если значения предыдущих полей равны.
Поле RANK зависит от поискового движка. Встроенный движок учитывает частоту слов в документе и их распространенность в индексе. Настройка расстояния между словами может добавить вес близкому расположению слов запроса. Внешний движок рассчитывает релевантность по собственным правилам.
При встроенном поиске Bitrix поле TITLE_RANK отдельно учитывает совпадения в заголовке. MySQL и PostgreSQL обрабатывают его как RANK, а Sphinx и OpenSearch игнорируют. Стандартный компонент bitrix:search.page добавляет это поле в сортировку, когда параметр USE_TITLE_RANK равен Y. О различиях между движками читайте в статье Выбор и настройка поискового движка.
Пользовательский вес не меняет соответствие документа запросу. Он влияет только на порядок уже найденных результатов. Поэтому правило ранжирования не вернет документ с неподходящим текстом, сайтом, датами или правами.
Состав массива сортировки и правила вызова CSearch::Search() смотрите в статье Поисковые запросы через CSearch.
Настроить пользовательское ранжирование
Пользовательское ранжирование — это правила класса CSearchCustomRank, которые задает администратор или разработчик. Они действуют для всех посетителей, а не создают персональный порядок для отдельного пользователя.
Правило ранжирования присваивает значение CUSTOM_RANK группе документов. Сайт, модуль-источник и признаки PARAM1, PARAM2, ITEM_ID задают состав группы. Больший вес поднимает документ при сортировке CUSTOM_RANK DESC.
Используйте правила для устойчивых приоритетов. Например, можно поднять документы одного типа инфоблоков или конкретный элемент. Если вес зависит от состояния отдельного объекта, источник может передать CUSTOM_RANK во время индексации. Подготовку поискового документа показывает инструкция Индексация собственного контента.
Создать правило
Метод CSearchCustomRank::Add() принимает массив полей и возвращает идентификатор правила. Значение false означает, что CheckFields() отклонил пустой SITE_ID или MODULE_ID. Текст такой ошибки находится в свойстве LAST_ERROR. Ошибка SQL приводит к исключению слоя базы данных.
Передайте следующие поля:
-
SITE_ID— обязательный строковый идентификатор сайта, -
MODULE_ID— обязательный идентификатор модуля-источника, -
PARAM1— необязательный первый признак области источника, -
PARAM2— необязательный второй признак области источника, -
ITEM_ID— необязательный идентификатор одного документа, -
RANK— необязательный целый пользовательский вес. База данных использует0, если поле не передано. Для правила задайте значение явно. Чем оно больше, тем выше приоритет.
Пустые PARAM1, PARAM2 и ITEM_ID расширяют правило. Для модуля iblock поле PARAM1 обычно содержит идентификатор типа инфоблоков, а PARAM2 — идентификатор инфоблока. Тип объединяет инфоблоки общего назначения, например новости или каталог. Смысл этих полей для другого источника определяет сам модуль.
Для всех PHP-примеров нужны пролог Bitrix Framework, подключенный модуль search и объект текущего пользователя $USER. Порядок подключения смотрите во введении.
Пример. Правило задает вес 100 всем документам типа инфоблоков news на сайте s1.
use Bitrix\Main\Loader;
if (!Loader::includeModule('search'))
{
throw new \RuntimeException('Модуль search не установлен');
}
$customRank = new \CSearchCustomRank();
$ruleId = $customRank->Add([
'SITE_ID' => 's1',
'MODULE_ID' => 'iblock',
'PARAM1' => 'news',
'PARAM2' => '',
'ITEM_ID' => '',
'RANK' => 100,
]);
if ($ruleId === false)
{
throw new \RuntimeException($customRank->LAST_ERROR);
}
Метод CheckFields() отклоняет пустые SITE_ID и MODULE_ID, только если эти ключи есть в массиве. Наличие ключей он не проверяет. Обязательность полей задает структура таблицы, поэтому вызывающий код должен передать оба значения и проверить их до вызова Add().
Методы класса не проверяют право текущего пользователя на управление модулем поиска. Вызывающий код должен сначала авторизовать действие, затем проверить сайт, разрешенный модуль, область и числовой вес. Не передавайте параметры публичного запроса напрямую в Add(), Update() или Delete(). Используйте административный обработчик или фоновое задание с контролируемыми входными данными.
Изменить или удалить правило
Метод Update() получает два параметра.
-
$ID— целочисленный идентификатор правила. -
$arFields— массив изменяемых полей. Метод исключает ключIDиз массива изменений. Поля, отсутствующие в$arFields, остаются без изменений.
Если массив содержит изменения, метод возвращает объект CDBResult. Если после удаления ключа ID в массиве не осталось полей, метод возвращает true без запроса к базе. Значение false означает, что CheckFields() отклонил пустой SITE_ID или MODULE_ID. Текст такой ошибки доступен в свойстве LAST_ERROR. Ошибка SQL приводит к исключению слоя базы данных.
Пример. Вызов продолжает предыдущий сценарий и меняет вес правила из переменной $ruleId на 150. Объект $customRank создан при добавлении правила.
$updated = $customRank->Update($ruleId, [
'RANK' => 150,
]);
if ($updated === false)
{
throw new \RuntimeException($customRank->LAST_ERROR);
}
Статический метод Delete() принимает целочисленный идентификатор правила. При успешном запросе он возвращает объект CDBResult. Ошибка SQL приводит к исключению слоя базы данных.
Пример. Вызов продолжает тот же сценарий и удаляет правило по сохраненному идентификатору $ruleId.
\CSearchCustomRank::Delete($ruleId);
Изменение записи правила еще не меняет вес существующих документов. После создания, редактирования или удаления примените полный набор правил к индексу.
Применить правила к индексу
Пересчет через StartUpdate() и NextUpdate() обновляет веса только в основном хранилище. Он подходит для Bitrix, MySQL и PostgreSQL. Эти методы не отправляют новые веса в Sphinx и OpenSearch: для применения измененных правил к их документам выполните полную переиндексацию. Значение TODO = 0 само по себе не подтверждает обновление внешнего индекса.
Метод StartUpdate() начинает пересчет. Он помечает все правила как непримененные и сбрасывает CUSTOM_RANK существующих документов в 0. При успехе метод возвращает объект CDBResult. Ошибка SQL приводит к исключению слоя базы данных.
Метод NextUpdate() применяет одно правило и возвращает счетчики:
-
DONEсодержит число примененных правил, -
TODOсодержит число правил, которые еще нужно применить.
Значение false сообщает, что класс не смог обновить документ или отметку правила. Текст ошибки находится в свойстве LAST_ERROR. Ошибка SQL может прервать вызов исключением слоя базы данных.
Метод NextUpdate() применяет правила по возрастанию SITE_ID, MODULE_ID, PARAM1, PARAM2 и ITEM_ID, последовательно учитывая каждое поле. Позднее примененное правило заменяет вес, заданный ранее. Наличие конкретного ITEM_ID само по себе не дает приоритета над правилом с заполненным PARAM1. Для правила отдельного элемента укажите также соответствующие ему PARAM1 и PARAM2. Не создавайте несколько правил с одинаковым набором условий: для таких дублей код не задает устойчивый порядок.
Параметр $maxAttempts ограничивает число последовательных ошибок NextUpdate(). Ограничение останавливает фоновое задание, если временная ошибка стала постоянной. Передайте целое число не меньше 1.
Пример. Функция запускает пересчет и продолжает его до значения TODO = 0. Для каждого правила она допускает не более трех последовательных ошибок.
use Bitrix\Main\DB\SqlQueryException;
use Bitrix\Main\Loader;
if (!Loader::includeModule('search'))
{
throw new \RuntimeException('Модуль search не установлен');
}
function applyCustomRankRules(int $maxAttempts = 3): array
{
if ($maxAttempts < 1)
{
throw new \InvalidArgumentException('Число попыток должно быть положительным');
}
// Начать полный пересчет: сбросить прежние веса и отметки применения правил
$customRank = new \CSearchCustomRank();
$customRank->StartUpdate();
$failedAttempts = 0;
while (true)
{
try
{
// Применить одно правило и получить число оставшихся правил
$progress = $customRank->NextUpdate();
}
catch (SqlQueryException $exception)
{
// Повторить после SQL-ошибки, но остановиться при исчерпании попыток
$failedAttempts++;
if ($failedAttempts >= $maxAttempts)
{
throw new \RuntimeException(
'Не удалось применить правила ранжирования',
0,
$exception
);
}
continue;
}
// Учесть ошибку, которую метод вернул без исключения
if ($progress === false)
{
$failedAttempts++;
if ($failedAttempts >= $maxAttempts)
{
throw new \RuntimeException($customRank->LAST_ERROR);
}
continue;
}
// Успешный шаг сбрасывает счетчик последовательных ошибок
$failedAttempts = 0;
if ((int)$progress['TODO'] === 0)
{
// Все правила применены к основному хранилищу
return $progress;
}
}
}
$progress = applyCustomRankRules();
Метод StartUpdate() сразу сбрасывает прежние веса, включая значения, которые источник передал напрямую в CUSTOM_RANK. Если проект использует такие значения вместе с правилами, заранее определите порядок их восстановления из источника. Большой набор правил лучше пересчитывать в фоновом задании в период низкой нагрузки. Не запускайте два пересчета одновременно. Оба процесса меняют общие отметки APPLIED, поэтому число оставшихся правил становится недостоверным.
Ошибка NextUpdate() оставляет индекс в промежуточном состоянии. Уже обработанные правила сохраняют отметку APPLIED = Y, а остальные остаются со значением N. После временного сбоя продолжите тот же процесс через NextUpdate(). Успешные правила повторно применять не нужно.
После исчерпания попыток запишите исключение или LAST_ERROR в журнал и остановите процесс. Если во время сбоя изменился набор правил или состояние процесса неизвестно, запустите полный проход через StartUpdate(). Результат TODO = 0 подтверждает завершение применения правил к основному хранилищу.
Новые документы получают вес при индексации. Существующая запись получает новый вес, если вызов не завершился раньше из-за совпадения даты. Метод CSearch::Index() отбирает подходящие правила по сайтам документа, модулю, PARAM1, PARAM2 и ITEM_ID, затем выбирает первое в порядке PARAM1 DESC, PARAM2 DESC, ITEM_ID DESC. Это порядок сравнения полей, а не оценка числа заполненных условий. Полный пересчет нужен для документов, которые уже находились в индексе во время изменения правил.
Уточнить выдачу с помощью тегов
Теги связывают документы с короткими тематическими метками. Они подходят для дополнительного уточнения выдачи, автодополнения и облака популярных тем. Тег не заменяет права или область поиска. Модуль применяет эти ограничения и к обычному запросу, и к тегам.
Передать теги при индексации
Поле TAGS поискового документа содержит строку значений через запятую. Модуль обрезает пробелы по краям каждого значения, пропускает пустые элементы и удаляет точные дубли.
Добавьте теги к полному документу, подготовленному по примеру индексации собственного контента. Сохраните его идентификатор, права, сайты и остальные поля.
$searchDocument['TAGS'] = 'уведомления, интеграции, почта';
Передайте полный документ в CSearch::Index(). Если дата исходного объекта не изменилась, задайте $bOverWrite = true, чтобы метод обработал новые теги. Модуль обновит связи тегов и очистит затронутые записи кеша.
Найти документы по тегу
Передайте поле TAGS в первом аргументе CSearch::Search(). Значение содержит теги через запятую. Модуль добавляет каждый непустой тег к поисковому выражению как отдельную фразу.
При встроенном поиске Bitrix пустой QUERY включает отдельный режим поиска по именам тегов. При непустом QUERY тег становится фразой в общем поисковом выражении и может совпасть с текстом документа без такой метки. MySQL и PostgreSQL ищут фразы в полнотекстовом содержимом, а внешние движки используют собственную обработку поля TAGS. Не считайте сочетание текста и тегов переносимым строгим фильтром по меткам; проверьте его на выбранном движке.
Для проверки нужны основные поля:
-
QUERY— строка обычного текстового запроса. Пустая строка оставляет поиск только по тегам. -
TAGS— строка выбранных тегов через запятую. -
SITE_ID— строковый идентификатор сайта. -
MODULE_ID— необязательное ограничение по модулю-источнику.
Пример. Запрос ищет документы собственного модуля с тегом интеграции на сайте s1.
$search = new \CSearch();
$search->Search([
'QUERY' => '',
'TAGS' => 'интеграции',
'SITE_ID' => 's1',
'MODULE_ID' => 'vendor.docs',
]);
if ((int)$search->errorno !== 0)
{
throw new \RuntimeException($search->error);
}
$documents = [];
while ($document = $search->Fetch())
{
$documents[] = $document;
}
Метод проверяет права текущего пользователя. Результат может отличаться для двух пользователей с разными кодами доступа. Ограничение SITE_ID также обязательно для предсказуемой проверки в многосайтовой конфигурации.
Получить теги для автодополнения
Метод CSearchTags::GetList() возвращает объект CDBResult. Это итератор результата из классического API. Метод Fetch() читает следующую строку и возвращает false, когда строки закончились. CSearchTags::GetList() всегда добавляет проверку прав текущего пользователя.
Метод принимает четыре параметра:
-
$arSelect— список полейNAME,CNTиDATE_CHANGE. Пустой массив выбираетNAMEиCNT. -
$arFilter— массив ограниченийSITE_ID,TAG,MODULE_ID,PARAM1,PARAM2иPARAMS. -
$arOrder— массив сортировки поNAME,CNTилиDATE_CHANGE. Пустой массив сортирует по имени по возрастанию. -
$limit— максимальное число тегов. Значение по умолчанию равно100. Для числового лимита модуль учитывает настройкуmax_result_size. Значениеfalseснимает ограничение и отключает кеш этой выборки.
Фильтр TAG выполняет поиск по началу последнего введенного тега. Это позволяет передать всю строку поля и получить варианты для незавершенного фрагмента.
Пример. Код получает до десяти наиболее частых тегов, которые начинаются с инт, относятся к модулю vendor.docs и доступны текущему пользователю на сайте s1.
$tagResult = \CSearchTags::GetList(
[
'NAME',
'CNT',
],
[
'SITE_ID' => 's1',
'TAG' => 'инт',
'MODULE_ID' => 'vendor.docs',
],
[
'CNT' => 'DESC',
'NAME' => 'ASC',
],
10
);
$tags = [];
while ($tag = $tagResult->Fetch())
{
$tags[] = [
'name' => $tag['NAME'],
'documentCount' => (int)$tag['CNT'],
];
}
Компонент bitrix:search.tags.input использует тот же класс для автодополнения. Компонент bitrix:search.tags.cloud строит выборку тегов через режим облака класса CSearch.
Настроить кеш выборки тегов
Класс CSearchTags кеширует ограниченные выборки, если константа CACHED_b_search_tags разрешает кеширование. При подключении модуль задает две поддерживаемые константы:
-
CACHED_b_search_tagsзадает срок кеша в секундах. Значение по умолчанию равно3600. Значениеfalseотключает этот кеш, -
CACHED_b_search_tags_lenзадает максимальную длину префикса из фильтраTAG, при которой запрос можно кешировать. Значение по умолчанию равно2.
Определяйте константы до подключения модуля search, потому что при подключении модуль задает отсутствующие значения по умолчанию. Класс кеширует только ограниченные выборки, поэтому $limit = false одновременно снимает ограничение и отключает кеш. Класс также не кеширует запрос, если длина префикса TAG больше CACHED_b_search_tags_len.
Компонент bitrix:search.tags.cloud дополнительно кеширует подготовленный результат. Он включает группы текущего пользователя в ключ. Поисковая строка или выбранные теги отключают кеш компонента для текущего вызова.
Собрать статистику поисковых фраз
Статистика показывает, что искали посетители, сколько результатов они получили и на какую страницу выдачи перешли. Эти данные помогают найти запросы без результатов и проверить реальные формулировки пользователей.
Настройка модуля stat_phrase включает сбор. Значение по умолчанию равно Y. Метод CSearch::NavStart() создает объект CSearchStatistic и записывает фразу, теги, сайт, число результатов и номер страницы. Поэтому одного вызова Search() недостаточно. Запустите постраничную навигацию, даже если затем читаете только первую страницу.
Модуль связывает запись с сессией. Если пользователь повторяет ту же фразу с теми же тегами, модуль не создает новую запись в пределах этой сессии. Он только увеличивает сохраненный максимальный номер просмотренной страницы.
Метод Fetch() добавляет параметр sphrase_id к URL результата. Обработчик в конце открытой страницы сохраняет адрес перехода и признак ответа 404. Собственный шаблон должен выводить URL из результата без удаления этого параметра.
Проверить фразу в статистике
Метод CSearchStatistic::GetList() возвращает объект CDBResult с собранными записями. Метод Fetch() читает следующую строку и возвращает false после последней записи. Вызывайте GetList() из доверенного служебного сценария. Сам метод не проверяет право на просмотр статистики.
Для проверки передайте следующие параметры:
-
$arOrder— массив сортировки. Пустое значение используетID DESC, -
$arFilter— массив условий. Для контрольного запроса достаточноSITE_IDиPHRASE, -
$arSelect— список возвращаемых полей. Пустое значение выбирает все поддерживаемые поля, -
$bGroup— логический флаг группировки. Для одной контрольной записи оставьтеfalse.
Пример. Код выполняет запрос, запускает навигацию и проверяет статистику. Проверка предполагает, что настройка stat_phrase включена и код запущен в контексте сайта s1: константа SITE_ID равна 's1'. Метод PhraseStat() записывает сайт из этой константы, а не из фильтра поискового запроса.
$search = new \CSearch();
$search->Search([
'QUERY' => 'настройка уведомлений',
'SITE_ID' => 's1',
'MODULE_ID' => 'vendor.docs',
]);
if ((int)$search->errorno !== 0)
{
throw new \RuntimeException($search->error);
}
$search->NavStart(20);
$statistic = \CSearchStatistic::GetList(
[
'ID' => 'DESC',
],
[
'=SITE_ID' => 's1',
'=PHRASE' => 'настройка уведомлений',
],
[
'ID',
'SITE_ID',
'PHRASE',
'RESULT_COUNT',
'PAGES',
'TIMESTAMP_X',
]
);
$phrase = $statistic->Fetch();
if ($phrase === false)
{
throw new \RuntimeException('Поисковая фраза не попала в статистику');
}
Настройка stat_phrase_save_days задает срок хранения статистики в днях. Значение по умолчанию равно 360. Агент периодически удаляет старые записи, если настройка имеет положительное значение.
Подключить подсказки по фразам
Подсказки используют отдельный набор данных. Класс CSearchSuggest не читает записи CSearchStatistic. Стандартная страница поиска наполняет оба набора в одном сценарии. Метод NavStart() сохраняет статистику, а шаблон страницы передает фразу и число результатов в CSearchSuggest::SetResultCount().
Каждая запись подсказки связана с сайтом, фразой и хешем фильтра. Хеш отделяет одинаковые запросы для разных областей поиска. Стандартная страница получает его из результата поиска через GetFilterMD5() и передает компоненту подсказок. Метод GetList() ищет фразы по введенному префиксу и сортирует их по убыванию накопленного рейтинга, затем по алфавиту.
Подсказки общие для сайта или области поиска и не фильтруются по правам пользователя. Хеш фильтра не заменяет проверку доступа. Права на документы проверяются при последующем поиске по выбранной фразе.
Для готового интерфейса включите USE_SUGGEST = Y на странице результатов и в форме. Полный порядок подключения смотрите в статье Поиск на сайте через компоненты.
Шаблоны .default, clear, icons и tags компонента bitrix:search.page записывают подсказки при USE_SUGGEST = Y. Шаблон suggest вызывает SetResultCount() при непустом запросе и готовом объекте навигации. Собственный шаблон должен выполнять этот вызов самостоятельно.
Подсказка появляется после сохранения фразы. Поле на странице результатов использует хеш фильтра и предлагает фразы своей области. Отдельная форма без хеша объединяет накопленные варианты сайта. Поэтому наличие записи в статистике еще не подтверждает, что фраза появится в нужном поле подсказок.
Настройка suggest_save_days задает срок хранения подсказок в днях. Значение по умолчанию равно 30. Агент периодически удаляет устаревшие записи, если настройка имеет положительное значение. Отдельной константы кеша для подсказок нет. Метод CSearchSuggest::GetList() читает актуальные записи при каждом запросе компонента.
Проверить качество выдачи
Проверяйте изменения через обычный поисковый API или компонент. Прямое чтение хранилища не подтверждает сортировку, права, область сайта и обработку запроса.
Подготовьте два тестовых документа с одинаковым контрольным словом. Задайте им разные сайты, права, типы или теги. Затем выполните проверку по шагам.
-
Найдите документы с пустым массивом сортировки и запишите доступные поля
CUSTOM_RANK,RANK,DATE_CHANGE,MODULE_IDиITEM_IDв фактическом порядке. При OpenSearch полеRANKне возвращается, но результаты отсортированы движком. -
Создайте правило с заметно большим весом для одного документа или области. Для Bitrix, MySQL или PostgreSQL примените все правила до
TODO = 0. Для Sphinx или OpenSearch выполните полную переиндексацию. -
Повторите тот же запрос от имени того же пользователя. Целевой документ должен подняться, а состав выдачи не должен измениться только из-за веса.
-
Измените правило и повторите пересчет или переиндексацию для выбранного движка. Проверьте новое значение
CUSTOM_RANKв результате. -
Удалите правило, обновите веса тем же способом и убедитесь, что документ вернулся к обычному порядку.
-
Добавьте уникальный тег одному документу и повторно проиндексируйте его. Поиск с пустым
QUERYи полемTAGSдолжен вернуть документ только на связанном сайте и только пользователю с нужными правами. -
Получите тот же тег через
CSearchTags::GetList(). Повторите вызов для пользователя без доступа. Закрытый документ не должен участвовать вCNTи списке тегов. -
Выполните новую поисковую фразу через страницу с навигацией. Проверьте запись через
CSearchStatistic::GetList(). -
Убедитесь, что шаблон страницы передал фразу в
CSearchSuggest::SetResultCount(). Затем введите начало фразы в компоненте подсказок на том же сайте и с той же областью поиска. -
При расхождении временно передайте
CACHE_TYPE = Nкомпоненту облака. Для отдельного тестового запуска можно определитьCACHED_b_search_tags = falseдо подключения модуляsearch. Если результат изменился, проверьте обновление документа и очистку кеша после индексации.
Фиксируйте строку запроса, пользователя, сайт, область, сортировку и выбранный движок для каждого сравнения. Без этих данных два корректных запроса могут дать разный порядок и состав результатов.
Релевантность, пользовательский вес, теги и подсказки решают разные задачи. Настраивайте их по отдельности и проверяйте вместе на публичном поисковом сценарии. Такой подход показывает реальное качество выдачи и не маскирует ошибку прав или индексации изменением сортировки.