Skip to content

Commit

Permalink
Merge pull request #2415 from magento-performance/MAGETWO-90564
Browse files Browse the repository at this point in the history
[PERFORMANCE] Catalog Rule indexer matching mechanism optimization
  • Loading branch information
duhon authored Apr 20, 2018
2 parents a60b822 + ee7576e commit 6a69901
Show file tree
Hide file tree
Showing 26 changed files with 4,153 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\ConditionProcessor\ConditionBuilder;

use Magento\Framework\Api\SearchCriteria\CollectionProcessor\ConditionProcessor\CustomConditionInterface;
use Magento\Framework\Api\Filter;
use Magento\Catalog\Model\ResourceModel\Product\Collection;
use Magento\Catalog\Model\ResourceModel\Eav\Attribute;

/**
* Based on Magento\Framework\Api\Filter builds condition
* that can be applied to Catalog\Model\ResourceModel\Product\Collection
* to filter products that has specific value for EAV attribute
*/
class EavAttributeCondition implements CustomConditionInterface
{
/**
* @var \Magento\Framework\App\ResourceConnection
*/
private $resourceConnection;

/**
* @var \Magento\Eav\Model\Config
*/
private $eavConfig;

/**
* @param \Magento\Eav\Model\Config $eavConfig
* @param \Magento\Framework\App\ResourceConnection $resourceConnection
*/
public function __construct(
\Magento\Eav\Model\Config $eavConfig,
\Magento\Framework\App\ResourceConnection $resourceConnection
) {
$this->eavConfig = $eavConfig;
$this->resourceConnection = $resourceConnection;
}

/**
* Build condition to filter product collection by EAV attribute
*
* @param Filter $filter
* @return string
* @throws \DomainException
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function build(Filter $filter): string
{
$attribute = $this->getAttributeByCode($filter->getField());
$tableAlias = 'ca_' . $attribute->getAttributeCode();

$conditionType = $this->mapConditionType($filter->getConditionType());
$conditionValue = $this->mapConditionValue($conditionType, $filter->getValue());

// NOTE: store scope was ignored intentionally to perform search across all stores
$attributeSelect = $this->resourceConnection->getConnection()
->select()
->from(
[$tableAlias => $attribute->getBackendTable()],
$tableAlias . '.' . $attribute->getEntityIdField()
)->where(
$this->resourceConnection->getConnection()->prepareSqlCondition(
$tableAlias . '.' . $attribute->getIdFieldName(),
['eq' => $attribute->getAttributeId()]
)
)->where(
$this->resourceConnection->getConnection()->prepareSqlCondition(
$tableAlias . '.value',
[$conditionType => $conditionValue]
)
);

return $this->resourceConnection
->getConnection()
->prepareSqlCondition(
Collection::MAIN_TABLE_ALIAS . '.' . $attribute->getEntityIdField(),
[
'in' => $attributeSelect
]
);
}

/**
* @param string $field
* @return Attribute
* @throws \Magento\Framework\Exception\LocalizedException
*/
private function getAttributeByCode(string $field): Attribute
{
return $this->eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $field);
}

/**
* Map equal and not equal conditions to in and not in
*
* @param string $conditionType
* @return mixed
*/
private function mapConditionType(string $conditionType): string
{
$conditionsMap = [
'eq' => 'in',
'neq' => 'nin'
];

return isset($conditionsMap[$conditionType]) ? $conditionsMap[$conditionType] : $conditionType;
}

