Skip to content

Commit 03bc738

Browse files
MAGETWO-69130: Non effective query for catalog search & layered navigation
1 parent 26af11a commit 03bc738

File tree

3 files changed

+191
-80
lines changed

3 files changed

+191
-80
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\Bundle\Model\ResourceModel\Indexer;
8+
9+
use Magento\Catalog\Api\Data\ProductInterface;
10+
use Magento\Framework\DB\Select;
11+
12+
class BundleOptionStockDataSelectBuilder
13+
{
14+
/**
15+
* @param \Magento\Framework\App\ResourceConnection $resourceConnection
16+
* @param \Magento\Framework\EntityManager\MetadataPool $metadataPool
17+
*/
18+
public function __construct(
19+
\Magento\Framework\App\ResourceConnection $resourceConnection,
20+
\Magento\Framework\EntityManager\MetadataPool $metadataPool
21+
) {
22+
$this->resourceConnection = $resourceConnection;
23+
$this->metadataPool = $metadataPool;
24+
}
25+
26+
/**
27+
* @param $idxTable
28+
* @return Select
29+
*/
30+
public function buildSelect($idxTable)
31+
{
32+
$select = $this->resourceConnection->getConnection()->select();
33+
$linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField();
34+
35+
$select->from(
36+
['product' => $this->resourceConnection->getTableName('catalog_product_entity')],
37+
['entity_id']
38+
)->join(
39+
['bo' => $this->resourceConnection->getTableName('catalog_product_bundle_option')],
40+
"bo.parent_id = product.$linkField",
41+
[]
42+
)->join(
43+
['cis' => $this->resourceConnection->getTableName('cataloginventory_stock')],
44+
'',
45+
['website_id', 'stock_id']
46+
)->joinLeft(
47+
['bs' => $this->resourceConnection->getTableName('catalog_product_bundle_selection')],
48+
'bs.option_id = bo.option_id',
49+
[]
50+
)->joinLeft(
51+
['i' => $idxTable],
52+
'i.product_id = bs.product_id AND i.website_id = cis.website_id AND i.stock_id = cis.stock_id',
53+
[]
54+
)->joinLeft(
55+
['e' => $this->resourceConnection->getTableName('catalog_product_entity')],
56+
'e.entity_id = bs.product_id',
57+
[]
58+
)->group(
59+
['product.entity_id', 'cis.website_id', 'cis.stock_id', 'bo.option_id']
60+
)->columns(['option_id' => 'bo.option_id','status' => new \Zend_Db_Expr('MAX('. $statusExpression. ')')]);
61+
62+
return $select;
63+
}
64+
}

app/code/Magento/Bundle/Model/ResourceModel/Indexer/Stock.php

Lines changed: 35 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,13 @@
55
*/
66
namespace Magento\Bundle\Model\ResourceModel\Indexer;
77

8-
use Magento\Catalog\Api\Data\ProductInterface;
98
use Magento\CatalogInventory\Model\Indexer\Stock\Action\Full;
10-
use Magento\Catalog\Model\Product\Attribute\Source\Status as ProductStatus;
9+
use Magento\Framework\App\ObjectManager;
1110

