src/module-elasticsuite-catalog/Model/ResourceModel/Category/Fulltext/Collection.php
<?php
/**
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade Smile ElasticSuite to newer
* versions in the future.
*
* @category Smile
* @package Smile\ElasticsuiteCatalog
* @author Romain Ruaud <romain.ruaud@smile.fr>
* @copyright 2020 Smile
* @license Open Software License ("OSL") v. 3.0
*/
namespace Smile\ElasticsuiteCatalog\Model\ResourceModel\Category\Fulltext;
use Smile\ElasticsuiteCore\Search\Adapter\Elasticsuite\Response\QueryResponse;
use Smile\ElasticsuiteCore\Search\Request\QueryInterface;
use Smile\ElasticsuiteCore\Search\RequestInterface;
/**
* Search engine category collection for Autocomplete.
* Basically a copy-pasted version of @see Smile\ElasticsuiteCatalog\Model\ResourceModel\Product\Fulltext\Collection
*
* @codingStandardsIgnoreStart
* @TODO Refactor/Mutualize all copy/pasted methods.
* @codingStandardsIgnoreEnd
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*
* @category Smile
* @package Smile\ElasticsuiteCatalog
* @author Romain Ruaud <romain.ruaud@smile.fr>
*/
class Collection extends \Magento\Catalog\Model\ResourceModel\Category\Collection
{
/**
* @var \Smile\ElasticsuiteCore\Search\Request\Builder
*/
private $requestBuilder;
/**
* @var \Magento\Search\Model\SearchEngine
*/
private $searchEngine;
/**
* @var string
*/
private $searchRequestName;
/**
* @var array
*/
private $filters = [];
/**
* @var QueryInterface[]
*/
private $queryFilters = [];
/**
* @var QueryResponse
*/
private $queryResponse;
/**
* @var string|QueryInterface
*/
private $query;
/**
* @var boolean
*/
private $isSpellchecked = false;
/**
* Collection constructor.
*
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*
* @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory The Entity Factory
* @param \Psr\Log\LoggerInterface $logger The Logger
* @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy Fetch Strategy
* @param \Magento\Framework\Event\ManagerInterface $eventManager Event Manager
* @param \Magento\Eav\Model\Config $eavConfig EAV Configuration
* @param \Magento\Framework\App\ResourceConnection $resource Resource Connection
* @param \Magento\Eav\Model\EntityFactory $eavEntityFactory Entity Factory
* @param \Magento\Eav\Model\ResourceModel\Helper $resourceHelper Resource Helper
* @param \Magento\Framework\Validator\UniversalFactory $universalFactory Universal Factory
* @param \Magento\Store\Model\StoreManagerInterface $storeManager Store Manager
* @param \Smile\ElasticsuiteCore\Search\Request\Builder $requestBuilder Search request builder.
* @param \Magento\Search\Model\SearchEngine $searchEngine Search engine
* @param \Magento\Framework\DB\Adapter\AdapterInterface $connection Db Connection.
* @param string $searchRequestName Search request name.
*/
public function __construct(
\Magento\Framework\Data\Collection\EntityFactory $entityFactory,
\Psr\Log\LoggerInterface $logger,
\Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy,
\Magento\Framework\Event\ManagerInterface $eventManager,
\Magento\Eav\Model\Config $eavConfig,
\Magento\Framework\App\ResourceConnection $resource,
\Magento\Eav\Model\EntityFactory $eavEntityFactory,
\Magento\Eav\Model\ResourceModel\Helper $resourceHelper,
\Magento\Framework\Validator\UniversalFactory $universalFactory,
\Magento\Store\Model\StoreManagerInterface $storeManager,
\Smile\ElasticsuiteCore\Search\Request\Builder $requestBuilder,
\Magento\Search\Model\SearchEngine $searchEngine,
\Magento\Framework\DB\Adapter\AdapterInterface $connection = null,
$searchRequestName = 'category_search_container'
) {
parent::__construct(
$entityFactory,
$logger,
$fetchStrategy,
$eventManager,
$eavConfig,
$resource,
$eavEntityFactory,
$resourceHelper,
$universalFactory,
$storeManager,
$connection
);
$this->requestBuilder = $requestBuilder;
$this->searchEngine = $searchEngine;
$this->searchRequestName = $searchRequestName;
}
/**
* {@inheritDoc}
*/
public function getSize()
{
if ($this->_totalRecords === null) {
$this->loadItemCounts();
}
return $this->_totalRecords;
}
/**
* {@inheritDoc}
*/
public function setOrder($attribute, $dir = self::SORT_ORDER_DESC)
{
$this->_orders[$attribute] = $dir;
return $this;
}
/**
* {@inheritDoc}
*/
public function addFieldToFilter($field, $condition = null)
{
$this->filters[$field] = $condition;
return $this;
}
/**
* {@inheritDoc}
*/
public function addAttributeToSort($attribute, $dir = self::SORT_ORDER_ASC)
{
return $this->setOrder($attribute, $dir);
}
/**
* Append a prebuilt (QueryInterface) query filter to the collection.
*
* @param QueryInterface $queryFilter Query filter.
*
* @return $this
*/
public function addQueryFilter(QueryInterface $queryFilter)
{
$this->queryFilters[] = $queryFilter;
return $this;
}
/**
* Set search query filter in the collection.
*
* @param string|QueryInterface $query Search query text.
*
* @return \Smile\ElasticsuiteCatalog\Model\ResourceModel\Product\Fulltext\Collection
*/
public function setSearchQuery($query)
{
$this->query = $query;
return $this;
}
/**
* Add search query filter.
*
* @deprecated Replaced by setSearchQuery
*
* @param string $query Search query text.
*
* @return \Smile\ElasticsuiteCatalog\Model\ResourceModel\Product\Fulltext\Collection
*/
public function addSearchFilter($query)
{
return $this->setSearchQuery($query);
}
/**
* Return field faceted data from faceted search result.
*
* @param string $field Facet field.
*
* @return array
*/
public function getFacetedData($field)
{
$this->_renderFilters();
$result = [];
$aggregations = $this->queryResponse->getAggregations();
$bucket = $aggregations->getBucket($field);
if ($bucket) {
foreach ($bucket->getValues() as $value) {
$metrics = $value->getMetrics();
$result[$value->getValue()] = $metrics['count'];
}
}
return $result;
}
/**
* Indicates if the collection is spellchecked or not.
*
* @return boolean
*/
public function isSpellchecked()
{
return $this->isSpellchecked;
}
/**
* @SuppressWarnings(PHPMD.CamelCaseMethodName)
*
* {@inheritdoc}
*/
protected function _renderFiltersBefore()
{
$searchRequest = $this->prepareRequest();
$this->queryResponse = $this->searchEngine->search($searchRequest);
// Update the item count.
$this->_totalRecords = $this->queryResponse->count();
// Filter search results. The pagination has to be resetted since it is managed by the engine itself.
$docIds = array_map(
function (\Magento\Framework\Api\Search\Document $doc) {
return (int) $doc->getId();
},
$this->queryResponse->getIterator()->getArrayCopy()
);
if (empty($docIds)) {
$docIds[] = 0;
}
$this->getSelect()->where('e.entity_id IN (?)', ['in' => $docIds]);
$this->_pageSize = false;
$this->isSpellchecked = $searchRequest->isSpellchecked();
parent::_renderFiltersBefore();
}
/**
* @SuppressWarnings(PHPMD.CamelCaseMethodName)
*
* {@inheritDoc}
*/
protected function _afterLoad()
{
// Resort items according the search response.
$originalItems = $this->_items;
$this->_items = [];
foreach ($this->queryResponse->getIterator() as $document) {
$documentId = $document->getId();
if (isset($originalItems[$documentId])) {
$originalItems[$documentId]->setDocumentScore($document->getScore());
$originalItems[$documentId]->setDocumentSource($document->getSource());
$this->_items[$documentId] = $originalItems[$documentId];
}
}
return parent::_afterLoad();
}
/**
* Prepare the search request before it will be executed.
*
* @return RequestInterface
*/
private function prepareRequest()
{
// Store id and request name.
$storeId = $this->getStoreId();
$searchRequestName = $this->searchRequestName;
// Pagination params.
$size = $this->_pageSize ? $this->_pageSize : 20;
$from = $size * (max(1, $this->_curPage) - 1);
// Setup sort orders.
$sortOrders = $this->prepareSortOrders();
$searchRequest = $this->requestBuilder->create(
$storeId,
$searchRequestName,
$from,
$size,
$this->query,
$sortOrders,
$this->filters,
$this->queryFilters
);
return $searchRequest;
}
/**
* Prepare sort orders for the request builder.
*
* @return array()
*/
private function prepareSortOrders()
{
$sortOrders = [];
foreach ($this->_orders as $attribute => $direction) {
$sortParams = ['direction' => $direction];
$sortField = $attribute;
$sortOrders[$sortField] = $sortParams;
}
return $sortOrders;
}
/**
* Load items count :
* - collection size
*
* @return void
*/
private function loadItemCounts()
{
$storeId = $this->getStoreId();
$requestName = $this->searchRequestName;
$searchRequest = $this->requestBuilder->create(
$storeId,
$requestName,
0,
0,
$this->query,
[],
$this->filters,
$this->queryFilters
);
$searchResponse = $this->searchEngine->search($searchRequest);
$this->_totalRecords = $searchResponse->count();
}
}