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

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