Appearance
Модуль для работы с ElasticSearch. realweb.api.elastic
Совместимость
realweb.api v.6
Устаревшая версия модуля
Модуль позволяет индексировать любые элементы на сайте и быстро искать по ним. Модуль предоставляет api для работы с elastic search. Может использоваться для:
- Поиска по сайту
- Реализации “умного фильтра” с возможностью показа недоступных фильтров и количества товаров
- Фильтрации товаров по фильтру в каталоге
Преимущество использования модуля в:
- Простоте реализации функциональности
- Быстроте работы
- Отсутствие ошибок работы (при правильно составленном индексе)
Модуль также позволяет добавить словарь синонимов (Например, для транслитерации поиска и т.п.) Все существующие индексы можно анализировать через сервис http://kibana.bitrix.dev.realweb.ru/
Установка и настройка
Linux
- скачать архив ES с
DEV/var/www/apps/elasticsearch-8.1.1-amd64.deb - проверка контрольной суммы
shell
shasum -a 512 -c elasticsearch-8.1.1-amd64.deb.sha512- установка
shell
sudo dpkg -i elasticsearch-8.1.1-amd64.deb- настройка конфигов
shell
nano /etc/elasticsearch/elasticsearch.yml- основные настройки
ini
network.host: localhost
http.port: 9200
xpack.security.enabled: false- ограничение использования MEM
shell
nano /etc/default/elasticsearch- основная настройка
ini
ES_JAVA_OPTS="-Xms2g -Xmx2g"- запуск сервиса
shell
sudo systemctl daemon-reload
sudo systemctl enable elasticsearch.service
sudo systemctl start elasticsearch.service- проверка
shell
curl -X GET http://localhost:9200/?prettyНастройка безопасности
Будет требоваться авторизации при запросах к ES, также это нужно, чтобы получить полные права в кибане
- изменения в конфиге
/etc/elasticsearch/elasticsearch.yml
ini
xpack.security.enabled: true
xpack.security.http.ssl:
enabled: false- сброс пароля
shell
/usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic -f -i- проверка
shell
curl --user elastic:cxlQHE#z4zK*M5 -X GET "http://localhost:9200?pretty"Windows
Установка ES
- Скачать архив ES с
DEV/var/www/apps/elasticsearch-8.1.1-windows-x86_64.zip - Распаковать в удобную для себя папку
- Установка службу
cmd
elasticsearch-8.1.1\bin>elasticsearch-service.bat install- Настроить выделение памяти для службы
cmd
elasticsearch-8.1.1\bin>elasticsearch-service.bat managerВ открывшемся окне провести настройки

- Запустить службу По умолчанию сервис будет запущен на 9200 порту.
Официальная инструкция по установке
Установка Kibana
- скачать архив ES с
DEV/var/www/apps/kibana-8.1.1-windows-x86_64.zip - распаковать в удобную для себя папку
- запустить из командной строки
cmd
kibana-8.1.1\bin>kibana.batОфициальная инструкция по установке
Настройка Kibana как службы
- Скачать NSSM
- Выполнить
cmd
nssm install KibanaВ открывшемся окне указать путь к kibana.bat по аналогии с картинкой.

