First, install the Magento module:
composer require ph2m/magento2-elasticsearch
You need to install the analysis-icu plugin for ElasticSearch.
You have to lock the Elasticsearch configurations:
php bin/magento config:set --lock catalog/search/elasticsearch7_server_hostname <elasticsearch_hostname>
php bin/magento config:set --lock catalog/search/elasticsearch7_server_port <elasticsearch_port>
php bin/magento config:set --lock catalog/search/elasticsearch7_index_prefix <elasticsearch_index_prefix>
php bin/magento config:set --lock catalog/search/elasticsearch7_enable_auth <elasticsearch_enable_auth>
php bin/magento config:set --lock catalog/search/elasticsearch7_username <elasticsearch_username>
php bin/magento config:set --lock catalog/search/elasticsearch7_password <elasticsearch_password>
Copy file es.php.sample
to your Magento pub
directory and customize it if needed.
cp vendor/ph2m/magento2-elasticsearch/etc/es.php.sample pub/es.php
Update your Magento_Theme/templates/html/header.phtml
file to include the search form.
<?php echo $block->getChildHtml('ph2m.elasticsearch.search.form') ?>
You can customize the template file PH2M_Elasticsearch::search/form.phtml
in your theme.
After installation, reindex.
php bin/magento indexer:reindex
Let's imagine you have a module Vendor_Brand and you want to add brands to the search.
Update the di.xml
to add the field mapper:
<type name="Magento\Elasticsearch\Model\Adapter\FieldMapper\FieldMapperResolver">
<arguments>
<argument name="fieldMappers" xsi:type="array">
<item name="brand" xsi:type="string">Vendor\Brand\Model\Adapter\FieldMapper\BrandFieldMapper</item>
</argument>
</arguments>
</type>
Create the field mapper class:
<?php
declare(strict_types=1);
namespace Vendor\Brand\Model\Adapter\FieldMapper;
use Magento\Elasticsearch\Model\Adapter\FieldMapperInterface;
class BrandFieldMapper implements FieldMapperInterface
{
public function getFieldName($attributeCode, $context = [])
{
return $attributeCode;
}
public function getAllAttributesTypes($context = [])
{
return [
'name' => [
'type' => 'text',
'fields' => [
'keyword' => [
'type' => 'keyword',
],
]
],
'url_key' => [
'type' => 'text',
],
];
}
}
Declare a new indexer in etc/indexer.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Indexer/etc/indexer.xsd">
<indexer id="brandsearch_fulltext" view_id="brandsearch_fulltext" class="Vendor\Brand\Model\Indexer\Fulltext">
<title translate="true">Brand Search</title>
<description translate="true">Rebuild Brand fulltext search index</description>
</indexer>
</config>
Declare the view in etc/mview.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Mview/etc/mview.xsd">
<view id="brandsearch_fulltext" class="Vendor\Brand\Model\Indexer\Fulltext" group="indexer">
<subscriptions>
<table name="brand" entity_column="brand_id"/>
</subscriptions>
</view>
</config>
Create the indexer class Vendor\Brand\Model\Indexer\Fulltext
:
<?php
declare(strict_types=1);
namespace Vendor\Brand\Model\Indexer;
use Magento\AdvancedSearch\Model\Client\ClientResolver;
use Magento\Elasticsearch\Model\Adapter\Elasticsearch;
use Magento\Elasticsearch\Model\Adapter\Index\IndexNameResolver;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Vendor\Brand\Api\Data\BrandInterface;
use Vendor\Brand\Query\Brand\GetListQuery;
use Psr\Log\LoggerInterface;
class Fulltext implements \Magento\Framework\Indexer\ActionInterface, \Magento\Framework\Mview\ActionInterface
{
public function __construct(
protected ClientResolver $clientResolver,
protected GetListQuery $getListQuery,
protected SearchCriteriaBuilder $searchCriteriaBuilder,
protected Elasticsearch $elasticsearchAdapter,
protected LoggerInterface $logger,
protected IndexNameResolver $indexNameResolver
) {
}
public function execute($ids)
{
$this->executeList($ids);
}
public function executeFull(): void
{
$this->elasticsearchAdapter->cleanIndex(1, 'brand');
$this->executeList([]);
$this->elasticsearchAdapter->updateAlias(1, 'brand');
}
public function executeList(array $ids): void
{
$searchCriteria = $this->searchCriteriaBuilder;
if (!empty($ids)) {
$searchCriteria->addFilter(BrandInterface::BRAND_ID, $ids, 'in');
}
$brands = $this->getListQuery->execute($searchCriteria->create())->getItems();
$brandsToReindex = [];
foreach ($brands as $brand) {
$brandsToReindex[] = [
'name' => $brand->getName(),
'url_key' => $brand->getUrlKey(),
];
}
try {
$this->elasticsearchAdapter->addDocs($brandsToReindex, 1, 'brand');
} catch (\Throwable $e) {
$this->logger->error($e->getMessage());
}
}
public function executeRow($id)
{
$this->executeList([$id]);
}
}
Add a save_after
observer in etc/events.xml
:
<event name="brand_model_save_after">
<observer name="brand_model_save_after" instance="Vendor\Brand\Observer\SaveAfter" />
</event>
And the corresponding class:
<?php declare(strict_types=1);
namespace Vendor\Brand\Observer;
use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\Indexer\StateInterface;
use Magento\Indexer\Model\IndexerFactory;
class SaveAfter implements ObserverInterface
{
public function __construct(
protected IndexerFactory $indexerFactory,
protected \Vendor\Brand\Model\Indexer\Fulltext $fulltextIndexer
) {
}
public function execute(Observer $observer): void
{
$index = $this->indexerFactory->create()->load('brandsearch_fulltext');
if ($index->isScheduled()) {
$index->invalidate();
} else {
$this->fulltextIndexer->executeRow($observer->getData('object')->getBrandId());
$state = $index->getState();
$state->setStatus(StateInterface::STATUS_VALID);
$state->save();
$index->setState($state);
}
}
}
You can add some synonyms in Stores > Configuration > Catalog > Catalog > Catalog Search > Search synonyms. Synonyms must be separated by commas, enter one synonyms group by line.
This module is designed for Hyvä out of the box.