vendor/pimcore/portal-engine/src/Service/SearchIndex/Asset/IndexService.php line 329

Open in your IDE?
  1. <?php
  2. /**
  3.  * Pimcore
  4.  *
  5.  * This source file is available under following license:
  6.  * - Pimcore Commercial License (PCL)
  7.  *
  8.  *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  9.  *  @license    http://www.pimcore.org/license     PCL
  10.  */
  11. namespace Pimcore\Bundle\PortalEngineBundle\Service\SearchIndex\Asset;
  12. use Elasticsearch\Common\Exceptions\Missing404Exception;
  13. use Pimcore\AssetMetadataClassDefinitionsBundle\Model\ClassDefinition\Data\Data;
  14. use Pimcore\AssetMetadataClassDefinitionsBundle\Model\Collections;
  15. use Pimcore\AssetMetadataClassDefinitionsBundle\Model\Configuration;
  16. use Pimcore\AssetMetadataClassDefinitionsBundle\Service;
  17. use Pimcore\Bundle\PortalEngineBundle\Enum\ElasticSearchFields;
  18. use Pimcore\Bundle\PortalEngineBundle\Enum\ImageThumbnails;
  19. use Pimcore\Bundle\PortalEngineBundle\Event\Asset\ExtractMappingEvent;
  20. use Pimcore\Bundle\PortalEngineBundle\Event\Asset\UpdateIndexDataEvent;
  21. use Pimcore\Bundle\PortalEngineBundle\Service\Asset\SearchIndexFieldDefinitionService;
  22. use Pimcore\Bundle\PortalEngineBundle\Service\Asset\ThumbnailService;
  23. use Pimcore\Bundle\PortalEngineBundle\Service\SearchIndex\Asset\FieldDefinitionAdapter\DefaultAdapter;
  24. use Pimcore\Bundle\PortalEngineBundle\Service\SearchIndex\Asset\FieldDefinitionAdapter\FieldDefinitionAdapterInterface;
  25. use Pimcore\Bundle\PortalEngineBundle\Service\Workflow\WorkflowService;
  26. use Pimcore\Model\Asset;
  27. /**
  28.  * Class IndexService
  29.  *
  30.  * @package Pimcore\Bundle\PortalEngineBundle\Service\SearchIndex\Asset
  31.  */
  32. class IndexService extends \Pimcore\Bundle\PortalEngineBundle\Service\SearchIndex\AbstractIndexService
  33. {
  34.     /** @var SearchIndexFieldDefinitionService */
  35.     protected $fieldDefinitionService;
  36.     /** @var ThumbnailService */
  37.     protected $thumbnailService;
  38.     /** @var WorkflowService */
  39.     protected $workflowService;
  40.     /**
  41.      * @param SearchIndexFieldDefinitionService $fieldDefinitionService
  42.      * @required
  43.      */
  44.     public function setFieldDefinitionService(SearchIndexFieldDefinitionService $fieldDefinitionService)
  45.     {
  46.         $this->fieldDefinitionService $fieldDefinitionService;
  47.     }
  48.     /**
  49.      * @param ThumbnailService $thumbnailService
  50.      * @required
  51.      */
  52.     public function setThumbnailService(ThumbnailService $thumbnailService)
  53.     {
  54.         $this->thumbnailService $thumbnailService;
  55.     }
  56.     /**
  57.      * @param WorkflowService $workflowService
  58.      * @required
  59.      */
  60.     public function setWorkflowService(WorkflowService $workflowService)
  61.     {
  62.         $this->workflowService $workflowService;
  63.     }
  64.     /**
  65.      * @return string
  66.      */
  67.     protected function getIndexName(): string
  68.     {
  69.         return $this->elasticSearchConfigService->getIndexName('asset');
  70.     }
  71.     /**
  72.      * @return string
  73.      */
  74.     protected function getCurrentFullIndexName(): string
  75.     {
  76.         $indexName $this->getIndexName();
  77.         $currentIndexVersion $this->getCurrentIndexVersion($indexName);
  78.         return $indexName '-' . ($currentIndexVersion === 'even' 'even' 'odd');
  79.     }
  80.     /**
  81.      * @return $this
  82.      */
  83.     public function createIndex()
  84.     {
  85.         //create index
  86.         $fullIndexName $this->getCurrentFullIndexName();
  87.         $this->doCreateIndex($fullIndexName);
  88.         //update alias
  89.         $params['body'] = [
  90.             'actions' => [
  91.                 [
  92.                     'add' => [
  93.                         'index' => $fullIndexName,
  94.                         'alias' => $this->getIndexName(),
  95.                     ],
  96.                 ],
  97.             ],
  98.         ];
  99.         $this->esClient->indices()->updateAliases($params);
  100.         return $this;
  101.     }
  102.     /**
  103.      * @return $this
  104.      */
  105.     public function deleteIndex()
  106.     {
  107.         $this->doDeleteIndex($this->getCurrentFullIndexName());
  108.         return $this;
  109.     }
  110.     protected function extractSystemFieldsMapping()
  111.     {
  112.         $mappingProperties parent::extractSystemFieldsMapping();
  113.         $mappingProperties[ElasticSearchFields::SYSTEM_FIELDS]['properties'][ElasticSearchFields::SYSTEM_FIELDS_HAS_WORKFLOW_WITH_PERMISSIONS] = ['type' => 'boolean'];
  114.         return $mappingProperties;
  115.     }
  116.     /**
  117.      * @return array
  118.      */
  119.     public function extractMapping()
  120.     {
  121.         /** @var array $mappingProperties */
  122.         $mappingProperties $this->extractSystemFieldsMapping();
  123.         foreach (Configuration\Dao::getList(true) as $configuration) {
  124.             /** @var Data[] $fieldDefinitions */
  125.             $fieldDefinitions = [];
  126.             /** @var Data[] $localizedFieldDefinitions */
  127.             $localizedFieldDefinitions = [];
  128.             Service::extractDataDefinitions($configuration->getLayoutDefinitions(), false$fieldDefinitions$localizedFieldDefinitions);
  129.             foreach ($fieldDefinitions as $fieldDefinition) {
  130.                 /** @var FieldDefinitionAdapterInterface|null $fieldDefinitionAdapter */
  131.                 $fieldDefinitionAdapter $this->fieldDefinitionService->getFieldDefinitionAdapter($fieldDefinition);
  132.                 if ($fieldDefinitionAdapter) {
  133.                     list($mappingKey$mappingEntry) = $fieldDefinitionAdapter->getESMapping();
  134.                     $mappingProperties[ElasticSearchFields::STANDARD_FIELDS]['properties'][$configuration->getName()]['properties'][$mappingKey] = $mappingEntry;
  135.                 }
  136.             }
  137.             foreach ($localizedFieldDefinitions as $fieldDefinition) {
  138.                 /** @var FieldDefinitionAdapterInterface|null $fieldDefinitionAdapter */
  139.                 $fieldDefinitionAdapter $this->fieldDefinitionService->getFieldDefinitionAdapter($fieldDefinition);
  140.                 if ($fieldDefinitionAdapter instanceof DefaultAdapter) {
  141.                     foreach ($fieldDefinitionAdapter->getLocalizedESMapping() as $mappingKey => $mappingEntry) {
  142.                         $mappingProperties[ElasticSearchFields::STANDARD_FIELDS]['properties'][$configuration->getName()]['properties'][$mappingKey] = $mappingEntry;
  143.                     }
  144.                 }
  145.             }
  146.         }
  147.         $mappingProperties[ElasticSearchFields::CUSTOM_FIELDS] = [];
  148.         /** @var ExtractMappingEvent $extractMappingEvent */
  149.         $extractMappingEvent = new ExtractMappingEvent($mappingProperties[ElasticSearchFields::CUSTOM_FIELDS]);
  150.         $this->eventDispatcher->dispatch($extractMappingEvent);
  151.         $mappingProperties[ElasticSearchFields::CUSTOM_FIELDS]['properties'] = $extractMappingEvent->getCustomFieldsMapping();
  152.         $mappingParams = [
  153.             'index' => $this->getIndexName(),
  154.             'include_type_name' => false,
  155.             'body' => [
  156.                 '_source' => [
  157.                     'enabled' => true
  158.                 ],
  159.                 'properties' => $mappingProperties
  160.             ]
  161.         ];
  162.         return $mappingParams;
  163.     }
  164.     /**
  165.      * @param bool $forceCreateIndex
  166.      *
  167.      * @return $this
  168.      */
  169.     public function updateMapping($forceCreateIndex false)
  170.     {
  171.         if ($forceCreateIndex || !$this->esClient->indices()->existsAlias(['name' => $this->getIndexName()])) {
  172.             $this->createIndex();
  173.         }
  174.         try {
  175.             $this->doUpdateMapping();
  176.         } catch (\Exception $e) {
  177.             $this->logger->info($e);
  178.             $this->reindex($this->getIndexName(), $this->extractMapping());
  179.         }
  180.         return $this;
  181.     }
  182.     /**
  183.      * @return $this
  184.      */
  185.     protected function doUpdateMapping()
  186.     {
  187.         $mapping $this->extractMapping();
  188.         $response $this->esClient->indices()->putMapping($mapping);
  189.         $this->logger->debug(json_encode($response));
  190.         return $this;
  191.     }
  192.     /**
  193.      * @param Asset $asset
  194.      *
  195.      * @return array
  196.      */
  197.     public function getIndexData($asset)
  198.     {
  199.         /** @var array $systemFields */
  200.         $systemFields $this->getCoreFieldsIndexData($asset);
  201.         /** @var array $standardFields */
  202.         $standardFields = [];
  203.         /** @var array $customFields */
  204.         $customFields = [];
  205.         /** @var Collections|null $collections */
  206.         $collections Collections::getByAssetId($asset->getId());
  207.         foreach ($collections->getCollections() as $configurationName) {
  208.             /** @var Configuration|null $configuration */
  209.             $configuration Configuration\Dao::getByName($configurationName);
  210.             if ($configuration) {
  211.                 /** @var Data[] $fieldDefinitions */
  212.                 $fieldDefinitions = [];
  213.                 /** @var Data[] $localizedFieldDefinitions */
  214.                 $localizedFieldDefinitions = [];
  215.                 Service::extractDataDefinitions($configuration->getLayoutDefinitions(), false$fieldDefinitions$localizedFieldDefinitions);
  216.                 foreach ($fieldDefinitions as $key => $fieldDefinition) {
  217.                     /** @var FieldDefinitionAdapterInterface|null $fieldDefinitionAdapter */
  218.                     $fieldDefinitionAdapter $this->fieldDefinitionService->getFieldDefinitionAdapter($fieldDefinition);
  219.                     if ($fieldDefinitionAdapter) {
  220.                         $standardFields[$configuration->getName()][$key] = $fieldDefinitionAdapter->getIndexData($asset$configuration);
  221.                     }
  222.                 }
  223.                 foreach ($localizedFieldDefinitions as $key => $fieldDefinition) {
  224.                     /** @var FieldDefinitionAdapterInterface|null $fieldDefinitionAdapter */
  225.                     $fieldDefinitionAdapter $this->fieldDefinitionService->getFieldDefinitionAdapter($fieldDefinition);
  226.                     if ($fieldDefinitionAdapter) {
  227.                         $standardFields[$configuration->getName()][$key] = $fieldDefinitionAdapter->getIndexData($asset$configurationtrue);
  228.                     }
  229.                 }
  230.             }
  231.         }
  232.         //dispatch event before building checksum
  233.         /** @var UpdateIndexDataEvent $updateIndexDataEvent */
  234.         $updateIndexDataEvent = new UpdateIndexDataEvent($asset$customFields);
  235.         $this->eventDispatcher->dispatch($updateIndexDataEvent);
  236.         $customFields $updateIndexDataEvent->getCustomFields();
  237.         /** @var string $checksum */
  238.         $checksum $checksum crc32(json_encode([$systemFields$standardFields$customFields]));
  239.         $systemFields[ElasticSearchFields::SYSTEM_FIELDS_CHECKSUM] = $checksum;
  240.         $params = [
  241.             'index' => $this->elasticSearchConfigService->getIndexName('asset'),
  242.             'type' => '_doc',
  243.             'id' => $asset->getId(),
  244.             'refresh' => $this->performIndexRefresh,
  245.             'body' => [
  246.                 ElasticSearchFields::SYSTEM_FIELDS => $systemFields,
  247.                 ElasticSearchFields::STANDARD_FIELDS => $standardFields,
  248.                 ElasticSearchFields::CUSTOM_FIELDS => $customFields
  249.             ]
  250.         ];
  251.         return $params;
  252.     }
  253.     /**
  254.      * @param Asset $asset
  255.      *
  256.      * @return $this
  257.      */
  258.     public function doUpdateIndexData($asset)
  259.     {
  260.         $params = [
  261.             'index' => $this->elasticSearchConfigService->getIndexName('asset'),
  262.             'type' => '_doc',
  263.             'id' => $asset->getId()
  264.         ];
  265.         try {
  266.             $indexDocument $this->esClient->get($params);
  267.             $originalChecksum $indexDocument['_source'][ElasticSearchFields::SYSTEM_FIELDS][ElasticSearchFields::SYSTEM_FIELDS_CHECKSUM] ?? -1;
  268.         } catch (\Exception $e) {
  269.             $this->logger->debug($e->getMessage());
  270.             $originalChecksum = -1;
  271.         }
  272.         $indexUpdateParams $this->getIndexData($asset);
  273.         if ($indexUpdateParams['body'][ElasticSearchFields::SYSTEM_FIELDS][ElasticSearchFields::SYSTEM_FIELDS_CHECKSUM] != $originalChecksum) {
  274.             $response $this->esClient->index($indexUpdateParams);
  275.             $this->logger->info('Updates es index for asset ' $asset->getId());
  276.             $this->logger->debug(json_encode($response));
  277.         } else {
  278.             $this->logger->info('Not updating index for asset ' $asset->getId() . ' - nothing has changed.');
  279.         }
  280.         return $this;
  281.     }
  282.     /**
  283.      * @param int $elementId
  284.      * @param string $elementIndexName
  285.      *
  286.      * @return $this
  287.      */
  288.     public function doDeleteFromIndex($elementId$elementIndexName)
  289.     {
  290.         $params = [
  291.             'index' => $this->elasticSearchConfigService->getIndexName($elementIndexName),
  292.             'type' => '_doc',
  293.             'id' => $elementId,
  294.             'refresh' => $this->performIndexRefresh
  295.         ];
  296.         try {
  297.             $response $this->esClient->delete($params);
  298.             $this->logger->info('Deleting asset ' $elementId ' from es index.');
  299.             $this->logger->debug(json_encode($response));
  300.         } catch (Missing404Exception $e) {
  301.             $this->logger->info('Cannot delete asset ' $elementId ' from es index because not found.');
  302.         }
  303.         return $this;
  304.     }
  305.     /**
  306.      * returns core fields index data array for given $asset
  307.      *
  308.      * @param Asset $asset
  309.      *
  310.      * @return array
  311.      */
  312.     public function getCoreFieldsIndexData($asset)
  313.     {
  314.         $date = new \DateTime();
  315.         return [
  316.             ElasticSearchFields::SYSTEM_FIELDS_ID => $asset->getId(),
  317.             ElasticSearchFields::SYSTEM_FIELDS_CREATION_DATE => $date->setTimestamp($asset->getCreationDate())->format(\DateTime::ISO8601),
  318.             ElasticSearchFields::SYSTEM_FIELDS_MODIFICATION_DATE => $date->setTimestamp($asset->getModificationDate())->format(\DateTime::ISO8601),
  319.             ElasticSearchFields::SYSTEM_FIELDS_TYPE => $asset->getType(),
  320.             ElasticSearchFields::SYSTEM_FIELDS_KEY => $asset->getKey(),
  321.             ElasticSearchFields::SYSTEM_FIELDS_PATH => $asset->getPath(),
  322.             ElasticSearchFields::SYSTEM_FIELDS_FULL_PATH => $asset->getRealFullPath(),
  323.             ElasticSearchFields::SYSTEM_FIELDS_PATH_LEVELS => $this->extractPathLevels($asset->getType() === 'folder' $asset->getRealFullPath() : $asset->getPath()),
  324.             ElasticSearchFields::SYSTEM_FIELDS_TAGS => $this->extractTagIds($asset),
  325.             ElasticSearchFields::SYSTEM_FIELDS_MIME_TYPE => $asset->getMimetype(),
  326.             ElasticSearchFields::SYSTEM_FIELDS_NAME => $this->nameExtractorService->extractAllLanguageNames($asset),
  327.             ElasticSearchFields::SYSTEM_FIELDS_THUMBNAIL => $this->thumbnailService->getThumbnailPath($assetImageThumbnails::ELEMENT_TEASER),
  328.             ElasticSearchFields::SYSTEM_FIELDS_COLLECTIONS => $this->getCollectionIdsByElement($asset),
  329.             ElasticSearchFields::SYSTEM_FIELDS_PUBLIC_SHARES => $this->getPublicShareIdsByElement($asset),
  330.             ElasticSearchFields::SYSTEM_FIELDS_USER_OWNER => $asset->getUserOwner(),
  331.             ElasticSearchFields::SYSTEM_FIELDS_HAS_WORKFLOW_WITH_PERMISSIONS => $this->workflowService->hasWorkflowWithPermissions($asset),
  332.             ElasticSearchFields::SYSTEM_FIELDS_FILE_SIZE => $asset->getFileSize()
  333.         ];
  334.     }
  335.     /**
  336.      * Called in index.yml
  337.      *
  338.      * @param array $coreFieldsConfig
  339.      */
  340.     public function setCoreFieldsConfig(array $coreFieldsConfig)
  341.     {
  342.         if (is_array($coreFieldsConfig['general']) && is_array($coreFieldsConfig['asset'])) {
  343.             $this->coreFieldsConfig array_merge($coreFieldsConfig['general'], $coreFieldsConfig['asset']);
  344.         }
  345.     }
  346. }