Magento 2社区版仅支持MySQL搜索引擎,但有些项目需要更好或更可调的搜索引擎才能提高销售额或转换率。
在这种情况下,我们正在使用SolrElasticsearch搜索引擎。

在这篇博客中,我们将创建一个框架代码或粗略的示例,我们将介绍实现其他搜索引擎(如Solr或Elasticsearch)的主要类和方法。如果您查看Magento 2后台,您可以在这个位置配置搜索引擎: Stores -> Configuration -> Catalog -> Catalog Search,然后下拉“搜索引擎”

在下拉列表中,您将注意到您只有MySQL引擎,我们的第一步是在此下拉列表中添加标签为“Solr”的附加选项。那么,让我们开始吧。

按照惯例,你需要创建一个Magento 2模块在etc文件夹中的模块中,您需要使用下面的代码创建di.xml文件:

    <type name="Magento\Search\Model\Adminhtml\System\Config\Source\Engine">
        <arguments>
            <argument name="engines" xsi:type="array">
                <item name="solr" xsi:type="string">Solr</item>
            </argument>
        </arguments>
    </type>

使用此代码,我们在下拉列表中添加了一个新选项,其选项名称为“Solr”。如果您正确创建并清理了Magento缓存,您将能够在下拉列表中看到它,其中会有一个新选项“Solr”,这表示您已正确添加它。

在下一步中,我们将从php类开始,这些类负责将数据索引到搜索服务器。

首先,我们应该实现Engine类,在di.xml中加入下面代码:

    <type name="Magento\CatalogSearch\Model\ResourceModel\EngineProvider">
        <arguments>
            <argument name="engines" xsi:type="array">
                <item name="solr" xsi:type="string">Magease\Solr\Model\ResourceModel\Engine</item>
            </argument>
        </arguments>
    </type> 

您可以看到我们为“Magease\Solr\Model\ResourceModel\Engine” 引入了我们自己的Engine类,引擎类负责在数据进入我们的indexerHandler类(solr服务器之前的最后一个端点)之前准备数据,而Engine类必须实现:\Magento\CatalogSearch\Model\ResourceModel\EngineInterface

接口类包含以下四种方法:
processAttributeValue准备要存储在solr索引中的属性值
getAllowedVisibility检索当前引擎允许的可见性值
allowAdvancedIndex定义当前搜索引擎是否支持高级索引
prepareEntityIndex将索引数组准备为由分隔符粘合的字符串

这些方法是必需的,必须在Engine类中实现。为了更好地理解,您可以在类似的MySQL原生类中检查/比较逻辑:Magento\CatalogSearch\Model\ResourceModel\Engine

我们的骨架类示例如下:

<?php
namespace Magease\Solr\Model\ResourceModel;
 
use Magento\CatalogSearch\Model\ResourceModel\EngineInterface;
 
 
class Engine implements EngineInterface
{
 
    protected $catalogProductVisibility;
    private $indexScopeResolver;
 
    public function __construct(
        \Magento\Catalog\Model\Product\Visibility $catalogProductVisibility,
        \Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver $indexScopeResolver
    ) {
        $this->catalogProductVisibility = $catalogProductVisibility;
        $this->indexScopeResolver = $indexScopeResolver;
    }
 
    public function getAllowedVisibility()
    {
        return $this->catalogProductVisibility->getVisibleInSiteIds();
    }
 
    public function allowAdvancedIndex()
    {
        return false;
    }
 
    public function processAttributeValue($attribute, $value)
    {
        return $value;
    }
 
    public function prepareEntityIndex($index, $separator = ' ')
    {
        return $index;
    }
 
    public function isAvailable()
    {
        return true;
    }
}

下一步是创建名为“Magease\Solr\Model\Indexer\IndexerHandler”的indexerHandler,它必须实现Magento\Framework\Indexer\SaveHandler\IndexerInterface
要实现IndexerHandler,您应该在di.xml文件中添加下一个代码:

   <type name="Magento\CatalogSearch\Model\Indexer\IndexerHandlerFactory">
        <arguments>
            <argument name="handlers" xsi:type="array">
                <item name="solr" xsi:type="string">Magease\Solr\Model\Indexer\IndexerHandler</item>
            </argument>
        </arguments>
    </type>

如果打开IndexerInterface,您将看到四个必须实现的方法:

