Skip to content

Commit 6a69901

Browse files
authored
Merge pull request #2415 from magento-performance/MAGETWO-90564
[PERFORMANCE] Catalog Rule indexer matching mechanism optimization
2 parents a60b822 + ee7576e commit 6a69901

File tree

26 files changed

+4153
-9
lines changed

26 files changed

+4153
-9
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\ConditionProcessor\ConditionBuilder;
9+
10+
use Magento\Framework\Api\SearchCriteria\CollectionProcessor\ConditionProcessor\CustomConditionInterface;
11+
use Magento\Framework\Api\Filter;
12+
use Magento\Catalog\Model\ResourceModel\Product\Collection;
13+
use Magento\Catalog\Model\ResourceModel\Eav\Attribute;
14+
15+
/**
16+
* Based on Magento\Framework\Api\Filter builds condition
17+
* that can be applied to Catalog\Model\ResourceModel\Product\Collection
18+
* to filter products that has specific value for EAV attribute
19+
*/
20+
class EavAttributeCondition implements CustomConditionInterface
21+
{
22+
/**
23+
* @var \Magento\Framework\App\ResourceConnection
24+
*/
25+
private $resourceConnection;
26+
27+
/**
28+
* @var \Magento\Eav\Model\Config
29+
*/
30+
private $eavConfig;
31+
32+
/**
33+
* @param \Magento\Eav\Model\Config $eavConfig
34+
* @param \Magento\Framework\App\ResourceConnection $resourceConnection
35+
*/
36+
public function __construct(
37+
\Magento\Eav\Model\Config $eavConfig,
38+
\Magento\Framework\App\ResourceConnection $resourceConnection
39+
) {
40+
$this->eavConfig = $eavConfig;
41+
$this->resourceConnection = $resourceConnection;
42+
}
43+
44+
/**
45+
* Build condition to filter product collection by EAV attribute
46+
*
47+
* @param Filter $filter
48+
* @return string
49+
* @throws \DomainException
50+
* @throws \Magento\Framework\Exception\LocalizedException
51+
*/
52+
public function build(Filter $filter): string
53+
{
54+
$attribute = $this->getAttributeByCode($filter->getField());
55+
$tableAlias = 'ca_' . $attribute->getAttributeCode();
56+
57+
$conditionType = $this->mapConditionType($filter->getConditionType());
58+
$conditionValue = $this->mapConditionValue($conditionType, $filter->getValue());
59+
60+
// NOTE: store scope was ignored intentionally to perform search across all stores
61+
$attributeSelect = $this->resourceConnection->getConnection()
62+
->select()
63+
->from(
64+
[$tableAlias => $attribute->getBackendTable()],
65+
$tableAlias . '.' . $attribute->getEntityIdField()
66+
)->where(
67+
$this->resourceConnection->getConnection()->prepareSqlCondition(
68+
$tableAlias . '.' . $attribute->getIdFieldName(),
69+
['eq' => $attribute->getAttributeId()]
70+
)
71+
)->where(
72+
$this->resourceConnection->getConnection()->prepareSqlCondition(
73+
$tableAlias . '.value',
74+
[$conditionType => $conditionValue]
75+
)
76+
);
77+
78+
return $this->resourceConnection
79+
->getConnection()
80+
->prepareSqlCondition(
81+
Collection::MAIN_TABLE_ALIAS . '.' . $attribute->getEntityIdField(),
82+
[
83+
'in' => $attributeSelect
84+
]
85+
);
86+
}
87+
88+
/**
89+
* @param string $field
90+
* @return Attribute
91+
* @throws \Magento\Framework\Exception\LocalizedException
92+
*/
93+
private function getAttributeByCode(string $field): Attribute
94+
{
95+
return $this->eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $field);
96+
}
97+
98+
/**
99+
* Map equal and not equal conditions to in and not in
100+
*
101+
* @param string $conditionType
102+
* @return mixed
103+
*/
104+
private function mapConditionType(string $conditionType): string
105+
{
106+
$conditionsMap = [
107+
'eq' => 'in',
108+
'neq' => 'nin'
109+
];
110+
111+
return isset($conditionsMap[$conditionType]) ? $conditionsMap[$conditionType] : $conditionType;
112+
}
113+
114+
/**
115+
* Wraps value with '%' if condition type is 'like' or 'not like'
116+
*
117+
* @param string $conditionType
118+
* @param string $conditionValue
119+
* @return string
120+
*/
121+
private function mapConditionValue(string $conditionType, string $conditionValue): string
122+
{
123+
$conditionsMap = ['like', 'nlike'];
124+
125+
if (in_array($conditionType, $conditionsMap)) {
126+
$conditionValue = '%' . $conditionValue . '%';
127+
}
128+
129+
return $conditionValue;
130+
}
131+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\ConditionProcessor\ConditionBuilder;
9+
10+
use Magento\Framework\Api\Filter;
11+
use Magento\Framework\Api\SearchCriteria\CollectionProcessor\ConditionProcessor\CustomConditionInterface;
12+
use Magento\Catalog\Model\ResourceModel\Eav\Attribute;
13+
14+
/**
15+
* Creates appropriate condition builder based on filter field
16+
* - native attribute condition builder if filter field is native attribute in product
17+
* - eav condition builder if filter field is eav attribute
18+
*/
19+
class Factory
20+
{
21+
/**
22+
* @var \Magento\Eav\Model\Config
23+
*/
24+
private $eavConfig;
25+
26+
/**
27+
* @var \Magento\Catalog\Model\ResourceModel\Product
28+
*/
29+
private $productResource;
30+
31+
/**
32+
* @var CustomConditionInterface
33+
*/
34+
private $eavAttributeConditionBuilder;
35+
36+
/**
37+
* @var CustomConditionInterface
38+
*/
39+
private $nativeAttributeConditionBuilder;
40+
41+
/**
42+
* @param \Magento\Eav\Model\Config $eavConfig
43+
* @param \Magento\Catalog\Model\ResourceModel\Product $productResource
44+
* @param CustomConditionInterface $eavAttributeConditionBuilder
45+
* @param CustomConditionInterface $nativeAttributeConditionBuilder
46+
*/
47+
public function __construct(
48+
\Magento\Eav\Model\Config $eavConfig,
49+
\Magento\Catalog\Model\ResourceModel\Product $productResource,
50+
CustomConditionInterface $eavAttributeConditionBuilder,
51+
CustomConditionInterface $nativeAttributeConditionBuilder
52+
) {
53+
$this->eavConfig = $eavConfig;
54+
$this->productResource = $productResource;
55+
$this->eavAttributeConditionBuilder = $eavAttributeConditionBuilder;
56+
$this->nativeAttributeConditionBuilder = $nativeAttributeConditionBuilder;
57+
}
58+
59+
/**
60+
* Decides which condition builder should be used for passed filter
61+
* can be either EAV attribute builder or native attribute builder
62+
* "native" attribute means attribute that is in catalog_product_entity table
63+
*
64+
* @param Filter $filter
65+
* @return CustomConditionInterface
66+
* @throws \Magento\Framework\Exception\LocalizedException
67+
*/
68+
public function createByFilter(Filter $filter): CustomConditionInterface
69+
{
70+
$attribute = $this->getAttributeByCode($filter->getField());
71+
72+
if ($attribute->getBackendTable() === $this->productResource->getEntityTable()) {
73+
return $this->nativeAttributeConditionBuilder;
74+
}
75+
76+
return $this->eavAttributeConditionBuilder;
77+
}
78+
79+
/**
80+
* @param string $field
81+
* @return \Magento\Catalog\Model\ResourceModel\Eav\Attribute
82+
* @throws \Magento\Framework\Exception\LocalizedException
83+
*/
84+
private function getAttributeByCode(string $field): Attribute
85+
{
86+
return $this->eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $field);
87+
}
88+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\ConditionProcessor\ConditionBuilder;
9+
10+
use Magento\Catalog\Api\Data\ProductInterface;
11+
use Magento\Framework\Api\SearchCriteria\CollectionProcessor\ConditionProcessor\CustomConditionInterface;
12+
use Magento\Framework\Api\Filter;
13+
use Magento\Catalog\Model\ResourceModel\Product\Collection;
14+
15+
/**
16+
* Based on Magento\Framework\Api\Filter builds condition
17+
* that can be applied to Catalog\Model\ResourceModel\Product\Collection
18+
* to filter products that has specific value for their native attribute
19+
*/
20+
class NativeAttributeCondition implements CustomConditionInterface
21+
{
22+
/**
23+
* @var \Magento\Framework\App\ResourceConnection
24+
*/
25+
private $resourceConnection;
26+
27+
/**
28+
* @param \Magento\Framework\App\ResourceConnection $resourceConnection
29+
*/
30+
public function __construct(
31+
\Magento\Framework\App\ResourceConnection $resourceConnection
32+
) {
33+
$this->resourceConnection = $resourceConnection;
34+
}
35+
36+
/**
37+
* Build condition to filter product collection by product native attribute
38+
* "native" attribute means attribute that is in catalog_product_entity table
39+
*
40+
* @param Filter $filter
41+
* @return string
42+
* @throws \DomainException
43+
*/
44+
public function build(Filter $filter): string
45+
{
46+
$conditionType = $this->mapConditionType($filter->getConditionType(), $filter->getField());
47+
$conditionValue = $this->mapConditionValue($conditionType, $filter->getValue());
48+
49+
return $this->resourceConnection
50+
->getConnection()
51+
->prepareSqlCondition(
52+
Collection::MAIN_TABLE_ALIAS . '.' . $filter->getField(),
53+
[
54+
$conditionType => $conditionValue
55+
]
56+
);
57+
}
58+
59+
/**
60+
* Map equal and not equal conditions to in and not in
61+
*
62+
* @param string $conditionType
63+
* @param string $field
64+
* @return mixed
65+
*/
66+
private function mapConditionType(string $conditionType, string $field): string
67+
{
68+
if (strtolower($field) === ProductInterface::SKU) {
69+
$conditionsMap = [
70+
'eq' => 'like',
71+
'neq' => 'nlike'
72+
];
73+
} else {
74+
$conditionsMap = [
75+
'eq' => 'in',
76+
'neq' => 'nin'
77+
];
78+
}
79+
80+
return isset($conditionsMap[$conditionType]) ? $conditionsMap[$conditionType] : $conditionType;
81+
}
82+
83+
/**
84+
* Wraps value with '%' if condition type is 'like' or 'not like'
85+
*
86+
* @param string $conditionType
87+
* @param string $conditionValue
88+
* @return string
89+
*/
90+
private function mapConditionValue(string $conditionType, string $conditionValue): string
91+
{
92+
$conditionsMap = ['like', 'nlike'];
93+
94+
if (in_array($conditionType, $conditionsMap)) {
95+
$conditionValue = '%' . $conditionValue . '%';
96+
}
97+
98+
return $conditionValue;
99+
}
100+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\ConditionProcessor;
9+
10+
use Magento\Framework\Api\SearchCriteria\CollectionProcessor\ConditionProcessor\CustomConditionInterface;
11+
use Magento\Framework\Api\Filter;
12+
use Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\ConditionProcessor\ConditionBuilder\Factory;
13+
14+
/**
15+
* Default condition builder for Catalog\Model\ResourceModel\Product\Collection
16+
*/
17+
class DefaultCondition implements CustomConditionInterface
18+
{
19+
/**
20+
* @var Factory
21+
*/
22+
private $conditionBuilderFactory;
23+
24+
/**
25+
* @param Factory $conditionBuilderFactory
26+
*/
27+
public function __construct(
28+
Factory $conditionBuilderFactory
29+
) {
30+
$this->conditionBuilderFactory = $conditionBuilderFactory;
31+
}
32+
33+
/**
34+
* Builds condition to filter product collection either by EAV or by native attribute
35+
*
36+
* @param Filter $filter
37+
* @return string
38+
*/
39+
public function build(Filter $filter): string
40+
{
41+
$filterBuilder = $this->conditionBuilderFactory->createByFilter($filter);
42+
43+
return $filterBuilder->build($filter);
44+
}
45+
}

0 commit comments

Comments
 (0)