- Во вкладке Dependencies можно указать службу Elastic Search.
- Нажать Install Service.
- Запустить службу
После запуска kibana будет доступна в браузере по адресу http://localhost:5601
MacOs
Установка kibana
Установка и настройка Kibana
shell
sudo dpkg -i kibana-8.1.1-amd64.debзапуск сервиса
shell
sudo systemctl daemon-reload
sudo systemctl enable kibana.service
sudo systemctl start kibana.serviceпример конфига nginx
text
server {
listen 80;
server_name kibana.local;
#server_name server-ip-address;
#For autherntication remove the # from below two line:
#auth_basic "Restricted Access";
#auth_basic_user_file /etc/nginx/.htpasswd;
location / {
proxy_pass http://localhost:5601;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#upgrade to WebSocket protocol when requested
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
}Если нужны полные права, то необходимо выполнить следующие шаги
- сброс пароля (вход в кибану по elastic:cxlQHE#z4zK*M5)
shell
/usr/share/elasticsearch/bin/elasticsearch-reset-password -u kibana_system -f -i- конфигурация
ini
elasticsearch.username: "kibana_system"
elasticsearch.password: "cxlQHE#z4zK*M5"
xpack.encryptedSavedObjects.encryptionKey: "ключ_32_символа"- перезапуск сервиса
shell
systemctl restart kibanaВ случае возникновения ошибки Kibana server is not ready yet. удалить индексы кибаны
shell
curl http://localhost:9200/_aliases?pretty=true
curl -XDELETE http://localhost:9200/.kibana-event-log-8.1.1-000001Описание работы с Kibana в веб-интерфейсе
todo
Работа с модулем.
Для работы с модулем необходимо создать структуру ORM для индекса. Пример
Создание индекса / Обновление индекса
Описание индекса во многом напоминает описание таблиц Mysql Класс, описывающий индекс расширяет класс \Realweb\Api\Module\Elastic\Model\Orm\DataManager и должен реализовывать следующие методы:
public static function getIndexCode(): string;- получение название индексаpublic static function getMap(): array;- получение массива полей индекса
Также, класс \Realweb\Api\Module\Elastic\Model\Orm\DataManager имеет встроенные методы
public static function getSettings(): array- возвращает настройки индексаpublic static function save($bDelete = false): bool- Создание / обновление индекса. Если передать в качестве параметркаtrue- индекс будет сначала удален.
Необходимо помнить, что менять типы свойств elasticsearch не умеет. Поэтому для изменения типа поля индекс необходимо сохранять с параметром true. После сохранения индекса необходимо переиндексировать все записи.
public static function delete(): bool- удаление индексаpublic static function close(): bool- закрытие индекса, необходимо перед сохранением настроекpublic static function open(): bool- открытие индекса, необходимо после сохранения настроек
Поддерживаемые типы полей
\Realweb\Api\Module\Elastic\Model\Orm\Field\BooleanField- Булево\Realweb\Api\Module\Elastic\Model\Orm\Field\DateField- Дата\Realweb\Api\Module\Elastic\Model\Orm\Field\FloatField- Дробное число\Realweb\Api\Module\Elastic\Model\Orm\Field\IntegerField- Целое число\Realweb\Api\Module\Elastic\Model\Orm\Field\ByteField- Целое число в пределах от -128 до 127\Realweb\Api\Module\Elastic\Model\Orm\Field\StringField- Строка (может агрегироваться и по строке можно сортировать)\Realweb\Api\Module\Elastic\Model\Orm\Field\TextField- Текст (Не может агрегироваться, сортировка по полю невозможна)
Индексация
Основной сущностью (записью, документом) является экземпляр класса расширяющий \Realweb\Api\Module\Elastic\Model\Orm\Entity должен реализовывать следующий метод:
protected static function _getDatabase(): string;- получение класса Базы Данных
Имеет встроенные методы
public function getId(): int|string- Получение уникального идентификатора (Зачастую он совпадает сID)public function getData(): array- получение данных сущностиpublic function save(): bool- Создание / обновление сущностиpublic function isExist(): bool- проверка на существование
Пример индексации сущностей
php
$obIndexEntity = new \Realweb\Api\Module\Catalog\Model\Element\Elastic\Orm\Entity(1);
$obIndexEntity
->setBrandId('3')
->setSectionId(array('1', '2'))
->setPrice('1100.50')
->setName('First Entity')
->setDescription('Description of First Entity')
->setDate(new \DateTimeImmutable('2023-01-01'))
->setPopular(0);
$obIndexEntity->save();
$obIndexEntity = new \Realweb\Api\Module\Catalog\Model\Element\Elastic\Orm\Entity(2);
$obIndexEntity
->setBrandId('1')
->setSectionId(array('1', '3'))
->setPrice('1000')
->setName('Second Entity')
->setDescription('Description of Second Entity')
->setDate(new \Realweb\Api\Model\Utils\DateTime('2023-01-01 11:01','Y-m-d H:i'))
->setPopular(0);
$obIndexEntity->save();Поиск по индексу
Database
Работа с индексом максимальна похожа на работу с Инфоблоками и другими таблицами БД в ядре realweb.api за некоторыми исключениями. Наследуемая Database должна реализовывать следующие методы
public static function getQuery(?Pagination $obNav = null): Elastic\Model\Query;- возвращает запрос к индексуpublic static function getIndex(): string;- класс индекса, наследуется от\Realweb\Api\Module\Elastic\Model\Orm\DataManagerpublic static function getAggregationCountFields(): array;- массив "количественных" полей индекса - списки выбора, чекбоксы и т.п.public static function getAggregationRangeFields(): array;- массив "диапазонных" полей индекса - цены и т.п.public static function getCollection(): string;- класс коллекции сущностей, наследуется отElastic\Model\Orm\Collectionpublic static function getEntity(): string;- класс сущности, наследуется отElastic\Model\Orm\Entity
Встроенные методы
public static function getFields(): array- массив полей, выбираемых в индексе, по умолчанию пустой массивpublic static function getRow(?Pagination $obNav = null): ?array- возвращает одну запись из индекса в виде массива по фильтруpublic static function getObject(?Pagination $obNav = null): Entity- возвращает экземпляр класса, объявленного вgetEntityпо фильтруpublic static function getList(?Pagination $obNav = null): array- возвращает массив записей из индекса в виде массивов по фильтруpublic static function getObjectCollection(?Pagination $obNav = null): Collection- возвращает коллекцию сущностей, объявленную вgetCollectionпо фильтруpublic static function getByPrimary(int | string $mPrimary): Entity- возвращает экземпляр сущности по первичному ключу (В эластике это_id)public static function getCount(?Pagination $obNav = null): int- возвращает количество записей по фильтруpublic static function getAggregation(?Pagination $obNav = null): Elastic\Model\Aggregation\Collection- возвращает коллекцию агрегированных данных по фильтру
Elastic\Model\Query
Класс запроса в индекс. Экземпляр класса получаем методом parent::_query(); Методы работы с классом похожи на методы с аналогичным классом при работе с ИБ. По умолчанию фильтрация происходит с логическим "И"
Встроенные методы
public function setFields(array $arFields): self- установка полей запросаpublic function addField(string $strField): self- добавление поля в запросpublic function setSelect(array $arFields): self- алиас setFieldspublic function addSelect(string $strField): self- алиас addFieldpublic function where(...$arVariables): self- метод задания условия фильтрации, может принимать либо 3 аргумента либо 1 3 аргумента 1 аргумент - название поля 2 аргумент - операция условия (=, >, <, >=, <=, !=) 3 аргумент - значение 1 аргумент - подзапрос, экземпляр класса Elastic\Model\Filterpublic function count(): int- выполнение запроса к индексу на получение количества сущностей по фильтруpublic function search(): \Bitrix\Main\DB\ArrayResult- выполнение запроса к индексу на получение списка сущностей по фильтруpublic function aggregation(): \Bitrix\Main\DB\ArrayResult- выполнение запроса к индексу на получение списка агрегации по фильтруpublic function getQuery(): array- получение массива параметров для запроса к индексу (для дебага)public function getIndexCode(): string- получение символьного кода индексаpublic function setLimit(?int $obLimit): self- установка размера страницы при пагинацииpublic function setOffset(?int $obOffset): self- установка смещения запроса при пагинацииpublic function setOrder(array $arOrder): self- установка сортировки (Аналогичное поведение как и в случае Query для ИБ)public function addOrder(string $strOrderField, string $strOrder): self- добавление поля сортировки (Аналогичное поведение как и в случае Query для ИБ)public function setAggregationCountFields(array $arAggregationCountFields): self- установка "количественных" полей для агрегацииpublic function setAggregationRangeFields(array $arAggregationCountFields): self- установка "диапазонных" полей для агрегации
Магические методы, для облегчения написания запросов
Встроенные методы позволяют не использовать метод where с 3 аргументами, а пользоваться более удобной и понятной формой задания условия. Все методы принимают строго 2 аргумента - поле фильтрации и значение
@method Query whereEqual(string $strName, mixed $mValue)- условие равенства, вторым аргументом может быть число, строка или массив значений@method Query whereLess(string $strName, float|int $mValue)- условие строго меньше, вторым аргументом может быть только число@method Query whereMore(string $strName, float|int $mValue)- условие строго больше, вторым аргументом может быть только число@method Query whereLessEqual(string $strName, float|int $mValue)- условие меньше или равно, вторым аргументом может быть только число@method Query whereMoreEqual(string $strName, float|int $mValue)- условие больше или равно, вторым аргументом может быть только число@method Query whereNotEqual(string $strName, float|int $mValue)- условие неравнества, вторым аргументом может быть число, строка или массив значений@method Query whereIn(string $strName, array $mValue)- условие равенства массиву, по сути алиас whereEqual, вторым аргументом может быть только массив значений@method Query whereLike(string $strName, mixed $strValue)- условие сравнения по подстроке, вторым аргументом может быть только строка@method Query whereSearch(string $strName, mixed $strValue)- поиск по строке (целому слову), вторым аргументом может быть только строка
В основном, для построения запроса будут использоваться только методы where, setSelect/addSelect, setOrder/addOrder и встроенные магические методы Использование подзапросов Elastic\Model\Filter Для реализации более сложной логики подзапроса изпользуется экземпляр класса Elastic\Model\Filter. Обладает всеми методами построения условий where и дополнительными:
public function setLogicOr(): self- установка работы фильтра в логическое "ИЛИ"public function setLogicAnd(): self- установка работы фильтра в логическое "И"public function getFilterQuery(): array- получение параметров запроса (для дебага)
Пример построения запроса в Database
php
public static function getQuery(?Pagination $obNav = null): Elastic\Model\Query
{
$obQuery = parent::_query();
$obQuery
->setFields(array('JSON'))
->addOrder('NAME', 'desc');
$obQuery->where(
(new Elastic\Model\Filter())
->setLogicOr()
->whereEqual('LEGAL', 377)
->whereEqual('LANG_ID', 4)
);
if ($obNav !== null) {
$iBrandId = $obNav->getParamNumber('brand_id', 0);
if ((int) $iBrandId > 0) {
$obQuery->where('BRAND_ID', '=', $iBrandId);
}
$arSectionIds = $obNav->getParamArray('section_ids', array());
if (count($arSectionIds) > 0) {
$obQuery->whereIn('SECTION_ID', $arSectionIds);
}
$obDate = $obNav->getParam('date_more', null);
if ($obDate !== null) {
$obQuery->whereMoreEqual('DATE', $obDate);
}
$strDescription = $obNav->getParamString('description', '');
if (strlen($strDescription) > 0) {
$obQuery->whereSearch('DESCRIPTION', $strDescription);
}
$strName = $obNav->getParamString('name', '');
if (strlen($strName) > 0) {
$obQuery->whereLike('NAME', $strName);
}
$strSort = $obNav->getParamString('sort', '');
if (strlen($strSort) > 0) {
if ($strSort == "price-asc") {
$obQuery->addOrder('PRICE_MIN', 'ASC');
} elseif ($strSort == "price-desc") {
$obQuery->addOrder('PRICE_MIN', 'DESC');
}
}
}
\utilphp\util::var_dump($obQuery->getQuery()); //Дебаг
return $obQuery;
}Пример поиска
php
$obNav = new Pagination();
$obNav
->setAllRecords(false)
->setPageSize(24)
->setCurrentPage(1)
->setParam('brand_id', 3)
->setParam('section_ids', array(2))
->setParam('sort', 'price-asc');
$obCollection = \Realweb\Api\Module\Catalog\Model\Production\Element\Elastic\Orm\Database::getObjectCollection($obNav);Агрегация данных (Получение данных для смарт фильтра)
Для получения агрегированных данных использется метод getAggregation
php
$obNav = new Pagination();
$obCollection = \Realweb\Api\Module\Catalog\Model\Element\Elastic\Orm\Database::getAggregation($obNav);Метод возвращает коллекцию полей \Realweb\Api\Module\Elastic\Model\Aggregation\Entity Сущность \Realweb\Api\Module\Elastic\Model\Aggregation\Entity обладает следующими методами:
string getName()- код поляstring getType()- тип поля - count (обычные поля с возможностью подсчета количества) или range (с характеристиками min и max, например, цена)public function isCount(): bool- проверка на тип поля countpublic function isRange(): bool- проверка на тип поля rangepublic function getItems(): Elastic\Model\Aggregation\Item\Collection- возвращает коллекцию данных по полю
Данные по полю Elastic\Model\Aggregation\Item\Collection
Коллекция представляет собой коллекцию сущностей \Realweb\Api\Module\Elastic\Model\Aggregation\Item\Entity Сущность имеет встроенные методы:
float|int|string getValue()- получение значение в том виде, в котором оно хранится в индексеint|null getCount()- получение количество документов (записей) в индексе с таким значением. Для полей типа range - всегдаnull
Для получения минимума и максимума значений полей типа range следует использовать метод коллекции \Realweb\Api\Module\Elastic\Model\Aggregation\Item\Collection getMinimum и getMaximum() соответственно.
Пример использования
php
$obNav = new Pagination();
$obCollection = \Realweb\Api\Module\Catalog\Model\Element\Elastic\Orm\Database::getAggregation($obNav);
$iMin = $obCollection->getByKey('PRICE')->getItems()->getMinimum();
$iMax = $obCollection->getByKey('PRICE')->getItems()->getMaximum();