Skip to content

Модуль для работы с ElasticSearch. realweb.api.elastic

Совместимость

realweb.api v.6

Устаревшая версия модуля

realweb.api.elasticsearch

Модуль позволяет индексировать любые элементы на сайте и быстро искать по ним. Модуль предоставляет 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\DataManager
  • public static function getAggregationCountFields(): array; - массив "количественных" полей индекса - списки выбора, чекбоксы и т.п.
  • public static function getAggregationRangeFields(): array; - массив "диапазонных" полей индекса - цены и т.п.
  • public static function getCollection(): string; - класс коллекции сущностей, наследуется от Elastic\Model\Orm\Collection
  • public 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 - алиас setFields
  • public function addSelect(string $strField): self - алиас addField
  • public function where(...$arVariables): self - метод задания условия фильтрации, может принимать либо 3 аргумента либо 1 3 аргумента 1 аргумент - название поля 2 аргумент - операция условия (=, >, <, >=, <=, !=) 3 аргумент - значение 1 аргумент - подзапрос, экземпляр класса Elastic\Model\Filter
  • public 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 - проверка на тип поля count
  • public function isRange(): bool - проверка на тип поля range
  • public 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();

Репозиторий

Репозиторий модуля