Skip to content

Commit 5a1fa62

Browse files
committed
ACP2E-3891: Categories in admin are loading very slow
1 parent a04b0ef commit 5a1fa62

File tree

2 files changed

+92
-20
lines changed

2 files changed

+92
-20
lines changed

app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -375,35 +375,57 @@ private function getCountFromCategoryTableBulk(
375375
array $categoryIds,
376376
int $websiteId
377377
) : array {
378-
$subSelect = clone $this->_conn->select();
379-
$subSelect->from(['ce2' => $this->getTable('catalog_category_entity')], 'ce2.entity_id')
380-
->where("ce2.path LIKE CONCAT(ce.path, '/%') OR ce2.path = ce.path");
381-
382-
$select = clone $this->_conn->select();
383-
$select->from(
378+
$connection = $this->_conn;
379+
$tempTableName = $connection->getTableName('temp_category_descendants_' . uniqid());
380+
$connection->query("CREATE TEMPORARY TABLE {$tempTableName} (
381+
category_id INT UNSIGNED NOT NULL,
382+
descendant_id INT UNSIGNED NOT NULL,
383+
PRIMARY KEY (category_id, descendant_id)
384+
)");
385+
$selectDescendants = clone $connection->select();
386+
$selectDescendants->from(
384387
['ce' => $this->getTable('catalog_category_entity')],
385-
'ce.entity_id'
386-
);
387-
$joinCondition = new \Zend_Db_Expr("cp.category_id IN ({$subSelect})");
388-
$select->joinLeft(
389-
['cp' => $this->getProductTable()],
390-
$joinCondition,
391-
'COUNT(DISTINCT cp.product_id) AS product_count'
388+
['category_id' => 'ce.entity_id', 'descendant_id' => 'ce2.entity_id']
389+
)
390+
->joinInner(
391+
['ce2' => $this->getTable('catalog_category_entity')],
392+
'ce2.path LIKE CONCAT(ce.path, \'/%\') OR ce2.entity_id = ce.entity_id',
393+
[]
394+
)
395+
->where('ce.entity_id IN (?)', $categoryIds);
396+
$connection->query(
397+
$connection->insertFromSelect(
398+
$selectDescendants,
399+
$tempTableName,
400+
['category_id', 'descendant_id']
401+
)
392402
);
403+
$select = clone $connection->select();
404+
$select->from(
405+
['t' => $tempTableName],
406+
['category_id' => 't.category_id']
407+
)
408+
->joinLeft(
409+
['cp' => $this->getTable('catalog_category_product')],
410+
'cp.category_id = t.descendant_id',
411+
['product_count' => 'COUNT(DISTINCT cp.product_id)']
412+
);
393413
if ($websiteId) {
394414
$select->join(
395415
['w' => $this->getProductWebsiteTable()],
396416
'cp.product_id = w.product_id',
397417
[]
398-
)->where(
399-
'w.website_id = ?',
400-
$websiteId
401-
);
418+
)->where('w.website_id = ?', $websiteId);
419+
}
420+
$select->group('t.category_id');
421+
$result = $connection->fetchPairs($select);
422+
$connection->query("DROP TEMPORARY TABLE {$tempTableName}");
423+
$counts = array_fill_keys($categoryIds, 0);
424+
foreach ($result as $categoryId => $count) {
425+
$counts[$categoryId] = (int)$count;
402426
}
403-
$select->where('ce.entity_id IN(?)', $categoryIds);
404-
$select->group('ce.entity_id');
405427

406-
return $this->_conn->fetchPairs($select);
428+
return $counts;
407429
}
408430

409431
/**

app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Category/CollectionTest.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77

88
namespace Magento\Catalog\Test\Unit\Model\ResourceModel\Category;
99

10+
use Magento\Catalog\Model\Category;
1011
use Magento\Framework\Data\Collection\EntityFactory;
12+
use Magento\Store\Model\Store;
1113
use Psr\Log\LoggerInterface;
1214
use Magento\Framework\Data\Collection\Db\FetchStrategyInterface;
1315
use Magento\Framework\Event\ManagerInterface;
@@ -215,4 +217,52 @@ public function testLoadProductCount() : void
215217
->willReturn([]);
216218
$this->collection->loadProductCount([]);
217219
}
220+
221+
/**
222+
* Test that loadProductCount calls getCountFromCategoryTableBulk
223+
*/
224+
public function testLoadProductCountCallsBulkMethodForLargeCategoryCount()
225+
{
226+
$websiteId = 1;
227+
$storeId = 1;
228+
$categoryCount = 401;
229+
$items = [];
230+
$categoryIds = [];
231+
for ($i = 1; $i <= $categoryCount; $i++) {
232+
$category = $this->getMockBuilder(Category::class)
233+
->addMethods(['getIsAnchor'])
234+
->onlyMethods(['getId', 'setProductCount'])
235+
->disableOriginalConstructor()
236+
->getMock();
237+
$category->method('getId')->willReturn($i);
238+
$category->method('getIsAnchor')->willReturn(true);
239+
$category->expects($this->once())->method('setProductCount')->with(5);
240+
$items[$i] = $category;
241+
$categoryIds[] = $i;
242+
}
243+
$storeMock = $this->createMock(Store::class);
244+
$storeMock->method('getWebsiteId')->willReturn($websiteId);
245+
$this->storeManager->method('getStore')->with($storeId)->willReturn($storeMock);
246+
$this->connection->method('select')->willReturn($this->select);
247+
$counts = array_fill_keys($categoryIds, 5);
248+
$this->select->method('from')->willReturnSelf();
249+
$this->select->method('where')->willReturnSelf();
250+
$this->select->method('group')->willReturnSelf();
251+
$this->connection->method('fetchPairs')
252+
->with($this->isInstanceOf(Select::class))
253+
->willReturn($counts);
254+
$tempTableName = 'temp_category_descendants_';
255+
$this->connection->method('query')->willReturn($tempTableName);
256+
$this->select->method('joinInner')->willReturnSelf();
257+
$this->connection->method('insertFromSelect')->willReturn('INSERT QUERY');
258+
$bulkSelectMock = $this->createMock(Select::class);
259+
$bulkSelectMock->method('from')->willReturnSelf();
260+
$bulkSelectMock->method('joinLeft')->willReturnSelf();
261+
$bulkSelectMock->method('join')->willReturnSelf();
262+
$bulkSelectMock->method('where')->willReturnSelf();
263+
$bulkSelectMock->method('group')->willReturnSelf();
264+
$this->connection->method('fetchPairs')->willReturn($counts);
265+
$this->collection->setProductStoreId($storeId);
266+
$this->collection->loadProductCount($items, false, true);
267+
}
218268
}

0 commit comments

Comments
 (0)