diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 9c3b3616d180b2ef8dd1062451c6d25344c19a59..c4907e8cd9276d32541c39af068966c0cec83839 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -20,5 +20,6 @@ + \ No newline at end of file diff --git a/src/ElasticSearch/Helper/ArrayHelper.php b/src/ElasticSearch/Helper/ArrayHelper.php index e050625cff5c4cb35e700cdf4645f2ea2bfbeb1e..4d0765cca1181232f99c263c3a67770188684257 100644 --- a/src/ElasticSearch/Helper/ArrayHelper.php +++ b/src/ElasticSearch/Helper/ArrayHelper.php @@ -15,12 +15,14 @@ class ArrayHelper public static function array_filter_recursive(array $array, ?callable $callback = null): array { $array = is_callable($callback) ? array_filter($array, $callback) : array_filter($array); - foreach ($array as &$value) { + foreach ($array as $key => &$value) { if (is_array($value)) { $value = call_user_func([__CLASS__, __FUNCTION__], $value, $callback); if (!empty($value)) { $value = self::array_filter_recursive($value, $callback); + } else { + unset($array[$key]); } } } diff --git a/src/ElasticSearch/Indexer/IndexRunner.php b/src/ElasticSearch/Indexer/IndexRunner.php index 8ac968bd3dd07b3afb90fb6af3244f34967d4955..f2e4e737edb26827b3195d2cf85308888c85493f 100644 --- a/src/ElasticSearch/Indexer/IndexRunner.php +++ b/src/ElasticSearch/Indexer/IndexRunner.php @@ -74,11 +74,15 @@ final class IndexRunner continue; } - if (!($index instanceof AddIndex) && !($index instanceof UpdateIndex)) { + if ($index instanceof AddIndex) { + $this->esClient->index($index->es()); continue; } - $this->esClient->index($index->es()); + if ($index instanceof UpdateIndex) { + $this->esClient->update($index->es()); + continue; + } } } } diff --git a/tests/Config/ChangingStateConfiguration.php b/tests/Config/ChangingStateConfiguration.php new file mode 100644 index 0000000000000000000000000000000000000000..61166d021d3fee8fc4093d7da1a07fbff3c0da08 --- /dev/null +++ b/tests/Config/ChangingStateConfiguration.php @@ -0,0 +1,13 @@ + 'test', + 'name' => 'Test товар', + 'category' => 'indexes', + 'properties' => [ + 'prop1' => 'value1', + 'prop2' => 'value2', + 'prop3' => 'value3', + ] + ]; + + private IndexRunner $indexRunner; + private Client $esClient; + private Configuration $configuration; + + public function __construct(?string $name = null, array $data = [], $dataName = '') + { + parent::__construct($name, $data, $dataName); + + $this->configuration = new ChangingStateConfiguration(); + $this->esClient = ClientFactory::create(); + + $this->indexRunner = new IndexRunner( + $this->esClient, + $this->configuration, + new TestLogger() + ); + } + + public function testUpdate() + { + $indexProvider = new TestIndexProvider($this->configuration, [$this->product]); + $this->indexRunner->run($indexProvider); + + $updateData = [ + 'id' => $this->product['id'], + 'category' => $this->product['category'], + 'type' => 'update', + 'name' => 'Обновленный элемент' + ]; + $indexProvider = new TestIndexProvider($this->configuration, [$updateData]); + $this->indexRunner->run($indexProvider); + + $response = $this->esClient->search([ + 'index' => $this->configuration->getIndexName(), + 'body' => [ + 'query' => [ + 'match' => [ + '_id' => $this->product['id'] + ], + ] + ] + ]); + $esResponseToResult = new EsResponseToResult(); + $result = $esResponseToResult->fromResponse($response); + + unset($updateData['type']); + $expected = [ + 'products' => [ + array_merge($this->product, $updateData) + ] + ]; + + $this->assertEqualsCanonicalizing($expected, FormatData::formatDataProducts($result)); + } +} \ No newline at end of file diff --git a/tests/Helpers/FormatData.php b/tests/Helpers/FormatData.php index dd2808b2fb7877069ff32991ff00f41a1227cdc2..f042c9a1dddcb40e5642e45c755a2a227f0d9c8f 100644 --- a/tests/Helpers/FormatData.php +++ b/tests/Helpers/FormatData.php @@ -74,4 +74,37 @@ class FormatData } return $aResult; } + + public static function formatDataProducts(Result $result): array + { + $products = []; + /** @var Product $product */ + foreach ($result->getProducts() as $product) { + $data = [ + 'id' => $product->id, + 'category' => $product->info['category_id'] + ]; + + if ($product->title) { + $data['name'] = $product->title; + } + + if (isset($product->info['search_data'])) { + $props = $product->info['search_data']; + if (!empty($props['keyword_facet'])) { + foreach ($props['keyword_facet'] as $prop) { + $data['properties'][$prop['facet_code']] = $prop['facet_value']; + } + } + if (!empty($props['number_facet'])) { + foreach ($props['number_facet'] as $prop) { + $data['properties'][$prop['facet_code']] = $prop['facet_value']; + } + } + } + + $products[] = $data; + } + return ['products' => $products]; + } } \ No newline at end of file diff --git a/tests/Helpers/TestIndexProvider.php b/tests/Helpers/TestIndexProvider.php new file mode 100644 index 0000000000000000000000000000000000000000..89761f057372389acdb74884a098905dd209bdd4 --- /dev/null +++ b/tests/Helpers/TestIndexProvider.php @@ -0,0 +1,116 @@ +configuration = $configuration; + $this->products = $products; + } + + /** + * @inheritDoc + */ + public function get(): \Generator + { + foreach ($this->products as $product) { + $document = new ProductDocument(new FacetCategory($product['category'])); + //todo по-хорошему нужны базовые классы, которые будут описывать свойства + // и формировать структуру для последующей обработки + + $data = [ + 'id' => $product['id'], + ]; + + if (isset($product['name'])) { + $document->setSearchContent($product['name']); + $data['title'] = $product['name']; + } + + $document->setAdditionData($data); + + if (isset($product['properties'])) { + foreach ($product['properties'] as $key => $prop) { + if ($key === 'price') { + $document->getNumberFacets()->add(new FacetNumber($key, $prop)); + } else { + $document->getKeywordFacets()->add(new FacetKeyword($key, $prop)); + } + } + } + + $product['type'] = $product['type'] ?? null; + switch ($product['type']) { + case 'update': + $document->skipEmpty(true); + $index = new UpdateIndex( + $this->configuration->getIndexName(), + $document, + $product['id'] + ); + break; + case 'delete': + $index = new DeleteIndex( + $this->configuration->getIndexName(), + $product['id'] + ); + break; + default: + $index = new AddIndex( + $this->configuration->getIndexName(), + $document, + $product['id'] + ); + break; + } + + yield $index; + } + } + + /** + * @inheritDoc + */ + public function setBatchSize(int $size): void + { + } + + /** + * @inheritDoc + */ + public function getBatchSize(): ?int + { + return null; + } + + /** + * @inheritDoc + */ + public function setLimit(int $limit): void + { + } + + /** + * @inheritDoc + */ + public function getLimit(): ?int + { + return null; + } +} \ No newline at end of file diff --git a/tests/Seed/DefaultSeed.php b/tests/Seed/DefaultSeed.php index f2390cdc3748b993b616e362556641ad834d26b9..2453c4a0a17d9702b014b591ca1c1a3220ddcae0 100644 --- a/tests/Seed/DefaultSeed.php +++ b/tests/Seed/DefaultSeed.php @@ -4,14 +4,9 @@ namespace IQDEV\ElasticSearchTests\Seed; use IQDEV\ElasticSearch\Config\BaseConfiguration; use IQDEV\ElasticSearch\Configuration; -use IQDEV\ElasticSearch\Document\ProductDocument; -use IQDEV\ElasticSearch\Facet\FacetCategory; -use IQDEV\ElasticSearch\Facet\FacetKeyword; -use IQDEV\ElasticSearch\Facet\FacetNumber; -use IQDEV\ElasticSearch\Indexer\AddIndex; -use IQDEV\ElasticSearch\Indexer\IndexProvider; use IQDEV\ElasticSearch\Indexer\IndexRunner; use IQDEV\ElasticSearchTests\Factory\ClientFactory; +use IQDEV\ElasticSearchTests\Helpers\TestIndexProvider; use Psr\Log\Test\TestLogger; class DefaultSeed @@ -33,105 +28,56 @@ class DefaultSeed public function start() { - $provider = new class implements IndexProvider { - public function get(): \Generator - { - $products = [ - [ - 'id' => 's1', - 'name' => 'Кроссовки NMD_R1 Boba Fett Spectoo', - 'category' => 'shoes', - 'properties' => ['brand' => 'adidas', 'color' => 'green', 'size' => 46,'price' => 100] - ], - [ - 'id' => 's2', - 'name' => 'КРОССОВКИ ULTRABOOST 5.0 DNA', - 'category' => 'shoes', - 'properties' => ['brand' => 'adidas', 'color' => 'red', 'size' => 47,'price' => 101] - ], - [ - 'id' => 's3', - 'name' => 'Кроссовки Reebok Royal Techque', - 'category' => 'shoes', - 'properties' => ['brand' => 'rebook', 'color' => 'blue', 'size' => 47,'price' => 102] - ], - [ - 'id' => 's4', - 'name' => 'Nike Air Zoom Pegasus 39', - 'category' => 'shoes', - 'properties' => ['brand' => 'nike', 'color' => 'green', 'size' => 43,'price' => 103] - ], - [ - 'id' => 'h1', - 'name' => 'Nike Dri-FIT Strike', - 'category' => 't-short', - 'properties' => ['brand' => 'nike', 'color' => 'red', 'size' => 'xl','price' => 104] - ], - [ - 'id' => 'h2', - 'name' => 'Nike Dri-FIT Rise 365', - 'category' => 't-short', - 'properties' => ['brand' => 'nike', 'color' => 'white', 'size' => 'xxl','price' => 105] - ], - [ - 'id' => 'h3', - 'name' => 'Компрессионная Футболка ACTIVCHILL Graphic Move', - 'category' => 't-short', - 'properties' => ['brand' => 'rebook', 'color' => 'white', 'size' => 'xl','price' => 106] - ], - [ - 'id' => 'p1', - 'name' => 'Товар с ценой', - 'category' => 'prices', - 'properties' => ['brand' => 'rebook', 'color' => 'white', 'size' => 'xl','price' => 107] - ], - ]; - - foreach ($products as $product) { - $document = new ProductDocument(new FacetCategory($product['category'])); - //todo по-хорошему нужны базовые классы, которые будут описывать свойства - // и формировать структуру для последующей обработки - - $document->setAdditionData([ - 'id' => $product['id'], - 'title' => $product['name'], - ]); - - foreach ($product['properties'] as $key => $prop) { - if ($key === 'price') { - $document->getNumberFacets()->add(new FacetNumber($key, $prop)); - } else { - $document->getKeywordFacets()->add(new FacetKeyword($key, $prop)); - } - } - $document->setSearchContent($product['name']); - - yield new AddIndex( - $_ENV['IQ_ES_PRODUCT_SEARCH_INDEX'], - $document, - $product['id'] - ); - } - } - - public function setBatchSize(int $size): void - { - } - - public function getBatchSize(): ?int - { - return null; - } - - public function setLimit(int $limit): void - { - } - - public function getLimit(): ?int - { - return null; - } - }; + $provider = new TestIndexProvider([ + [ + 'id' => 's1', + 'name' => 'Кроссовки NMD_R1 Boba Fett Spectoo', + 'category' => 'shoes', + 'properties' => ['brand' => 'adidas', 'color' => 'green', 'size' => 46,'price' => 100] + ], + [ + 'id' => 's2', + 'name' => 'КРОССОВКИ ULTRABOOST 5.0 DNA', + 'category' => 'shoes', + 'properties' => ['brand' => 'adidas', 'color' => 'red', 'size' => 47,'price' => 101] + ], + [ + 'id' => 's3', + 'name' => 'Кроссовки Reebok Royal Techque', + 'category' => 'shoes', + 'properties' => ['brand' => 'rebook', 'color' => 'blue', 'size' => 47,'price' => 102] + ], + [ + 'id' => 's4', + 'name' => 'Nike Air Zoom Pegasus 39', + 'category' => 'shoes', + 'properties' => ['brand' => 'nike', 'color' => 'green', 'size' => 43,'price' => 103] + ], + [ + 'id' => 'h1', + 'name' => 'Nike Dri-FIT Strike', + 'category' => 't-short', + 'properties' => ['brand' => 'nike', 'color' => 'red', 'size' => 'xl','price' => 104] + ], + [ + 'id' => 'h2', + 'name' => 'Nike Dri-FIT Rise 365', + 'category' => 't-short', + 'properties' => ['brand' => 'nike', 'color' => 'white', 'size' => 'xxl','price' => 105] + ], + [ + 'id' => 'h3', + 'name' => 'Компрессионная Футболка ACTIVCHILL Graphic Move', + 'category' => 't-short', + 'properties' => ['brand' => 'rebook', 'color' => 'white', 'size' => 'xl','price' => 106] + ], + [ + 'id' => 'p1', + 'name' => 'Товар с ценой', + 'category' => 'prices', + 'properties' => ['brand' => 'rebook', 'color' => 'white', 'size' => 'xl','price' => 107] + ], + ]); $this->indexRunner->run($provider); }