1211
/**
1312
* Bundle Stock Status Indexer Resource Model
1413
*
1514
* @author Magento Core Team <core@magentocommerce.com>
16-
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
1715
*/
1816
class Stock extends \Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\DefaultStock
1917
{
@@ -22,6 +20,16 @@ class Stock extends \Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\
2220
*/
2321
private $indexerStockFrontendResource;
2422

23+
/**
24+
* @var \Magento\Bundle\Model\ResourceModel\Indexer\StockStatusSelectBuilder
25+
*/
26+
private $stockStatusSelectBuilder;
27+
28+
/**
29+
* @var \Magento\Bundle\Model\ResourceModel\Indexer\BundleOptionStockDataSelectBuilder
30+
*/
31+
private $bundleOptionStockDataSelectBuilder;
32+
2533
/**
2634
* Class constructor
2735
*
@@ -32,6 +40,7 @@ class Stock extends \Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\
3240
* @param string $connectionName
3341
* @param null|\Magento\Indexer\Model\Indexer\StateFactory $stateFactory
3442
* @param null|\Magento\Indexer\Model\ResourceModel\FrontendResource $indexerStockFrontendResource
43+
* @param null|\Magento\Bundle\Model\ResourceModel\Indexer\StockStatusSelectBuilder $stockStatusSelectBuilder
3544
*/
3645
public function __construct(
3746
\Magento\Framework\Model\ResourceModel\Db\Context $context,
@@ -40,11 +49,20 @@ public function __construct(
4049
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
4150
$connectionName = null,
4251
\Magento\Indexer\Model\Indexer\StateFactory $stateFactory = null,
43-
\Magento\Indexer\Model\ResourceModel\FrontendResource $indexerStockFrontendResource = null
52+
\Magento\Indexer\Model\ResourceModel\FrontendResource $indexerStockFrontendResource = null,
53+
StockStatusSelectBuilder $stockStatusSelectBuilder = null,
54+
BundleOptionStockDataSelectBuilder $bundleOptionStockDataSelectBuilder = null
4455
) {
4556
parent::__construct($context, $tableStrategy, $eavConfig, $scopeConfig, $connectionName, $stateFactory);
57+
4658
$this->indexerStockFrontendResource = $indexerStockFrontendResource ?: ObjectManager::getInstance()
4759
->get(\Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\FrontendResource::class);
60+
61+
$this->stockStatusSelectBuilder = $stockStatusSelectBuilder ?: ObjectManager::getInstance()
62+
->get(StockStatusSelectBuilder::class);
63+
64+
$this->bundleOptionStockDataSelectBuilder = $bundleOptionStockDataSelectBuilder ?: ObjectManager::getInstance()
65+
->get(BundleOptionStockDataSelectBuilder::class);
4866
}
4967

5068
/**
@@ -67,46 +85,21 @@ protected function _getBundleOptionTable()
6785
protected function _prepareBundleOptionStockData($entityIds = null, $usePrimaryTable = false)
6886
{
6987
$this->_cleanBundleOptionStockData();
70-
$linkField = $this->getMetadataPool()->getMetadata(ProductInterface::class)->getLinkField();
88+
$connection = $this->getConnection();
7189
$table = $this->getActionType() === Full::ACTION_TYPE
7290
? $this->getMainTable()
7391
: $this->indexerStockFrontendResource->getMainTable();
7492
$idxTable = $usePrimaryTable ? $table : $this->getIdxTable();
75-
$connection = $this->getConnection();
76-
$select = $connection->select()->from(
77-
['product' => $this->getTable('catalog_product_entity')],
78-
['entity_id']
79-
);
80-
$select->join(
81-
['bo' => $this->getTable('catalog_product_bundle_option')],
82-
"bo.parent_id = product.$linkField",
83-
[]
84-
);
93+
$select = $this->bundleOptionStockDataSelectBuilder->buildSelect($idxTable);
94+
8595
$status = new \Zend_Db_Expr(
86-
'MAX(' . $connection->getCheckSql('e.required_options = 0', 'i.stock_status', '0') . ')'
87-
);
88-
$select->join(
89-
['cis' => $this->getTable('cataloginventory_stock')],
90-
'',
91-
['website_id', 'stock_id']
92-
)->joinLeft(
93-
['bs' => $this->getTable('catalog_product_bundle_selection')],
94-
'bs.option_id = bo.option_id',
95-
[]
96-
)->joinLeft(
97-
['i' => $idxTable],
98-
'i.product_id = bs.product_id AND i.website_id = cis.website_id AND i.stock_id = cis.stock_id',
99-
[]
100-
)->joinLeft(
101-
['e' => $this->getTable('catalog_product_entity')],
102-
'e.entity_id = bs.product_id',
103-
[]
104-
)->group(
105-
['product.entity_id', 'cis.website_id', 'cis.stock_id', 'bo.option_id']
106-
)->columns(
107-
['option_id' => 'bo.option_id', 'status' => $status]
96+
'MAX('
97+
. $connection->getCheckSql('e.required_options = 0', 'i.stock_status', '0')
98+
. ')'
10899
);
109100

101+
$select->columns(['status' => $status]);
102+
110103
if ($entityIds !== null) {
111104
$select->where('product.entity_id IN(?)', $entityIds);
112105
}
@@ -136,56 +129,18 @@ protected function _getStockStatusSelect($entityIds = null, $usePrimaryTable = f
136129
{
137130
$this->_prepareBundleOptionStockData($entityIds, $usePrimaryTable);
138131
$connection = $this->getConnection();
139-
$select = parent::_getStockStatusSelect($entityIds, $usePrimaryTable);
140-
$metadata = $this->getMetadataPool()->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class);
141-
$linkField = $metadata->getLinkField();
142-
143-
$select->reset(
144-
\Magento\Framework\DB\Select::COLUMNS
145-
)->columns(
146-
['e.entity_id', 'cis.website_id', 'cis.stock_id']
147-
)->joinLeft(
148-
['o' => $this->_getBundleOptionTable()],
149-
'o.entity_id = e.entity_id AND o.website_id = cis.website_id AND o.stock_id = cis.stock_id',
150-
[]
151-
)->joinInner(
152-
['cpr' => $this->getTable('catalog_product_relation')],
153-
'e.' . $linkField . ' = cpr.parent_id',
154-
[]
155-
)->columns(
156-
['qty' => new \Zend_Db_Expr('0')]
157-
);
158132

159-
if ($metadata->getIdentifierField() === $metadata->getLinkField()) {
160-
$select->joinInner(
161-
['cpei' => $this->getTable('catalog_product_entity_int')],
162-
'cpr.child_id = cpei.' . $linkField
163-
. ' AND cpei.attribute_id = ' . $this->_getAttribute('status')->getId()
164-
. ' AND cpei.value = ' . ProductStatus::STATUS_ENABLED,
165-
[]
166-
);
167-
} else {
168-
$select->joinInner(
169-
['cpel' => $this->getTable('catalog_product_entity')],
170-
'cpel.entity_id = cpr.child_id',
171-
[]
172-
)->joinInner(
173-
['cpei' => $this->getTable('catalog_product_entity_int')],
174-
'cpel.'. $linkField . ' = cpei.' . $linkField
175-
. ' AND cpei.attribute_id = ' . $this->_getAttribute('status')->getId()
176-
. ' AND cpei.value = ' . ProductStatus::STATUS_ENABLED,
177-
[]
178-
);
179-
}
133+
$select = parent::_getStockStatusSelect($entityIds, $usePrimaryTable);
134+
$select = $this->stockStatusSelectBuilder->buildSelect($select);
180135

136+
$statusNotNullExpr = $connection->getCheckSql('o.stock_status IS NOT NULL', 'o.stock_status', '0');
181137
$statusExpr = $this->getStatusExpression($connection);
138+
182139
$select->columns(
183140
[
184141
'status' => $connection->getLeastSql(
185142
[
186-
new \Zend_Db_Expr(
187-
'MIN(' . $connection->getCheckSql('o.stock_status IS NOT NULL', 'o.stock_status', '0') . ')'
188-
),
143+
new \Zend_Db_Expr('MIN(' . $statusNotNullExpr . ')'),
189144
new \Zend_Db_Expr('MIN(' . $statusExpr . ')'),
190145
]
191146
),
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\Bundle\Model\ResourceModel\Indexer;
8+
9+
use Magento\Framework\DB\Select;
10+
use Magento\Catalog\Api\Data\ProductInterface;
11+
use Magento\Catalog\Model\Product\Attribute\Source\Status as ProductStatus;
12+
13+
class StockStatusSelectBuilder
14+
{
15+
16+
/**
17+
* @param \Magento\Framework\App\ResourceConnection $resourceConnection
18+
* @param \Magento\Framework\EntityManager\MetadataPool $metadataPool
19+
* @param \Magento\Eav\Model\Config $eavConfig
20+
*/
21+
public function __construct(
22+
\Magento\Framework\App\ResourceConnection $resourceConnection,
23+
\Magento\Framework\EntityManager\MetadataPool $metadataPool,
24+
\Magento\Eav\Model\Config $eavConfig
25+
) {
26+
$this->resourceConnection = $resourceConnection;
27+
$this->metadataPool = $metadataPool;
28+
$this->eavConfig = $eavConfig;
29+
}
30+
31+
/**
32+
* @param Select $select
33+
* @return Select
34+
*/
35+
public function buildSelect(Select $select)
36+
{
37+
$select = clone $select;
38+
$metadata = $this->metadataPool->getMetadata(ProductInterface::class);
39+
$linkField = $metadata->getLinkField();
40+
41+
$select->reset(
42+
\Magento\Framework\DB\Select::COLUMNS
43+
)->columns(
44+
['e.entity_id', 'cis.website_id', 'cis.stock_id']
45+
)->joinLeft(
46+
['o' => $this->resourceConnection->getTableName('catalog_product_bundle_stock_index')],
47+
'o.entity_id = e.entity_id AND o.website_id = cis.website_id AND o.stock_id = cis.stock_id',
48+
[]
49+
)->joinInner(
50+
['cpr' => $this->resourceConnection->getTableName('catalog_product_relation')],
51+
'e.' . $linkField . ' = cpr.parent_id',
52+
[]
53+
)->columns(
54+
['qty' => new \Zend_Db_Expr('0')]
55+
);
56+
57+
if ($metadata->getIdentifierField() === $metadata->getLinkField()) {
58+
$select->joinInner(
59+
['cpei' => $this->resourceConnection->getTableName('catalog_product_entity_int')],
60+
'cpr.child_id = cpei.' . $linkField
61+
. ' AND cpei.attribute_id = ' . $this->getAttribute('status')->getId()
62+
. ' AND cpei.value = ' . ProductStatus::STATUS_ENABLED,
63+
[]
64+
);
65+
} else {
66+
$select->joinInner(
67+
['cpel' => $this->resourceConnection->getTableName('catalog_product_entity')],
68+
'cpel.entity_id = cpr.child_id',
69+
[]
70+
)->joinInner(
71+
['cpei' => $this->resourceConnection->getTableName('catalog_product_entity_int')],
72+
'cpel.'. $linkField . ' = cpei.' . $linkField
73+
. ' AND cpei.attribute_id = ' . $this->getAttribute('status')->getId()
74+
. ' AND cpei.value = ' . ProductStatus::STATUS_ENABLED,
75+
[]
76+
);
77+
}
78+
79+
return $select;
80+
}
81+
82+
/**
83+
* Retrieve catalog_product attribute instance by attribute code
84+
*
85+
* @param string $attributeCode
86+
* @return \Magento\Catalog\Model\ResourceModel\Eav\Attribute
87+
*/
88+
private function getAttribute($attributeCode)
89+
{
90+
return $this->eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $attributeCode);
91+
}
92+
}

0 commit comments

Comments
 (0)