Skip to content

Commit 2123373

Browse files
author
Anna Bukatar
committed
ACP2E-2219: Indexer catalogsearch_fulltext runs multiple times per single product/category during partial reindex
1 parent 821bcac commit 2123373

File tree

4 files changed

+158
-7
lines changed
  • app/code/Magento
    • CatalogSearch/etc
    • Elasticsearch
      • Model/Indexer/Fulltext/Plugin/Category/Product/Action
      • Test/Unit/Model/Indexer/Fulltext/Plugin/Category/Product/Action
  • lib/internal/Magento/Framework/Mview/View

4 files changed

+158
-7
lines changed

app/code/Magento/CatalogSearch/etc/di.xml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,6 @@
3838
<type name="Magento\Catalog\Model\Product\Action">
3939
<plugin name="catalogsearchFulltextMassAction" type="Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin\Product\Action"/>
4040
</type>
41-
<type name="Magento\Catalog\Model\Indexer\Product\Category\Action\Rows">
42-
<plugin name="catalogsearchFulltextCategoryAssignment" type="Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin\Product\Category\Action\Rows"/>
43-
</type>
4441
<type name="Magento\Store\Model\ResourceModel\Store">
4542
<plugin name="catalogsearchFulltextIndexerStoreView" type="Magento\CatalogSearch\Model\Indexer\Fulltext\Plugin\Store\View" />
4643
</type>

