vendor/pimcore/pimcore/bundles/EcommerceFrameworkBundle/IndexService/Config/ElasticSearch.php line 176

Open in your IDE?
  1. <?php
  2. /**
  3.  * Pimcore
  4.  *
  5.  * This source file is available under two different licenses:
  6.  * - GNU General Public License version 3 (GPLv3)
  7.  * - Pimcore Commercial License (PCL)
  8.  * Full copyright and license information is available in
  9.  * LICENSE.md which is distributed with this source code.
  10.  *
  11.  *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12.  *  @license    http://www.pimcore.org/license     GPLv3 and PCL
  13.  */
  14. namespace Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\Config;
  15. use Pimcore\Bundle\EcommerceFrameworkBundle\EnvironmentInterface;
  16. use Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\Config\Definition\Attribute;
  17. use Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\Interpreter\RelationInterpreterInterface;
  18. use Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\SynonymProvider\SynonymProviderInterface;
  19. use Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\Worker\ElasticSearch\AbstractElasticSearch as DefaultElasticSearchWorker;
  20. use Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\Worker\WorkerInterface;
  21. use Pimcore\Bundle\EcommerceFrameworkBundle\Model\DefaultMockup;
  22. use Pimcore\Bundle\EcommerceFrameworkBundle\Model\IndexableInterface;
  23. use Pimcore\Bundle\EcommerceFrameworkBundle\Traits\OptionsResolverTrait;
  24. use Symfony\Component\OptionsResolver\OptionsResolver;
  25. /**
  26.  * Default configuration for elastic search as product index implementation.
  27.  *
  28.  * @method DefaultElasticSearchWorker getTenantWorker()
  29.  */
  30. class ElasticSearch extends AbstractConfig implements MockupConfigInterfaceElasticSearchConfigInterface
  31. {
  32.     use OptionsResolverTrait;
  33.     /**
  34.      * @var array
  35.      */
  36.     protected $clientConfig = [];
  37.     /**
  38.      * @var array
  39.      */
  40.     protected $indexSettings = [];
  41.     /**
  42.      * @var array
  43.      */
  44.     protected $elasticSearchClientParams = [];
  45.     /**
  46.      * contains the mapping for the fields in Elasticsearch
  47.      *
  48.      * @var array
  49.      */
  50.     protected $fieldMapping = [
  51.         'o_id' => 'system.o_id',
  52.         'o_classId' => 'system.o_classId',
  53.         'o_virtualProductId' => 'system.o_virtualProductId',
  54.         'o_virtualProductActive' => 'system.o_virtualProductActive',
  55.         'o_parentId' => 'system.o_parentId',
  56.         'o_type' => 'system.o_type',
  57.         'categoryIds' => 'system.categoryIds',
  58.         'parentCategoryIds' => 'system.parentCategoryIds',
  59.         'categoryPaths' => 'system.categoryPaths',
  60.         'priceSystemName' => 'system.priceSystemName',
  61.         'active' => 'system.active',
  62.         'inProductList' => 'system.inProductList',
  63.     ];
  64.     /**
  65.      * @var EnvironmentInterface
  66.      */
  67.     protected $environment;
  68.     /** @var SynonymProviderInterface[] */
  69.     protected $synonymProviders = [];
  70.     /**
  71.      * {@inheritdoc}
  72.      *
  73.      * @param SynonymProviderInterface[] $synonymProviders
  74.      */
  75.     public function __construct(
  76.         string $tenantName,
  77.         array $attributes,
  78.         array $searchAttributes,
  79.         array $filterTypes,
  80.         array $options = [],
  81.         iterable $synonymProviders = []
  82.     ) {
  83.         $this->synonymProviders $synonymProviders;
  84.         parent::__construct($tenantName$attributes$searchAttributes$filterTypes$options);
  85.     }
  86.     protected function addAttribute(Attribute $attribute)
  87.     {
  88.         parent::addAttribute($attribute);
  89.         $attributeType 'attributes';
  90.         if (null !== $attribute->getInterpreter() && $attribute->getInterpreter() instanceof RelationInterpreterInterface) {
  91.             $attributeType 'relations';
  92.         }
  93.         $this->fieldMapping[$attribute->getName()] = sprintf('%s.%s'$attributeType$attribute->getName());
  94.     }
  95.     protected function addSearchAttribute(string $searchAttribute)
  96.     {
  97.         if (isset($this->attributes[$searchAttribute])) {
  98.             $this->searchAttributes[] = $searchAttribute;
  99.             return;
  100.         }
  101.         $fieldNameParts $this->extractPossibleFirstSubFieldnameParts($searchAttribute);
  102.         foreach ($fieldNameParts as $fieldNamePart) {
  103.             if (isset($this->attributes[$fieldNamePart])) {
  104.                 $this->searchAttributes[] = $searchAttribute;
  105.                 return;
  106.             }
  107.         }
  108.         throw new \InvalidArgumentException(sprintf(
  109.             'The search attribute "%s" in product index tenant "%s" is not defined as attribute',
  110.             $searchAttribute,
  111.             $this->tenantName
  112.         ));
  113.     }
  114.     protected function processOptions(array $options)
  115.     {
  116.         $options $this->resolveOptions($options);
  117.         // TODO validate client config and other settings/params?
  118.         $this->clientConfig $options['client_config'];
  119.         $this->indexSettings $options['index_settings'];
  120.         $this->elasticSearchClientParams $options['es_client_params'];
  121.         //add default type for elasticsearch
  122.         if (empty($this->elasticSearchClientParams['indexType'])) {
  123.             $this->elasticSearchClientParams['indexType'] = '_doc';
  124.         }
  125.     }
  126.     protected function configureOptionsResolver(string $resolverNameOptionsResolver $resolver)
  127.     {
  128.         $arrayFields = [
  129.             'client_config',
  130.             'index_settings',
  131.             'es_client_params',
  132.             'mapping',
  133.         ];
  134.         foreach ($arrayFields as $field) {
  135.             $resolver->setDefault($field, []);
  136.             $resolver->setAllowedTypes($field'array');
  137.         }
  138.         $resolver->setDefined('mapper');
  139.         $resolver->setAllowedTypes('mapper''string');
  140.         $resolver->setDefined('analyzer');
  141.         $resolver->setDefined('synonym_providers');
  142.         $resolver->setDefault('store'true);
  143.         $resolver->setAllowedTypes('store''bool');
  144.         $resolver->setDefined('es_client_name');
  145.         $resolver->setAllowedTypes('es_client_name''string');
  146.         //set options to deprecated
  147.         $resolver->setDeprecated('es_client_params');
  148.     }
  149.     /**
  150.      * @param string $fieldName
  151.      *
  152.      * @return array
  153.      */
  154.     protected function extractPossibleFirstSubFieldnameParts($fieldName)
  155.     {
  156.         $parts = [];
  157.         $delimiters = ['.''^'];
  158.         foreach ($delimiters as $delimiter) {
  159.             if (strpos($fieldName$delimiter) !== false) {
  160.                 $fieldNameParts explode($delimiter$fieldName);
  161.                 $parts[] = $fieldNameParts[0];
  162.             }
  163.         }
  164.         return $parts;
  165.     }
  166.     /** @inheritDoc */
  167.     public function getFieldNameMapped($fieldName$considerSubFieldNames false)
  168.     {
  169.         if (isset($this->fieldMapping[$fieldName])) {
  170.             return $this->fieldMapping[$fieldName];
  171.         }
  172.         // consider subfield names like name.analyzed or score definitions like name^3
  173.         if ($considerSubFieldNames) {
  174.             $fieldNameParts $this->extractPossibleFirstSubFieldnameParts($fieldName);
  175.             foreach ($fieldNameParts as $fieldNamePart) {
  176.                 if (isset($this->fieldMapping[$fieldNamePart])) {
  177.                     return $this->fieldMapping[$fieldNamePart] . str_replace($fieldNamePart''$fieldName);
  178.                 }
  179.             }
  180.         }
  181.         return $fieldName;
  182.     }
  183.     /** @inheritDoc */
  184.     public function getReverseMappedFieldName($fullFieldName)
  185.     {
  186.         //check for direct match of field name
  187.         $fieldName array_search($fullFieldName$this->fieldMapping);
  188.         if ($fieldName) {
  189.             return $fieldName;
  190.         }
  191.         //search for part match in order to consider sub field names like name.analyzed
  192.         $fieldNamePart $fullFieldName;
  193.         while (!empty($fieldNamePart)) {
  194.             // cut off part after last .
  195.             $fieldNamePart substr($fieldNamePart0strripos($fieldNamePart'.'));
  196.             // search for mapping with field name part
  197.             $fieldName array_search($fieldNamePart$this->fieldMapping);
  198.             if ($fieldName) {
  199.                 // append cut off part again to returned field name
  200.                 return $fieldName str_replace($fieldNamePart''$fullFieldName);
  201.             }
  202.         }
  203.         //return full field name if no mapping was found
  204.         return $fullFieldName;
  205.     }
  206.     /**
  207.      * @param string $property
  208.      *
  209.      * @return array|string
  210.      */
  211.     public function getClientConfig($property null)
  212.     {
  213.         if ($property) {
  214.             return $this->clientConfig[$property] ?? null;
  215.         }
  216.         return $this->clientConfig;
  217.     }
  218.     /**
  219.      * @return array
  220.      */
  221.     public function getIndexSettings()
  222.     {
  223.         return $this->indexSettings;
  224.     }
  225.     /**
  226.      * @return array
  227.      */
  228.     public function getElasticSearchClientParams()
  229.     {
  230.         return $this->elasticSearchClientParams;
  231.     }
  232.     /**
  233.      * checks, if product should be in index for current tenant
  234.      *
  235.      * @param IndexableInterface $object
  236.      *
  237.      * @return bool
  238.      */
  239.     public function inIndex(IndexableInterface $object)
  240.     {
  241.         return true;
  242.     }
  243.     /**
  244.      * in case of subtenants returns a data structure containing all sub tenants
  245.      *
  246.      * @param IndexableInterface $object
  247.      * @param int|null $subObjectId
  248.      *
  249.      * @return array $subTenantData
  250.      */
  251.     public function prepareSubTenantEntries(IndexableInterface $object$subObjectId null)
  252.     {
  253.         return [];
  254.     }
  255.     /**
  256.      * populates index for tenant relations based on gived data
  257.      *
  258.      * @param mixed $objectId
  259.      * @param mixed $subTenantData
  260.      * @param mixed $subObjectId
  261.      *
  262.      * @return void
  263.      */
  264.     public function updateSubTenantEntries($objectId$subTenantData$subObjectId null)
  265.     {
  266.         // nothing to do
  267.         return;
  268.     }
  269.     /**
  270.      * returns condition for current subtenant
  271.      *
  272.      * @return array
  273.      */
  274.     public function getSubTenantCondition()
  275.     {
  276.         if ($currentSubTenant $this->environment->getCurrentAssortmentSubTenant()) {
  277.             return ['term' => ['subtenants.ids' => $currentSubTenant]];
  278.         }
  279.         return [];
  280.     }
  281.     /**
  282.      * {@inheritdoc}
  283.      */
  284.     public function setTenantWorker(WorkerInterface $tenantWorker)
  285.     {
  286.         if (!$tenantWorker instanceof DefaultElasticSearchWorker) {
  287.             throw new \InvalidArgumentException(sprintf(
  288.                 'Worker must be an instance of %s',
  289.                 DefaultElasticSearchWorker::class
  290.             ));
  291.         }
  292.         parent::setTenantWorker($tenantWorker);
  293.     }
  294.     /**
  295.      * creates object mockup for given data
  296.      *
  297.      * @param int $objectId
  298.      * @param array $data
  299.      * @param array $relations
  300.      *
  301.      * @return DefaultMockup
  302.      */
  303.     public function createMockupObject($objectId$data$relations)
  304.     {
  305.         return new DefaultMockup($objectId$data$relations);
  306.     }
  307.     /**
  308.      * Gets object mockup by id, can consider subIds and therefore return e.g. an array of values
  309.      * always returns a object mockup if available
  310.      *
  311.      * @param int $objectId
  312.      *
  313.      * @return IndexableInterface|null
  314.      */
  315.     public function getObjectMockupById($objectId)
  316.     {
  317.         $listing $this->getTenantWorker()->getProductList();
  318.         $listing->addCondition($objectId'o_id');
  319.         $listing->setLimit(1);
  320.         $product $listing->current();
  321.         return $product $product null;
  322.     }
  323.     /**
  324.      * @required
  325.      *
  326.      * @param EnvironmentInterface $environment
  327.      */
  328.     public function setEnvironment(EnvironmentInterface $environment)
  329.     {
  330.         $this->environment $environment;
  331.     }
  332.     /**
  333.      * Get an associative array of configured synonym providers.
  334.      *  - key: the name of the synonym provider configuration, which is equivalent to the name of the configured filter
  335.      *  - value: the synonym provider
  336.      *
  337.      * @return SynonymProviderInterface[]
  338.      */
  339.     public function getSynonymProviders(): array
  340.     {
  341.         return $this->synonymProviders;
  342.     }
  343. }