diff --git a/bundles/EcommerceFrameworkBundle/DependencyInjection/PimcoreEcommerceFrameworkExtension.php b/bundles/EcommerceFrameworkBundle/DependencyInjection/PimcoreEcommerceFrameworkExtension.php index b36be98670b..a850541bded 100644 --- a/bundles/EcommerceFrameworkBundle/DependencyInjection/PimcoreEcommerceFrameworkExtension.php +++ b/bundles/EcommerceFrameworkBundle/DependencyInjection/PimcoreEcommerceFrameworkExtension.php @@ -30,6 +30,7 @@ use Pimcore\Bundle\EcommerceFrameworkBundle\PriceSystem\PriceSystemLocator; use Pimcore\Bundle\EcommerceFrameworkBundle\PricingManager\PricingManagerLocator; use Pimcore\Bundle\EcommerceFrameworkBundle\PricingManager\PricingManagerLocatorInterface; +use Pimcore\Bundle\ElasticsearchClientBundle\DependencyInjection\PimcoreElasticsearchClientExtension; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ChildDefinition; @@ -465,6 +466,13 @@ private function registerIndexServiceConfig(ContainerBuilder $container, array $ $worker->setArgument('$tenantConfig', new Reference($configId)); $worker->addTag('pimcore_ecommerce.index_service.worker', ['tenant' => $tenant]); + if(!empty($tenantConfig['config_options']['es_client_name'])) { + $worker->addMethodCall( + 'setElasticSearchClient', + [new Reference(PimcoreElasticsearchClientExtension::CLIENT_SERVICE_PREFIX . $tenantConfig['config_options']['es_client_name'])] + ); + } + $container->setDefinition($configId, $config); $container->setDefinition($workerId, $worker); } diff --git a/bundles/EcommerceFrameworkBundle/IndexService/Config/ElasticSearch.php b/bundles/EcommerceFrameworkBundle/IndexService/Config/ElasticSearch.php index 917b629513f..9c0109001a4 100644 --- a/bundles/EcommerceFrameworkBundle/IndexService/Config/ElasticSearch.php +++ b/bundles/EcommerceFrameworkBundle/IndexService/Config/ElasticSearch.php @@ -168,6 +168,12 @@ protected function configureOptionsResolver(string $resolverName, OptionsResolve $resolver->setDefault('store', true); $resolver->setAllowedTypes('store', 'bool'); + + $resolver->setDefined('es_client_name'); + $resolver->setAllowedTypes('es_client_name', 'string'); + + //set options to deprecated + $resolver->setDeprecated('es_client_params'); } /** diff --git a/bundles/EcommerceFrameworkBundle/IndexService/ProductList/ElasticSearch/DefaultElasticSearch7.php b/bundles/EcommerceFrameworkBundle/IndexService/ProductList/ElasticSearch/DefaultElasticSearch7.php index 99266fdfa90..d56debb44a1 100644 --- a/bundles/EcommerceFrameworkBundle/IndexService/ProductList/ElasticSearch/DefaultElasticSearch7.php +++ b/bundles/EcommerceFrameworkBundle/IndexService/ProductList/ElasticSearch/DefaultElasticSearch7.php @@ -15,6 +15,9 @@ namespace Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\ProductList\ElasticSearch; +/** + * @deprecated + */ class DefaultElasticSearch7 extends AbstractElasticSearch { } diff --git a/bundles/EcommerceFrameworkBundle/IndexService/ProductList/ElasticSearch/DefaultElasticSearch8.php b/bundles/EcommerceFrameworkBundle/IndexService/ProductList/ElasticSearch/DefaultElasticSearch8.php new file mode 100644 index 00000000000..984dc1d52cc --- /dev/null +++ b/bundles/EcommerceFrameworkBundle/IndexService/ProductList/ElasticSearch/DefaultElasticSearch8.php @@ -0,0 +1,68 @@ +tenantConfig->getTenantWorker()->getElasticSearchClient(); + $result = []; + + if ($esClient instanceof Client) { + if ($this->doScrollRequest) { + $params = array_merge(['scroll' => $this->scrollRequestKeepAlive], $params); + //kind of dirty hack :/ + $params['body']['size'] = $this->getLimit(); + } + + $result = $esClient->search($params)->asArray(); + + if ($this->doScrollRequest) { + $additionalHits = []; + $scrollId = $result['_scroll_id']; + + while (true) { + $additionalResult = $esClient->scroll(['scroll_id' => $scrollId, 'scroll' => $this->scrollRequestKeepAlive])->asArray(); + + if (count($additionalResult['hits']['hits'])) { + $additionalHits = array_merge($additionalHits, $additionalResult['hits']['hits']); + $scrollId = $additionalResult['_scroll_id']; + } else { + break; + } + } + $result['hits']['hits'] = array_merge($result['hits']['hits'], $additionalHits); + } + } + + return $result; + } + +} diff --git a/bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/DefaultElasticSearch7.php b/bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/DefaultElasticSearch7.php index 2edad43d8ac..073d9ad20f5 100644 --- a/bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/DefaultElasticSearch7.php +++ b/bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/DefaultElasticSearch7.php @@ -16,7 +16,8 @@ namespace Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\Worker\ElasticSearch; /** - * Use this for ES Version >= 7 + * Use this for ES Version = 7 + * @deprecated */ class DefaultElasticSearch7 extends AbstractElasticSearch { diff --git a/bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/DefaultElasticSearch8.php b/bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/DefaultElasticSearch8.php new file mode 100644 index 00000000000..2fdb579460d --- /dev/null +++ b/bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/DefaultElasticSearch8.php @@ -0,0 +1,542 @@ +tenantConfig); + } + + /** + * @return int + */ + public function getIndexVersion() + { + if ($this->indexVersion === null) { + $this->indexVersion = 0; + $esClient = $this->getElasticSearchClient(); + + try { + $result = $esClient->indices()->getAlias([ + 'name' => $this->indexName, + ])->asArray(); + + if (is_array($result)) { + $aliasIndexName = array_key_first($result); + preg_match('/'.$this->indexName.'-(\d+)/', $aliasIndexName, $matches); + if (is_array($matches) && count($matches) > 1) { + $version = (int)$matches[1]; + if ($version > $this->indexVersion) { + $this->indexVersion = $version; + } + } + } + } catch (ClientResponseException $e) { + if($e->getCode() === 404) { + $this->indexVersion = 0; + } else { + throw $e; + } + } + } + + return $this->indexVersion; + } + + /** + * @param Client|null $elasticSearchClient + */ + public function setElasticSearchClient(?Client $elasticSearchClient): void + { + $this->elasticSearchClient = $elasticSearchClient; + } + + /** + * @return Client|null + */ + public function getElasticSearchClient() + { + return $this->elasticSearchClient; + } + + /** + * actually sending data to elastic search + */ + public function commitBatchToIndex(): void + { + if (count($this->bulkIndexData)) { + $esClient = $this->getElasticSearchClient(); + $responses = $esClient->bulk([ + 'body' => $this->bulkIndexData, + ])->asArray(); + + // save update status + foreach ($responses['items'] as $response) { + $operation = null; + if (isset($response['index'])) { + $operation = 'index'; + } elseif (isset($response['delete'])) { + $operation = 'delete'; + } + + if ($operation) { + $data = [ + 'update_status' => $response[$operation]['status'], + 'update_error' => null, + 'metadata' => isset($this->indexStoreMetaData[$response[$operation]['_id']]) ? $this->indexStoreMetaData[$response[$operation]['_id']] : null, + ]; + if (isset($response[$operation]['error']) && $response[$operation]['error']) { + $data['update_error'] = json_encode($response[$operation]['error']); + $data['crc_index'] = 0; + Logger::error( + 'Failed to Index Object with Id:' . $response[$operation]['_id'], + json_decode($data['update_error'], true) + ); + + $this->db->update( + $this->getStoreTableName(), + $data, + ['o_id' => $response[$operation]['_id'], 'tenant' => $this->name] + ); + } else { + //update crc sums in store table to mark element as indexed + $this->db->executeQuery( + 'UPDATE ' . $this->getStoreTableName() . ' SET crc_index = crc_current, update_status = ?, update_error = ?, metadata = ? WHERE o_id = ? and tenant = ?', + [$data['update_status'], $data['update_error'], $data['metadata'], $response[$operation]['_id'], $this->name] + ); + } + } else { + throw new \Exception('Unkown operation in response: ' . print_r($response, true)); + } + } + } + + // reset + $this->bulkIndexData = []; + $this->indexStoreMetaData = []; + } + + /** + * Sets the alias to the current index-version and deletes the old indices + * + * @throws \Exception + */ + public function switchIndexAlias() + { + Logger::info('Index-Actions - Switching Alias'); + $esClient = $this->getElasticSearchClient(); + + $params['body'] = [ + 'actions' => [ + [ + 'remove' => [ + 'index' => '*', + 'alias' => $this->indexName, + ], + ], + [ + 'add' => [ + 'index' => $this->getIndexNameVersion(), + 'alias' => $this->indexName, + ], + ], + ], + ]; + $result = $esClient->indices()->updateAliases($params)->asArray(); + if (!$result['acknowledged']) { + //set current index version + throw new \Exception('Switching Alias failed for ' . $this->getIndexNameVersion()); + } + + //delete old indices + $this->cleanupUnusedEsIndices(); + } + + + protected function cleanupUnusedEsIndices(): void + { + $esClient = $this->getElasticSearchClient(); + $stats = $esClient->indices()->stats()->asArray(); + foreach ($stats['indices'] as $key => $data) { + preg_match('/'.$this->indexName.'-(\d+)/', $key, $matches); + if (is_array($matches) && count($matches) > 1) { + $version = (int)$matches[1]; + if ($version != $this->indexVersion) { + $indexNameVersion = $this->getIndexNameVersion($version); + Logger::info('Index-Actions - Delete old Index ' . $indexNameVersion); + $this->deleteEsIndexIfExisting($indexNameVersion); + } + } + } + } + + /** + * @param int $objectId + * @param IndexableInterface|null $object + * + * @throws \Exception + */ + protected function doDeleteFromIndex($objectId, IndexableInterface $object = null) + { + $esClient = $this->getElasticSearchClient(); + + $storeEntry = \Pimcore\Db::get()->fetchAssociative('SELECT * FROM ' . $this->getStoreTableName() . ' WHERE o_id=? AND tenant=? ', [$objectId, $this->getTenantConfig()->getTenantName()]); + if ($storeEntry) { + $isLocked = $this->checkIndexLock(false); + if ($isLocked) { + throw new \Exception('Delete not possible due to product index lock. Please re-try later.'); + } + + try { + $tenantConfig = $this->getTenantConfig(); + if (!$tenantConfig instanceof ElasticSearchConfigInterface) { + throw new \Exception('Expected a ElasticSearchConfigInterface'); + } + $esClient->delete([ + 'index' => $this->getIndexNameVersion(), + 'type' => $tenantConfig->getElasticSearchClientParams()['indexType'], + 'id' => $objectId, + $this->routingParamName => $storeEntry['o_virtualProductId'], + ]); + } catch (ClientResponseException $e) { + if($e->getCode() !== 404) { + throw $e; + } + } + $this->deleteFromStoreTable($objectId); + } + } + + protected function doCreateOrUpdateIndexStructures() + { + $this->checkIndexLock(true); + + $this->createOrUpdateStoreTable(); + + $esClient = $this->getElasticSearchClient(); + + $result = $esClient->indices()->exists(['index' => $this->getIndexNameVersion()])->asBool(); + + if (!$result) { + $indexName = $this->getIndexNameVersion(); + $this->createEsIndex($indexName); + + //index didn't exist -> reset index queue to make sure all products get re-indexed + $this->resetIndexingQueue(); + + $this->createEsAliasIfMissing(); + } + + try { + $this->putIndexMapping($this->getIndexNameVersion()); + + $configuredSettings = $this->tenantConfig->getIndexSettings(); + $synonymSettings = $this->extractMinimalSynonymFiltersTreeFromTenantConfig(); + if (isset($synonymSettings['analysis'])) { + $configuredSettings['analysis']['filter'] = array_replace_recursive($configuredSettings['analysis']['filter'], $synonymSettings['analysis']['filter']); + } + + $currentSettings = $esClient->indices()->getSettings([ + 'index' => $this->getIndexNameVersion(), + ])->asArray(); + $currentSettings = $currentSettings[$this->getIndexNameVersion()]['settings']['index']; + + $settingsIntersection = array_intersect_key($currentSettings, $configuredSettings); + if ($settingsIntersection != $configuredSettings) { + $esClient->indices()->putSettings([ + 'index' => $this->getIndexNameVersion(), + 'body' => [ + 'index' => $this->tenantConfig->getIndexSettings(), + ], + ]); + Logger::info('Index-Actions - updated settings for Index: ' . $this->getIndexNameVersion()); + } else { + Logger::info('Index-Actions - no settings update necessary for Index: ' . $this->getIndexNameVersion()); + } + } catch (\Exception $e) { + Logger::info("Index-Actions - can't create Mapping - trying reindexing " . $e->getMessage()); + Logger::info('Index-Actions - Perform native reindexing for Index: ' . $this->getIndexNameVersion()); + + $this->startReindexMode(); + } + } + + /** + * Retrieve the currently active index name from ES based on the alias. + * + * @return string|null null if no index is found. + */ + public function fetchEsActiveIndex(): ?string + { + $esClient = $this->getElasticSearchClient(); + + try { + $result = $esClient->indices()->getAlias(['index' => $this->indexName])->asArray(); + } catch (\Exception $e) { + Logger::error((string) $e); + + return null; + } + + reset($result); + $currentIndexName = key($result); + + return $currentIndexName; + } + + /** + * Create the index alias on demand. + * + * @throws \Exception if alias could not be created. + */ + protected function createEsAliasIfMissing() + { + $esClient = $this->getElasticSearchClient(); + //create alias for new index if alias doesn't exist so far + $aliasExists = $esClient->indices()->existsAlias(['name' => $this->indexName])->asBool(); + if (!$aliasExists) { + Logger::info("Index-Actions - create alias for index since it doesn't exist at all. Name: " . $this->indexName); + $params['body'] = [ + 'actions' => [ + [ + 'add' => [ + 'index' => $this->getIndexNameVersion(), + 'alias' => $this->indexName, + ], + ], + ], + ]; + $result = $esClient->indices()->updateAliases($params)->asArray(); + if (!$result) { + throw new \Exception('Alias '.$this->indexName.' could not be created.'); + } + } + } + + + /** + * Create an ES index with the specified version. + * + * @param string $indexName the name of the index. + * + * @throws \Exception is thrown if index cannot be created, for instance if connection fails or index is already existing. + */ + protected function createEsIndex(string $indexName) + { + $esClient = $this->getElasticSearchClient(); + + Logger::info('Index-Actions - creating new Index. Name: ' . $indexName); + + $configuredSettings = $this->tenantConfig->getIndexSettings(); + $synonymSettings = $this->extractMinimalSynonymFiltersTreeFromTenantConfig(); + if (isset($synonymSettings['analysis'])) { + $configuredSettings['analysis']['filter'] = array_replace_recursive($configuredSettings['analysis']['filter'], $synonymSettings['analysis']['filter']); + } + + $result = $esClient->indices()->create([ + 'index' => $indexName, + 'body' => ['settings' => $configuredSettings], + ])->asArray(); + + if (!$result['acknowledged']) { + throw new \Exception('Index creation failed. IndexName: ' . $indexName); + } + } + + + + /** + * puts current mapping to index with given name + * + * @param string $indexName + * + * @throws \Exception + */ + protected function putIndexMapping(string $indexName) + { + $esClient = $this->getElasticSearchClient(); + + $params = $this->getMappingParams(); + $params['index'] = $indexName; + $result = $esClient->indices()->putMapping($params)->asArray(); + + if (!$result['acknowledged']) { + throw new \Exception('Putting mapping to index failed. IndexName: ' . $indexName); + } + + Logger::info('Index-Actions - updated Mapping for Index: ' . $indexName); + } + + /** + * Delete an ES index if existing. + * + * @param string $indexName the name of the index. + */ + protected function deleteEsIndexIfExisting(string $indexName) + { + $esClient = $this->getElasticSearchClient(); + $result = $esClient->indices()->exists(['index' => $indexName])->asBool(); + if ($result) { + Logger::info('Deleted index '.$indexName.'.'); + $result = $esClient->indices()->delete(['index' => $indexName])->asArray(); + if (!array_key_exists('acknowledged', $result) && !$result['acknowledged']) { + Logger::error("Could not delete index {$indexName} while cleanup. Please remove the index manually."); + } + } + } + + + /** + * Blocks all write operations + * + * @param string $indexName the name of the index. + */ + protected function blockIndexWrite(string $indexName) + { + $esClient = $this->getElasticSearchClient(); + $result = $esClient->indices()->exists(['index' => $indexName])->asBool(); + if ($result) { + Logger::info('Block write index '.$indexName.'.'); + $esClient->indices()->putSettings([ + 'index' => $indexName, + 'body' => [ + 'index.blocks.write' => true, + ], + ]); + + $esClient->indices()->refresh([ + 'index' => $indexName, + ]); + } + } + + + + /** + * Unblocks all write operations + * + * @param string $indexName the name of the index. + */ + protected function unblockIndexWrite(string $indexName) + { + $esClient = $this->getElasticSearchClient(); + $result = $esClient->indices()->exists(['index' => $indexName])->asBool(); + if ($result) { + Logger::info('Unlock write index '.$indexName.'.'); + $esClient->indices()->putSettings([ + 'index' => $indexName, + 'body' => [ + 'index.blocks.write' => false, + ], + ]); + + $esClient->indices()->refresh([ + 'index' => $indexName, + ]); + } + } + + + /** + * + * Perform a synonym update on the currently selected ES index, if necessary. + * + * Attention: the current index will be closed and opened, so it won't be available for a tiny moment (typically some milliseconds). + * + * @param string $indexNameOverride if given, then that index will be used instead of the current index. + * @param bool $skipComparison if explicitly set to true, then the comparison whether the synonyms between the current index settings + * and the local index settings vary, will be skipped, and the index settings will be updated regardless. + * @param bool $skipLocking if explictly set to true, then no global lock will be activated / released. + * + * @throws \Exception is thrown if the synonym transmission fails. + */ + public function updateSynonyms(string $indexNameOverride = '', bool $skipComparison = false, bool $skipLocking = true) + { + try { + if (!$skipLocking) { + $this->activateIndexLock(); //lock all other processes + } + + $indexName = $indexNameOverride ?: $this->getIndexNameVersion(); + + $indexSettingsSynonymPartLocalConfig = $this->extractMinimalSynonymFiltersTreeFromTenantConfig(); + if (empty($indexSettingsSynonymPartLocalConfig)) { + Logger::info('No index update required, as no synonym providers are configured. '. + 'If filters have been removed, then reindexing will help to get rid of old configurations.' + ); + + return; + } + + $esClient = $this->getElasticSearchClient(); + + if (!$skipComparison) { + $settings = $esClient->indices()->getSettings(['index' => $indexName])->asArray(); + $indexSettingsCurrentEs = $settings[$indexName]['settings']['index']; + $indexSettingsSynonymPartEs = $this->extractMinimalSynonymFiltersTreeFromIndexSettings($indexSettingsCurrentEs); + + if ($indexSettingsSynonymPartEs == $indexSettingsSynonymPartLocalConfig) { + Logger::info(sprintf('The synonyms in ES index "%s" are identical with those of the local configuration. '. + 'No update required.', $indexName)); + + return; + } + } + + Logger::info(sprintf('Update synonyms in "%s"...', $indexName)); + $esClient->indices()->close(['index' => $indexName]); + + $result = $esClient->indices()->putSettings([ + 'index' => $indexName, + 'body' => [ + 'index' => $indexSettingsSynonymPartLocalConfig, + ], + ])->asArray(); + + $esClient->indices()->open(['index' => $indexName]); + + if (!$result['acknowledged']) { + //exception must be thrown after re-opening the index! + throw new \Exception('Index synonym settings update failed. IndexName: ' . $indexName); + } + } finally { + if (!$skipLocking) { + $this->releaseIndexLock(); + } + } + } + + +} diff --git a/bundles/EcommerceFrameworkBundle/Resources/config/index_service_configs_workers.yaml b/bundles/EcommerceFrameworkBundle/Resources/config/index_service_configs_workers.yaml index 8b996a771a5..c82a490f4c6 100644 --- a/bundles/EcommerceFrameworkBundle/Resources/config/index_service_configs_workers.yaml +++ b/bundles/EcommerceFrameworkBundle/Resources/config/index_service_configs_workers.yaml @@ -68,5 +68,8 @@ services: Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\Worker\ElasticSearch\DefaultElasticSearch7: parent: Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\Worker\AbstractWorker + Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\Worker\ElasticSearch\DefaultElasticSearch8: + parent: Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\Worker\AbstractWorker + Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\Worker\DefaultFindologic: parent: Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\Worker\AbstractWorker diff --git a/composer.json b/composer.json index 375e316621d..0ded957c0e8 100644 --- a/composer.json +++ b/composer.json @@ -174,11 +174,12 @@ "codeception/codeception": "^4.1.12", "codeception/module-symfony": "^1.6.0", "codeception/phpunit-wrapper": "^9", + "pimcore/elasticsearch-client": "^1.0.0", "phpstan/phpstan": "^1.9", "phpstan/phpstan-symfony": "^1.2.14", "phpunit/phpunit": "^9.3", "spiritix/php-chrome-html2pdf": "^1.6", - "elasticsearch/elasticsearch": "^7.11", + "elasticsearch/elasticsearch": "^8.0", "composer/composer": "*", "chrome-php/chrome": "^1.4.0" }, diff --git a/doc/Development_Documentation/10_E-Commerce_Framework/05_Index_Service/01_Product_Index_Configuration/07_Elastic_Search/01_Configuration_Details.md b/doc/Development_Documentation/10_E-Commerce_Framework/05_Index_Service/01_Product_Index_Configuration/07_Elastic_Search/01_Configuration_Details.md index 8bf256a1b3f..27804c4b5fc 100644 --- a/doc/Development_Documentation/10_E-Commerce_Framework/05_Index_Service/01_Product_Index_Configuration/07_Elastic_Search/01_Configuration_Details.md +++ b/doc/Development_Documentation/10_E-Commerce_Framework/05_Index_Service/01_Product_Index_Configuration/07_Elastic_Search/01_Configuration_Details.md @@ -6,7 +6,7 @@ Following aspects need to be considered in index configuration: In the `config_options` area general elasticsearch settings can be made - like hosts, index settings, etc. ##### `client_config` -- `logging`: `true`/`false` to activate logging of elasticsearch client +- `logging`: (deprecated, for Elasticsearch 7 only) `true`/`false` to activate logging of elasticsearch client - `indexName`: index name to be used, if not provided tenant name is used as index name ##### `index_settings` @@ -14,8 +14,26 @@ Index settings that are used when creating a new index. They are passed 1:1 as settings param to the body of the create index command. Details see also [elasticsearch Docs](https://www.elastic.co/guide/en/elasticsearch/client/php-api/current/_index_management_operations.html). +#### `es_client_name` (for Elasticsearch 8 only) +Elasticsearch 8 client configuration takes place via +[Pimcore Elasticsearch Client Bundle](https://github.com/pimcore/elasticsearch-client) and has two parts. + +1) Configuring an elasticsearch client in separate configuration +```yaml +# Configure an elasticsearch client +pimcore_elasticsearch_client: + es_clients: + default: + hosts: ['elastic:9200'] + username: 'elastic' + password: 'somethingsecret' + logger_channel: 'pimcore.elasticsearch' +``` + +2) Define the client name to be used by an elasticsearch tenant. This will be done via the `es_client_name` configuration + in the `config_options`. -##### `es_client_params` +##### `es_client_params` (deprecated, for Elasticsearch 7 only) - `hosts`: Array of hosts of the elasticsearch cluster to use. - `timeoutMs`: optional parameter for setting the client timeout (frontend) in milliseconds. - `timeoutMsBackend`: optional parameter for setting the client timeout (CLI) in milliseconds. This value is typically higher than ``timeoutMs``. @@ -30,11 +48,18 @@ pimcore_ecommerce_framework: index_service: tenants: MyEsTenant: + worker_id: Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\Worker\ElasticSearch\DefaultElasticSearch8 + config_id: Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\Config\ElasticSearch + config_options: client_config: logging: false indexName: 'ecommerce-demo-elasticsearch' + # elasticsearch client name, for Elasticsearch 8 only + es_client_name: default + + # deprecated, for Elasticsearch 7 only es_client_params: hosts: - '%elasticsearch.host%' @@ -69,8 +94,7 @@ pimcore_ecommerce_framework: ## Data Types for attributes -The type of the data attributes needs to be set to elasticsearch data types. Be careful, some types changed between -elasticsearch 5 and 6 (like string vs. keyword/text). +The type of the data attributes needs to be set to elasticsearch data types.. ```yml pimcore_ecommerce_framework: diff --git a/doc/Development_Documentation/10_E-Commerce_Framework/05_Index_Service/01_Product_Index_Configuration/07_Elastic_Search/README.md b/doc/Development_Documentation/10_E-Commerce_Framework/05_Index_Service/01_Product_Index_Configuration/07_Elastic_Search/README.md index 180e6b57b86..b7fa247b704 100644 --- a/doc/Development_Documentation/10_E-Commerce_Framework/05_Index_Service/01_Product_Index_Configuration/07_Elastic_Search/README.md +++ b/doc/Development_Documentation/10_E-Commerce_Framework/05_Index_Service/01_Product_Index_Configuration/07_Elastic_Search/README.md @@ -1,10 +1,15 @@ # Special Aspects for Elasticsearch Basically Elasticsearch worker works as described in the [optimized architecture](../README.md). -Currently, Elasticsearch 7 is supported. +Currently, Elasticsearch 7 (deprecated) and Elasticsearch 8 are supported. ## Installation + +### Elasticsearch 7 (deprecated) To work properly Pimcore requires the Elasticsearch bindings, install them with: `composer require elasticsearch/elasticsearch`. +### Elasticsearch 8 +To work properly Pimcore requires the Elasticsearch client, install them with: `composer require pimcore/elasticsearch-client`. + ## Index Configuration Elasticsearch provides a couple of additional configuration options for the index to utilize elasticsearch features. See [Configuration Details](01_Configuration_Details.md) for more information. diff --git a/doc/Development_Documentation/23_Installation_and_Upgrade/09_Upgrade_Notes/README.md b/doc/Development_Documentation/23_Installation_and_Upgrade/09_Upgrade_Notes/README.md index 18d0fa61ce7..e04e5638415 100644 --- a/doc/Development_Documentation/23_Installation_and_Upgrade/09_Upgrade_Notes/README.md +++ b/doc/Development_Documentation/23_Installation_and_Upgrade/09_Upgrade_Notes/README.md @@ -7,6 +7,8 @@ - [DataObject]: Since the `o_` prefix will be removed in Pimcore 11, a new method has been added: `DataObject\Service::getVersionDependentDatabaseColumnName()`. This method will return the field/column name for the current version and provide a way to support both version for bundles. E.g. passing `o_id` in Pimcore 10 will return `o_id`, but `id` in Pimcore 11. +- [Ecommerce] Elasticsearch 7 support has been deprecated, elasticsearch 8 supported was added. + ## 10.5.10 - [DataObject] Deprecated: Loading non-Concrete objects with the Concrete class will not be possible in Pimcore 11. diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 8d049a96237..d7abf87562c 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -260,3 +260,52 @@ parameters: count: 1 path: models/Element/Service.php + - + message: "#^Class Elasticsearch\\\\Client not found\\.$#" + count: 1 + path: bundles/EcommerceFrameworkBundle/IndexService/ProductList/ElasticSearch/AbstractElasticSearch.php + + - + message: "#^PHPDoc tag (.*) contains unknown class Elasticsearch\\\\Client\\.$#" + count: 1 + path: bundles/EcommerceFrameworkBundle/IndexService/ProductList/ElasticSearch/AbstractElasticSearch.php + + - + message: "#^Call to method (.*) on an unknown class Elasticsearch\\\\Client\\.$#" + count: 2 + path: bundles/EcommerceFrameworkBundle/IndexService/ProductList/ElasticSearch/AbstractElasticSearch.php + + - + message: "#^Property (.*) has unknown class Elasticsearch\\\\Client as its type\\.$#" + count: 1 + path: bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/AbstractElasticSearch.php + + - + message: "#^(.*) Elasticsearch\\\\Common\\\\Exceptions\\\\Missing404Exception not found\\.$#" + count: 2 + path: bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/AbstractElasticSearch.php + + - + message: "#^Method (.*) has invalid return type Elasticsearch\\\\Client\\.$#" + count: 1 + path: bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/AbstractElasticSearch.php + + - + message: "#^Call to method (.*) on an unknown class Elasticsearch\\\\Client\\.$#" + count: 26 + path: bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/AbstractElasticSearch.php + + - + message: "#^Call to static method (.*) on an unknown class Elasticsearch\\\\ClientBuilder\\.$#" + count: 1 + path: bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/AbstractElasticSearch.php + + - + message: "#^PHPDoc tag \\@throws with type Elasticsearch\\\\Common\\\\Exceptions\\\\BadRequest400Exception\\|Elasticsearch\\\\Common\\\\Exceptions\\\\NoNodesAvailableException is not subtype of Throwable$#" + count: 2 + path: bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/AbstractElasticSearch.php + + - + message: "#^PHPDoc type Elastic\\\\Elasticsearch\\\\Client\\|null of property Pimcore\\\\Bundle\\\\EcommerceFrameworkBundle\\\\IndexService\\\\Worker\\\\ElasticSearch\\\\DefaultElasticSearch8\\:\\:\\$elasticSearchClient is not covariant with PHPDoc type Elasticsearch\\\\Client\\|null of overridden property Pimcore\\\\Bundle\\\\EcommerceFrameworkBundle\\\\IndexService\\\\Worker\\\\ElasticSearch\\\\AbstractElasticSearch\\:\\:\\$elasticSearchClient\\.$#" + count: 1 + path: bundles/EcommerceFrameworkBundle/IndexService/Worker/ElasticSearch/DefaultElasticSearch8.php