app/code/Magento/Elasticsearch/Model/Indexer/Fulltext/Plugin/Category/Product/Action/Rows.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public function afterExecute(
8686

8787
$productIds = array_merge([], ...$productIds);
8888
if (!empty($productIds)) {
89-
$indexer->reindexList(array_unique($productIds));
89+
$indexer->getView()->getChangelog()->addList($productIds);
9090
}
9191
}
9292

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
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\Elasticsearch\Test\Unit\Model\Indexer\Fulltext\Plugin\Category\Product\Action;
9+
10+
use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer;
11+
use Magento\Framework\DB\Adapter\AdapterInterface;
12+
use Magento\Framework\DB\Select;
13+
use Magento\Store\Model\Store;
14+
use PHPUnit\Framework\MockObject\MockObject;
15+
use PHPUnit\Framework\TestCase;
16+
use Magento\Framework\Indexer\IndexerRegistry;
17+
use Magento\Store\Model\StoreManagerInterface;
18+
use Magento\Elasticsearch\Model\Indexer\Fulltext\Plugin\Category\Product\Action\Rows;
19+
use Magento\Catalog\Model\Indexer\Category\Product\Action\Rows as ActionRows;
20+
use Magento\Framework\App\ResourceConnection;
21+
use Magento\CatalogSearch\Model\Indexer\Fulltext as FulltextIndexer;
22+
23+
class RowsTest extends TestCase
24+
{
25+
/**
26+
* @var IndexerRegistry|MockObject
27+
*/
28+
private $indexerRegistryMock;
29+
30+
/**
31+
* @var StoreManagerInterface|MockObject
32+
*/
33+
private $storeManagerMock;
34+
35+
/**
36+
* @var Select|MockObject
37+
*/
38+
private $selectMock;
39+
40+
/**
41+
* @var AdapterInterface|MockObject
42+
*/
43+
private $connectionMock;
44+
45+
/**
46+
* @var ResourceConnection|MockObject
47+
*/
48+
private $resourceMock;
49+
50+
/**
51+
* @var TableMaintainer|MockObject
52+
*/
53+
private $tableMaintainerMock;
54+
55+
/**
56+
* @var Rows
57+
*/
58+
private $plugin;
59+
public function setUp(): void
60+
{
61+
parent::setUp();
62+
$this->indexerRegistryMock = $this->createMock(IndexerRegistry::class);
63+
$this->storeManagerMock =
64+
$this->getMockBuilder(StoreManagerInterface::class)->getMockForAbstractClass();
65+
$this->connectionMock =
66+
$this->getMockBuilder(AdapterInterface::class)->getMockForAbstractClass();
67+
$this->selectMock = $this->createMock(Select::class);
68+
$this->connectionMock->expects($this->any())->method('select')->willReturn($this->selectMock);
69+
$this->tableMaintainerMock = $this->createMock(TableMaintainer::class);
70+
$this->resourceMock = $this->getMockBuilder(ResourceConnection::class)
71+
->disableOriginalConstructor()
72+
->getMock();
73+
$this->resourceMock->expects($this->any())
74+
->method('getConnection')
75+
->willReturn($this->connectionMock);
76+
$this->plugin = new Rows(
77+
$this->indexerRegistryMock,
78+
$this->storeManagerMock,
79+
$this->resourceMock,
80+
$this->tableMaintainerMock
81+
);
82+
}
83+
84+
/**
85+
* Test afterExecute method.
86+
*
87+
* @return void
88+
*/
89+
public function testAfterExecute(): void
90+
{
91+
$productToReindex = [1];
92+
$storeId = 1;
93+
$categoryIds = [4];
94+
$actionMock = $this->createMock(ActionRows::class);
95+
$storeMock = $this->createMock(Store::class);
96+
$storeMock->expects($this->once())->method('getId')->willReturn($storeId);
97+
$this->storeManagerMock->expects($this->once())->method('getStores')->willReturn([$storeMock]);
98+
$this->tableMaintainerMock->expects($this->once())->method('getMainTable')->with($storeId)->willReturn('table');
99+
100+
$this->getProductIdsFromIndex($productToReindex);
101+
$this->createIndexerMock($productToReindex);
102+
103+
$this->plugin->afterExecute($actionMock, $actionMock, $categoryIds);
104+
}
105+
106+
/**
107+
* Creates a mock for the indexer registry to add given ids to changelog.
108+
*
109+
* @param array $ids
110+
* @return void
111+
*/
112+
private function createIndexerMock(array $ids): void
113+
{
114+
//schedule catalogsearch indexer changes to improve row index performance instead of executing them right away
115+
$changelogMock = $this->createMock(\Magento\Framework\Mview\View\Changelog::class);
116+
$changelogMock->expects($this->once())->method('addList')->with($ids);
117+
$viewMock = $this->createMock(\Magento\Framework\Mview\ViewInterface::class);
118+
$viewMock->expects($this->once())->method('getChangelog')->willReturn($changelogMock);
119+
120+
$indexerMock = $this->createMock(\Magento\Framework\Indexer\IndexerInterface::class);
121+
$indexerMock->expects($this->once())->method('isScheduled')->willReturn(true);
122+
$indexerMock->expects($this->once())->method('getView')->willReturn($viewMock);
123+
124+
$this->indexerRegistryMock->expects($this->once())
125+
->method('get')
126+
->with(FulltextIndexer::INDEXER_ID)
127+
->willReturn($indexerMock);
128+
}
129+
130+
/**
131+
* Mocks the connection to return the given ids.
132+
*
133+
* @param array $ids
134+
* @return void
135+
*/
136+
private function getProductIdsFromIndex(array $ids): void
137+
{
138+
$this->selectMock->expects($this->any())->method('from')->with()->willReturnSelf();
139+
$this->selectMock->expects($this->any())->method('where')->willReturnSelf();
140+
$this->connectionMock->expects($this->any())->method('fetchCol')->willReturn($ids);
141+
}
142+
}

lib/internal/Magento/Framework/Mview/View/Changelog.php

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,17 @@ class Changelog implements ChangelogInterface
2222
/**
2323
* Suffix for changelog table
2424
*/
25-
const NAME_SUFFIX = 'cl';
25+
public const NAME_SUFFIX = 'cl';
2626

2727
/**
2828
* Column name of changelog entity
2929
*/
30-
const COLUMN_NAME = 'entity_id';
30+
public const COLUMN_NAME = 'entity_id';
3131

3232
/**
3333
* Column name for Version ID
3434
*/
35-
const VERSION_ID_COLUMN_NAME = 'version_id';
35+
public const VERSION_ID_COLUMN_NAME = 'version_id';
3636

3737
/**
3838
* Database connection
@@ -304,4 +304,16 @@ public function getViewId()
304304
{
305305
return $this->viewId;
306306
}
307+
308+
/**
309+
* Add list of ids to changelog
310+
*
311+
* @param array $ids
312+
* @return void
313+
*/
314+
public function addList(array $ids): void
315+
{
316+
$changelogTableName = $this->resource->getTableName($this->getName());
317+
$this->connection->insertArray($changelogTableName, ['entity_id'], $ids);
318+
}
307319
}

0 commit comments

Comments
 (0)