saveIndex将实体数据添加到索引
deleteIndex从索引中删除实体数据
cleanIndex从索引中删除所有数据
isAvailable定义引擎是否可用(您可以实现ping到solr)服务器和检查它是否实时)。

我们的IndexerHandler骨架类示例如下:

<?php
namespace Magease\Solr\Model\Indexer;
 
use Magento\Eav\Model\Config;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\DB\Adapter\AdapterInterface;
use Magento\Framework\Indexer\SaveHandler\IndexerInterface;
use Magento\Framework\Indexer\IndexStructureInterface;
use Magento\Framework\Search\Request\Dimension;
use Magento\Framework\Search\Request\IndexScopeResolverInterface;
use Magento\Framework\Indexer\SaveHandler\Batch;
use Magento\Framework\Indexer\ScopeResolver\IndexScopeResolver;
 
class IndexerHandler implements IndexerInterface
{
    private $indexStructure;
 
    private $data;
 
    private $fields;
 
    private $resource;
 
    private $batch;
 
    private $eavConfig;
 
    private $batchSize;
 
    private $indexScopeResolver;
 
    public function __construct(
        Batch $batch,
        array $data,
        $batchSize = 50
    ) {
        $this->batch = $batch;
        $this->data = $data;
        $this->batchSize = $batchSize;
    }
 
    public function saveIndex($dimensions, \Traversable $documents)
    {
        foreach ($this->batch->getItems($documents, $this->batchSize) as $batchDocuments) {
 
        }
    }
 
    public function deleteIndex($dimensions, \Traversable $documents)
    {
        foreach ($this->batch->getItems($documents, $this->batchSize) as $batchDocuments) {
 
        }
    }
 
    public function cleanIndex($dimensions)
    {
 
    }
 
    public function isAvailable()
    {
        return true;
    }
}

在这些方法中,您应该实现Solr PHP客户端,它将对Solr服务器进行列表操作,常用的是Solarium PHP客户端

通过此步骤,我们结束了向搜索服务器索引数据的过程。

现在您可以检查您的索引器是否可以使用下一个命令(在magento admin将搜索引擎设置为SOLR之前):

php /bin/magento indexer:reindex catalogsearch_fulltext

在接下来的最后一步中,我们将解释如何在Magento 2前端实现新的搜索引擎。此外,我们必须修改di.xml并添加下面代码:

<type name="Magento\Search\Model\AdapterFactory">
    <arguments>
        <argument name="adapters" xsi:type="array">
            <item name="solr" xsi:type="string">Magease\Solr\SearchAdapter\Adapter</item>
        </argument>
    </arguments>
</type>

我们的新适配器是Magease\Solr\SearchAdapter\Adapter适配器类应该实现Magento\Framework\Search\AdapterInterface在我们的适配器中,我们必须实现方法查询 - 此方法接受查询请求并处理它,看看我们的例子,一切都会更清晰。

<?php
namespace Magease\Solr\SearchAdapter;
 
use Magento\Framework\Search\AdapterInterface;
use Magento\Framework\Search\RequestInterface;
use Magento\Framework\Search\Response\QueryResponse;
use Magease\Solr\SearchAdapter\Aggregation\Builder;
 
 
class Adapter implements AdapterInterface
{
    protected $responseFactory;
 
    protected $connectionManager;
 
    protected $aggregationBuilder;
 
    public function __construct(
        ResponseFactory $responseFactory,
        Builder $aggregationBuilder,
        ConnectionManager $connectionManager
    ) {
        $this->responseFactory = $responseFactory;
        $this->aggregationBuilder = $aggregationBuilder;
        $this->connectionManager = $connectionManager;
 
    }
 
    /**
     * @param RequestInterface $request
     * @return QueryResponse
     */
    public function query(RequestInterface $request)
    {
        $client = $this->getConnection();
        $documents = [];
 
        $documents[1007] = array('entity_id'=>'1007', 'score'=>46.055);
        $documents[1031] = array('entity_id'=>'1031', 'score'=>45.055);
        $documents[1120] = array('entity_id'=>'1120', 'score'=>44.055);
 
        $aggregations = $this->aggregationBuilder->build($request, $documents);
 
        $response = [
            'documents' => $documents,
            'aggregations' => $aggregations,
        ];
        return $this->responseFactory->create($response);
    }
 
    public function getConnection(){
        return $this->connectionManager->getConnection();
    }
}

在我们的演示适配器类中,我们从我们的数据库产品ID中硬编码产品entity_ids:1007,1031,1120,仅用于测试目的。