Files

266 lines
7.9 KiB
PHP
Executable File

<?php
/**
* @author Amasty Team
* @copyright Copyright (c) Amasty (https://www.amasty.com)
* @package Elastic Search Base for Magento 2
*/
namespace Amasty\ElasticSearch\Model\Indexer\RelevanceRule;
use Amasty\ElasticSearch\Api\Data\RelevanceRuleInterface;
use Amasty\ElasticSearch\Api\RelevanceRuleRepositoryInterface;
use Amasty\ElasticSearch\Model\ResourceModel\RelevanceRule\CollectionFactory as RelevanceRuleCollectionFactory;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Store\Model\StoreManagerInterface;
use Psr\Log\LoggerInterface;
/**
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class IndexBuilder
{
public const SECONDS_IN_DAY = 86400;
public const PRODUCT_ID = 'product_id';
public const TABLE_NAME = 'amasty_elastic_relevance_rule_index_tmp';
public const MAX_INT_MYSQL = 4294967294;
/**
* @var ResourceConnection
*/
private $resource;
/**
* @var StoreManagerInterface
*/
private $storeManager;
/**
* @var RelevanceRuleRepositoryInterface
*/
private $ruleRepository;
/**
* @var ProductRepositoryInterface
*/
private $productRepository;
/**
* @var SearchCriteriaBuilder
*/
private $searchCriteriaBuilder;
/**
* @var LoggerInterface
*/
private $logger;
/**
* @var int
*/
private $batchCount;
/**
* @var RelevanceRuleCollectionFactory
*/
private $relevanceRuleCollectionFactory;
public function __construct(
ResourceConnection $resource,
StoreManagerInterface $storeManager,
RelevanceRuleRepositoryInterface $ruleRepository,
ProductRepositoryInterface $productRepository,
SearchCriteriaBuilder $searchCriteriaBuilder,
LoggerInterface $logger,
RelevanceRuleCollectionFactory $relevanceRuleCollectionFactory,
$batchCount = 1000
) {
$this->resource = $resource;
$this->storeManager = $storeManager;
$this->ruleRepository = $ruleRepository;
$this->productRepository = $productRepository;
$this->searchCriteriaBuilder = $searchCriteriaBuilder;
$this->logger = $logger;
$this->batchCount = $batchCount;
$this->relevanceRuleCollectionFactory = $relevanceRuleCollectionFactory;
}
/**
* Reindex by product Ids
*
* @param array $ids
* @throws LocalizedException
*/
public function reindexByIds(array $ids): void
{
try {
$this->cleanByIds($ids);
$products = $this->getProducts($ids);
foreach ($this->getActiveRules() as $rule) {
foreach ($products as $product) {
$this->applyRule($rule, $product);
}
}
} catch (\Exception $e) {
$this->critical($e);
throw new LocalizedException(
__("Amasty ElasticSearch Relevance rule indexing failed. See details in exception log.")
);
}
}
public function getProducts(array $productIds): iterable
{
$this->searchCriteriaBuilder->addFilter('entity_id', $productIds, 'in');
$searchCriteria = $this->searchCriteriaBuilder->create();
return $this->productRepository->getList($searchCriteria)->getItems();
}
public function reindexFull(): void
{
try {
$this->resource->getConnection()->truncateTable($this->getIndexTable());
foreach ($this->getActiveRules() as $rule) {
$this->doReindex($rule);
}
} catch (\Exception $e) {
$this->critical($e);
throw new LocalizedException(
__("Relevance rule indexing failed. See details in exception log.")
);
}
}
public function reindexByRuleIds(array $ids): void
{
$table = $this->getIndexTable();
$relevanceRuleCollection = $this->relevanceRuleCollectionFactory->create();
$relevanceRuleCollection->addFieldToFilter(RelevanceRuleInterface::RULE_ID, $ids);
$connection = $this->resource->getConnection();
$connection->delete(
$table,
[
$connection->prepareSqlCondition(RelevanceRuleInterface::RULE_ID, ['in' => $ids])
]
);
try {
foreach ($relevanceRuleCollection as $relevanceRule) {
try {
$this->doReindex($relevanceRule);
} catch (NoSuchEntityException $e) {
null;// do nothing
}
}
} catch (\Exception $e) {
$this->critical($e);
throw new LocalizedException(
__("Relevance rule indexing failed. See details in exception log.")
);
}
}
private function doReindex(RelevanceRuleInterface $rule): void
{
if ($rule->isConditionEmpty()) {
return;
}
$rows = [];
$size = 0;
$productIds = $rule->getCatalogRule()->getMatchingProductIds();
foreach ($productIds as $productId => $validationByWebsite) {
if (empty($validationByWebsite[$rule->getWebsiteId()])) {
continue;
}
$rows[] = $this->generateIndexData($rule, $productId);
$size++;
if ($size == $this->batchCount) {
$this->resource->getConnection()->insertMultiple($this->getIndexTable(), $rows);
$rows = [];
$size = 0;
}
}
if (!empty($rows)) {
$this->resource->getConnection()->insertMultiple($this->getIndexTable(), $rows);
}
}
private function applyRule(RelevanceRuleInterface $rule, ProductInterface $product): IndexBuilder
{
$table = $this->getIndexTable();
$connection = $this->resource->getConnection();
$connection->delete(
$table,
[
$connection->prepareSqlCondition(RelevanceRuleInterface::RULE_ID, ['eq' => $rule->getId()]),
$connection->prepareSqlCondition(self::PRODUCT_ID, ['eq' => $product->getId()])
]
);
if ($rule->isConditionEmpty() || !$rule->getCatalogRule()->validate($product)) {
return $this;
}
try {
$rows = [$this->generateIndexData($rule, $product->getId())];
$this->resource->getConnection()->insertMultiple($table, $rows);
} catch (\Exception $e) {
throw $e;
}
return $this;
}
private function generateIndexData(RelevanceRuleInterface $rule, int $productId): array
{
return [
RelevanceRuleInterface::RULE_ID => $rule->getId(),
RelevanceRuleInterface::WEBSITE_ID => $rule->getWebsiteId(),
RelevanceRuleInterface::MULTIPLIER => $rule->getMultiplier(),
self::PRODUCT_ID => $productId
];
}
private function cleanByIds(array $productIds): void
{
$select = $this->resource->getConnection()
->select()
->from($this->getIndexTable(), self::PRODUCT_ID)
->distinct()
->where(self::PRODUCT_ID . ' IN (?)', $productIds);
$query = $this->resource->getConnection()->deleteFromSelect($select, $this->getIndexTable());
$this->resource->getConnection()->query($query);
}
private function getIndexTable(): string
{
return $this->resource->getTableName(self::TABLE_NAME);
}
private function getActiveRules(): iterable
{
return $this->ruleRepository->getActiveRules();
}
private function critical(\Exception $exception): void
{
$this->logger->critical($exception);
}
}