/**
* Wraps value with '%' if condition type is 'like' or 'not like'
*
* @param string $conditionType
* @param string $conditionValue
* @return string
*/
private function mapConditionValue(string $conditionType, string $conditionValue): string
{
$conditionsMap = ['like', 'nlike'];

if (in_array($conditionType, $conditionsMap)) {
$conditionValue = '%' . $conditionValue . '%';
}

return $conditionValue;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\ConditionProcessor\ConditionBuilder;

use Magento\Framework\Api\Filter;
use Magento\Framework\Api\SearchCriteria\CollectionProcessor\ConditionProcessor\CustomConditionInterface;
use Magento\Catalog\Model\ResourceModel\Eav\Attribute;

/**
* Creates appropriate condition builder based on filter field
* - native attribute condition builder if filter field is native attribute in product
* - eav condition builder if filter field is eav attribute
*/
class Factory
{
/**
* @var \Magento\Eav\Model\Config
*/
private $eavConfig;

/**
* @var \Magento\Catalog\Model\ResourceModel\Product
*/
private $productResource;

/**
* @var CustomConditionInterface
*/
private $eavAttributeConditionBuilder;

/**
* @var CustomConditionInterface
*/
private $nativeAttributeConditionBuilder;

/**
* @param \Magento\Eav\Model\Config $eavConfig
* @param \Magento\Catalog\Model\ResourceModel\Product $productResource
* @param CustomConditionInterface $eavAttributeConditionBuilder
* @param CustomConditionInterface $nativeAttributeConditionBuilder
*/
public function __construct(
\Magento\Eav\Model\Config $eavConfig,
\Magento\Catalog\Model\ResourceModel\Product $productResource,
CustomConditionInterface $eavAttributeConditionBuilder,
CustomConditionInterface $nativeAttributeConditionBuilder
) {
$this->eavConfig = $eavConfig;
$this->productResource = $productResource;
$this->eavAttributeConditionBuilder = $eavAttributeConditionBuilder;
$this->nativeAttributeConditionBuilder = $nativeAttributeConditionBuilder;
}

/**
* Decides which condition builder should be used for passed filter
* can be either EAV attribute builder or native attribute builder
* "native" attribute means attribute that is in catalog_product_entity table
*
* @param Filter $filter
* @return CustomConditionInterface
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function createByFilter(Filter $filter): CustomConditionInterface
{
$attribute = $this->getAttributeByCode($filter->getField());

if ($attribute->getBackendTable() === $this->productResource->getEntityTable()) {
return $this->nativeAttributeConditionBuilder;
}

return $this->eavAttributeConditionBuilder;
}

/**
* @param string $field
* @return \Magento\Catalog\Model\ResourceModel\Eav\Attribute
* @throws \Magento\Framework\Exception\LocalizedException
*/
private function getAttributeByCode(string $field): Attribute
{
return $this->eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $field);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\ConditionProcessor\ConditionBuilder;

use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Framework\Api\SearchCriteria\CollectionProcessor\ConditionProcessor\CustomConditionInterface;
use Magento\Framework\Api\Filter;
use Magento\Catalog\Model\ResourceModel\Product\Collection;

/**
* Based on Magento\Framework\Api\Filter builds condition
* that can be applied to Catalog\Model\ResourceModel\Product\Collection
* to filter products that has specific value for their native attribute
*/
class NativeAttributeCondition implements CustomConditionInterface
{
/**
* @var \Magento\Framework\App\ResourceConnection
*/
private $resourceConnection;

/**
* @param \Magento\Framework\App\ResourceConnection $resourceConnection
*/
public function __construct(
\Magento\Framework\App\ResourceConnection $resourceConnection
) {
$this->resourceConnection = $resourceConnection;
}

/**
* Build condition to filter product collection by product native attribute
* "native" attribute means attribute that is in catalog_product_entity table
*
* @param Filter $filter
* @return string
* @throws \DomainException
*/
public function build(Filter $filter): string
{
$conditionType = $this->mapConditionType($filter->getConditionType(), $filter->getField());
$conditionValue = $this->mapConditionValue($conditionType, $filter->getValue());

return $this->resourceConnection
->getConnection()
->prepareSqlCondition(
Collection::MAIN_TABLE_ALIAS . '.' . $filter->getField(),
[
$conditionType => $conditionValue
]
);
}

/**
* Map equal and not equal conditions to in and not in
*
* @param string $conditionType
* @param string $field
* @return mixed
*/
private function mapConditionType(string $conditionType, string $field): string
{
if (strtolower($field) === ProductInterface::SKU) {
$conditionsMap = [
'eq' => 'like',
'neq' => 'nlike'
];
} else {
$conditionsMap = [
'eq' => 'in',
'neq' => 'nin'
];
}

return isset($conditionsMap[$conditionType]) ? $conditionsMap[$conditionType] : $conditionType;
}

/**
* Wraps value with '%' if condition type is 'like' or 'not like'
*
* @param string $conditionType
* @param string $conditionValue
* @return string
*/
private function mapConditionValue(string $conditionType, string $conditionValue): string
{
$conditionsMap = ['like', 'nlike'];

if (in_array($conditionType, $conditionsMap)) {
$conditionValue = '%' . $conditionValue . '%';
}

return $conditionValue;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\ConditionProcessor;

use Magento\Framework\Api\SearchCriteria\CollectionProcessor\ConditionProcessor\CustomConditionInterface;
use Magento\Framework\Api\Filter;
use Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\ConditionProcessor\ConditionBuilder\Factory;

/**
* Default condition builder for Catalog\Model\ResourceModel\Product\Collection
*/
class DefaultCondition implements CustomConditionInterface
{
/**
* @var Factory
*/
private $conditionBuilderFactory;

/**
* @param Factory $conditionBuilderFactory
*/
public function __construct(
Factory $conditionBuilderFactory
) {
$this->conditionBuilderFactory = $conditionBuilderFactory;
}

/**
* Builds condition to filter product collection either by EAV or by native attribute
*
* @param Filter $filter
* @return string
*/
public function build(Filter $filter): string
{
$filterBuilder = $this->conditionBuilderFactory->createByFilter($filter);

return $filterBuilder->build($filter);
}
}
Loading

0 comments on commit 6a69901

Please sign in to comment.