1С-Битрикс. Добавляем в морфологический индекс поиска возможность искать по части слова

Наверняка, многие знают, что в битриксе есть морфологический поиск. И вроде бы он даже кое-как работает. Но если мы торгуем товарами, и хотим, чтобы поиск искал и по части чего-то специфического, например, артикула товара (пример: артикул «Р754777», подстрокой будет «54777»), то ничего из этого не выйдет. Поиск просто не найдет ничего.

В этой статье мы разберем действенный способ решения этой проблемы.

Суть


В момент индексации элемента будем добавлять в таблицу b_search_content_stem наши части слов. То есть, в нашем случае, для товара с артикулом «Р6543» мы добавим «6543» в поисковый индекс. Запросы «654» и «65» так же будут отдавать товар с артикулом «Р6543».

Обработчики — наше всё


Как обычно, решать мы будем обработчиками. Оффтоп: иногда вижу проекты вообще без файлов init.php и своих кастомных модулей. Ребята, как вам удаётся без этого сайты собирать?


Будем слушать 3 события: BeforeIndex, OnBeforeIndexUpdate и OnAfterIndexAdd.


  • BeforeIndex — вызывается перед индексацией элемента методом CSearch::Index()
  • OnBeforeIndexUpdate — вызывается перед обновлением поискового индекса
  • OnAfterIndexAdd — вызывается после добавления новых данных в поисковый индекс.

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

На события OnBeforeIndexUpdate и OnAfterIndexAdd запишем в таблицу b_search_content_stem то, что нам нужно. API для этого нет, так что будем писать чистые SQL-запросы. Проверял на MySQL с InnoDB таблицей. На остальных не факт, что будет корректно работать


Код обработчика


Сохраняем код в файле /local/php_interface/classes/handlers/search/stemming.php


В свойстве $allowedIblockId класса необходимо указать ID инфоблоков, для которых нужен функционал.


		 <!?php namespace Olegpro\Classes\Handlers\Search; use Bitrix\Main\Loader; use Bitrix\Main\Application; use Bitrix\Main\DB\SqlQueryException;
		class Stemming { /** * @var array */ protected static $allowedIblockId = array( 1 ); /** * @var */ private static $element; /** * @param array $arFields * @return mixed */ public static function beforeIndex($arFields) { static $allowedIblockId = null; if ($allowedIblockId === null) { $allowedIblockId = array_flip(self::$allowedIblockId); } if ( $arFields['MODULE_ID'] == 'iblock' && isset($allowedIblockId[$arFields['PARAM2']]) && strlen($arFields['ITEM_ID']) > 0 && substr($arFields['ITEM_ID'], 0, 1) != 'S' && Loader::includeModule('iblock') ) { if ($obElement = \CIBlockElement::GetList( array(), array('ID' => $arFields['ITEM_ID']), false, false, array() )->GetNextElement() ) { $element = $obElement->GetFields(); $element['PROPERTIES'] = $obElement->GetProperties(); self::$element = $element; } } return $arFields; } /** * Events: OnBeforeIndexUpdate and OnAfterIndexAdd * @param $indexId * @param $arFields */ public static function beforeIndexUpdate($indexId, $arFields) { if ( isset(self::$element) && is_array(self::$element) && isset(self::$element['PROPERTIES']['ARTICLE']) && strlen(trim(self::$element['PROPERTIES']['ARTICLE']['VALUE'])) && preg_match('~^.*?([0-9.,]+).*?$~', trim(self::$element['PROPERTIES']['ARTICLE']['VALUE']), $m) ) { $word = ToUpper($m[1]); $stemId = \CSearch::RegisterStem($word); if ($stemId > 0) { $connection = Application::getConnection(); $sqlHelper = $connection->getSqlHelper(); try { $thereIs = $connection->queryScalar( sprintf( "SELECT 1 FROM b_search_content_stem WHERE SEARCH_CONTENT_ID = '%s' AND STEM = '%s'", $sqlHelper->forSql($indexId), \CSearch::RegisterStem($word) ) ); if ($thereIs === null) { $connection->query(sprintf( "INSERT IGNORE INTO `b_search_content_stem` (`SEARCH_CONTENT_ID`, `LANGUAGE_ID`, `STEM`, `TF`, `PS`) VALUES ('%s', '%s', '%s', '%s', '%s')", $sqlHelper->forSql($indexId), 'ru', \CSearch::RegisterStem($word), number_format(0.2, 4, ".", ""), number_format(1, 4, ".", "") )); } } catch (SqlQueryException $e) { AddMessage2Log(sprintf("\\%s:\n%s", __METHOD__, $e->getMessage())); } } } self::$element = null; } }
	

Регистрируем обработчики и добавляем класс в автозагрузку


Добавить в /local/php_interface/init.php
	 <!?php \Bitrix\Main\EventManager::getInstance()->addEventHandler('search', 'BeforeIndex', array('\Olegpro\Classes\Handlers\Search\Stemming', 'beforeIndex') ); \Bitrix\Main\EventManager::getInstance()->addEventHandler('search', 'OnBeforeIndexUpdate', array('\Olegpro\Classes\Handlers\Search\Stemming', 'beforeIndexUpdate') ); \Bitrix\Main\EventManager::getInstance()->addEventHandler('search', 'OnAfterIndexAdd', array('\Olegpro\Classes\Handlers\Search\Stemming', 'beforeIndexUpdate') ); \Bitrix\Main\Loader::registerAutoLoadClasses(null, array( '\Olegpro\Classes\Handlers\Search\Stemming' => '/local/php_interface/classes/handlers/search/stemming.php', ));

На этом всё. Осталось только запустить полную переиндексацию и теперь можно не тратить жизнь на вписывание в поиск артикулов полностью.

Автор статьи: Максименко Олег
Статья взята из источника ссылка