257 lines
8.3 KiB
PHP
Executable File
257 lines
8.3 KiB
PHP
Executable File
<?php
|
|
/**
|
|
* Copyright © Magento, Inc. All rights reserved.
|
|
* See COPYING.txt for license details.
|
|
*/
|
|
declare(strict_types=1);
|
|
|
|
namespace Magento\Elasticsearch\Model\Adapter;
|
|
|
|
use Magento\AdvancedSearch\Model\Client\ClientInterface;
|
|
use Magento\Catalog\Model\ResourceModel\Eav\Attribute;
|
|
use Magento\Catalog\Setup\CategorySetup;
|
|
use Magento\CatalogSearch\Model\Indexer\IndexerHandlerFactory;
|
|
use Magento\Eav\Model\Entity\Attribute\Backend\ArrayBackend;
|
|
use Magento\Elasticsearch\Model\Adapter\Index\BuilderInterface;
|
|
use Magento\Elasticsearch\Model\Adapter\Index\IndexNameResolver;
|
|
use Magento\Elasticsearch\Model\Indexer\IndexerHandler;
|
|
use Magento\Elasticsearch\SearchAdapter\ConnectionManager;
|
|
use Magento\Framework\Indexer\DimensionFactory;
|
|
use Magento\Framework\ObjectManagerInterface;
|
|
use Magento\Framework\Stdlib\ArrayManager;
|
|
use Magento\Indexer\Model\Indexer;
|
|
use Magento\Store\Model\StoreDimensionProvider;
|
|
use Magento\Store\Model\StoreManagerInterface;
|
|
use Magento\TestFramework\Helper\Bootstrap;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
/**
|
|
* Elasticsearch adapter model test class
|
|
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
|
|
*/
|
|
class ElasticsearchTest extends TestCase
|
|
{
|
|
/**
|
|
* @var ObjectManagerInterface
|
|
*/
|
|
private $objectManager;
|
|
|
|
/**
|
|
* @var IndexNameResolver
|
|
*/
|
|
private $indexNameResolver;
|
|
|
|
/**
|
|
* @var ClientInterface
|
|
*/
|
|
private $client;
|
|
|
|
/**
|
|
* @var StoreManagerInterface
|
|
*/
|
|
private $storeManager;
|
|
|
|
/**
|
|
* @var Elasticsearch
|
|
*/
|
|
private $adapter;
|
|
|
|
/**
|
|
* @var BuilderInterface
|
|
*/
|
|
private $indexBuilder;
|
|
|
|
/**
|
|
* @var ArrayManager
|
|
*/
|
|
private $arrayManager;
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
private $newIndex;
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
protected function setUp(): void
|
|
{
|
|
$this->objectManager = Bootstrap::getObjectManager();
|
|
$this->indexNameResolver = $this->objectManager->get(IndexNameResolver::class);
|
|
$this->adapter = $this->objectManager->get(Elasticsearch::class);
|
|
$this->storeManager = $this->objectManager->create(StoreManagerInterface::class);
|
|
$connectionManager = $this->objectManager->create(ConnectionManager::class);
|
|
$this->client = $connectionManager->getConnection();
|
|
$this->indexBuilder = $this->objectManager->get(BuilderInterface::class);
|
|
$this->arrayManager = $this->objectManager->get(ArrayManager::class);
|
|
}
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
public function tearDown(): void
|
|
{
|
|
if ($this->newIndex) {
|
|
$this->deleteIndex($this->newIndex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Tests possibility to create mapping if adapter has obsolete index name in cache
|
|
*
|
|
* @magentoDataFixture Magento/Elasticsearch/_files/select_attribute.php
|
|
* @return void
|
|
*/
|
|
public function testRetryOnIndexNotFoundException(): void
|
|
{
|
|
$this->reindex();
|
|
$this->updateElasticsearchIndex();
|
|
$this->createNewAttribute();
|
|
$mapping = $this->client->getMapping(['index' => $this->newIndex]);
|
|
$pathField = $this->arrayManager->findPath('properties', $mapping);
|
|
$attributes = $this->arrayManager->get($pathField, $mapping, []);
|
|
$this->assertArrayHasKey('multiselect_attribute', $attributes);
|
|
}
|
|
|
|
/**
|
|
* Test that new fields are not added during document indexing that were not explicitly defined in the mapping
|
|
*
|
|
* @magentoDataFixture Magento/Catalog/_files/product_simple.php
|
|
* @magentoDataFixture Magento/Store/_files/second_store.php
|
|
* @magentoDbIsolation disabled
|
|
*/
|
|
public function testMappingShouldNotChangeAfterReindex(): void
|
|
{
|
|
$index = 'catalogsearch_fulltext';
|
|
$storeId = (int) $this->storeManager->getStore('fixture_second_store')->getId();
|
|
// Create empty index and save the initial mapping
|
|
$dimensionFactory = $this->objectManager->get(DimensionFactory::class);
|
|
$dimensions = [
|
|
StoreDimensionProvider::DIMENSION_NAME => $dimensionFactory->create(
|
|
StoreDimensionProvider::DIMENSION_NAME,
|
|
(string) $storeId
|
|
)
|
|
];
|
|
$indexHandlerFactory = $this->objectManager->get(IndexerHandlerFactory::class);
|
|
/** @var IndexerHandler $indexHandler */
|
|
$indexHandler = $indexHandlerFactory->create(
|
|
[
|
|
'data' => [
|
|
'indexer_id' => $index
|
|
]
|
|
]
|
|
);
|
|
$indexHandler->cleanIndex($dimensions);
|
|
$indexHandler->saveIndex($dimensions, new \ArrayIterator([]));
|
|
$propertiesBefore = $this->getIndexMapping($storeId);
|
|
$this->reindex();
|
|
$propertiesAfter = $this->getIndexMapping($storeId);
|
|
$this->assertEquals($propertiesBefore, $propertiesAfter);
|
|
}
|
|
|
|
/**
|
|
* Prepare and save new attribute
|
|
*
|
|
* @return void
|
|
*/
|
|
public function createNewAttribute(): void
|
|
{
|
|
/** @var CategorySetup $installer */
|
|
$installer = $this->objectManager->get(CategorySetup::class);
|
|
/** @var Attribute $attribute */
|
|
$multiselectAttribute = $this->objectManager->get(Attribute::class);
|
|
$multiselectAttribute->setData(
|
|
[
|
|
'attribute_code' => 'multiselect_attribute',
|
|
'entity_type_id' => $installer->getEntityTypeId('catalog_product'),
|
|
'is_global' => 1,
|
|
'is_user_defined' => 1,
|
|
'frontend_input' => 'multiselect',
|
|
'is_unique' => 0,
|
|
'is_required' => 0,
|
|
'is_searchable' => 1,
|
|
'is_visible_in_advanced_search' => 0,
|
|
'is_comparable' => 0,
|
|
'is_filterable' => 1,
|
|
'is_filterable_in_search' => 0,
|
|
'is_used_for_promo_rules' => 0,
|
|
'is_html_allowed_on_front' => 1,
|
|
'is_visible_on_front' => 0,
|
|
'used_in_product_listing' => 0,
|
|
'used_for_sort_by' => 0,
|
|
'frontend_label' => ['Multiselect Attribute'],
|
|
'backend_type' => 'text',
|
|
'backend_model' => ArrayBackend::class,
|
|
'option' => [
|
|
'value' => [
|
|
'dog' => ['Dog'],
|
|
'cat' => ['Cat'],
|
|
],
|
|
'order' => [
|
|
'dog' => 1,
|
|
'cat' => 2,
|
|
],
|
|
],
|
|
]
|
|
);
|
|
$multiselectAttribute->save();
|
|
}
|
|
|
|
/**
|
|
* Prepare new index and delete old. Keep cache alive.
|
|
*
|
|
* @return void
|
|
*/
|
|
private function updateElasticsearchIndex(): void
|
|
{
|
|
$storeId = (int)$this->storeManager->getDefaultStoreView()->getId();
|
|
$mappedIndexerId = 'product';
|
|
$this->adapter->updateIndexMapping($storeId, $mappedIndexerId, 'select_attribute');
|
|
$oldIndex = $this->indexNameResolver->getIndexFromAlias($storeId, $mappedIndexerId);
|
|
$this->newIndex = $oldIndex . '1';
|
|
$this->deleteIndex($this->newIndex);
|
|
$this->indexBuilder->setStoreId($storeId);
|
|
$this->client->createIndex($this->newIndex, ['settings' => $this->indexBuilder->build()]);
|
|
$this->client->updateAlias(
|
|
$this->indexNameResolver->getIndexNameForAlias($storeId, $mappedIndexerId),
|
|
$this->newIndex,
|
|
$oldIndex
|
|
);
|
|
$this->client->deleteIndex($oldIndex);
|
|
}
|
|
|
|
/**
|
|
* Delete index by name if exists
|
|
*
|
|
* @param $newIndex
|
|
*/
|
|
private function deleteIndex($newIndex): void
|
|
{
|
|
if ($this->client->indexExists($newIndex)) {
|
|
$this->client->deleteIndex($newIndex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
private function reindex(): void
|
|
{
|
|
$indexer = $this->objectManager->create(Indexer::class);
|
|
$indexer->load('catalogsearch_fulltext');
|
|
$indexer->reindexAll();
|
|
}
|
|
|
|
/**
|
|
* @param int $storeId
|
|
* @return array
|
|
*/
|
|
private function getIndexMapping(int $storeId): array
|
|
{
|
|
$indexName = $this->indexNameResolver->getIndexName($storeId, 'product', []);
|
|
$mapping = $this->client->getMapping(['index' => $indexName]);
|
|
$pathField = $this->arrayManager->findPath('properties', $mapping);
|
|
return $this->arrayManager->get($pathField, $mapping, []);
|
|
}
|
|
}
|