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

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.         $this->doDeleteIndex($this->getIndexName());
  109.         return $this;
  110.     }
  111.     /**
  112.      * @return array
  113.      */
  114.     protected function extractSystemFieldsMapping()
  115.     {
  116.         $mappingProperties parent::extractSystemFieldsMapping();
  117.         $mappingProperties[ElasticSearchFields::SYSTEM_FIELDS]['properties'][ElasticSearchFields::SYSTEM_FIELDS_HAS_WORKFLOW_WITH_PERMISSIONS] = ['type' => 'boolean'];
  118.         return $mappingProperties;
  119.     }
  120.     /**
  121.      * @return array
  122.      */
  123.     public function extractMapping()
  124.     {
  125.         /** @var array $mappingProperties */
  126.         $mappingProperties $this->extractSystemFieldsMapping();
  127.         foreach (Configuration\Dao::getList(true) as $configuration) {
  128.             /** @var Data[] $fieldDefinitions */
  129.             $fieldDefinitions = [];
  130.             /** @var Data[] $localizedFieldDefinitions */
  131.             $localizedFieldDefinitions = [];
  132.             Service::extractDataDefinitions($configuration->getLayoutDefinitions(), false$fieldDefinitions$localizedFieldDefinitions);
  133.             foreach ($fieldDefinitions as $fieldDefinition) {
  134.                 if (!$fieldDefinition->getName()) {
  135.                     continue;
  136.                 }
  137.                 /** @var FieldDefinitionAdapterInterface|null $fieldDefinitionAdapter */
  138.                 $fieldDefinitionAdapter $this->fieldDefinitionService->getFieldDefinitionAdapter($fieldDefinition);
  139.                 if ($fieldDefinitionAdapter) {
  140.                     list($mappingKey$mappingEntry) = $fieldDefinitionAdapter->getESMapping();
  141.                     $mappingProperties[ElasticSearchFields::STANDARD_FIELDS]['properties'][$configuration->getName()]['properties'][$mappingKey] = $mappingEntry;
  142.                 }
  143.             }
  144.             foreach ($localizedFieldDefinitions as $fieldDefinition) {
  145.                 if (!$fieldDefinition->getName()) {
  146.                     continue;
  147.                 }
  148.                 /** @var FieldDefinitionAdapterInterface|null $fieldDefinitionAdapter */
  149.                 $fieldDefinitionAdapter $this->fieldDefinitionService->getFieldDefinitionAdapter($fieldDefinition);
  150.                 if ($fieldDefinitionAdapter instanceof DefaultAdapter) {
  151.                     foreach ($fieldDefinitionAdapter->getLocalizedESMapping() as $mappingKey => $mappingEntry) {
  152.                         $mappingProperties[ElasticSearchFields::STANDARD_FIELDS]['properties'][$configuration->getName()]['properties'][$mappingKey] = $mappingEntry;
  153.                     }
  154.                 }
  155.             }
  156.         }
  157.         $mappingProperties[ElasticSearchFields::CUSTOM_FIELDS] = [];
  158.         /** @var ExtractMappingEvent $extractMappingEvent */
  159.         $extractMappingEvent = new ExtractMappingEvent($mappingProperties[ElasticSearchFields::CUSTOM_FIELDS]);
  160.         $this->eventDispatcher->dispatch($extractMappingEvent);
  161.         $mappingProperties[ElasticSearchFields::CUSTOM_FIELDS]['properties'] = $extractMappingEvent->getCustomFieldsMapping();
  162.         $mappingParams = [
  163.             'index' => $this->getIndexName(),
  164.             'include_type_name' => false,
  165.             'body' => [
  166.                 '_source' => [
  167.                     'enabled' => true
  168.                 ],
  169.                 'properties' => $mappingProperties
  170.             ]
  171.         ];
  172.         return $mappingParams;
  173.     }
  174.     /**
  175.      * @param bool $forceCreateIndex
  176.      *
  177.      * @return $this
  178.      */
  179.     public function updateMapping($forceCreateIndex false)
  180.     {
  181.         if ($forceCreateIndex || !$this->esClient->indices()->existsAlias(['name' => $this->getIndexName()])) {
  182.             $this->createIndex();
  183.         }
  184.         try {
  185.             $this->doUpdateMapping();
  186.         } catch (\Exception $e) {
  187.             $this->logger->info($e);
  188.             $this->reindex($this->getIndexName(), $this->extractMapping());
  189.         }
  190.         return $this;
  191.     }
  192.     /**
  193.      * @return $this
  194.      */
  195.     protected function doUpdateMapping()
  196.     {
  197.         $mapping $this->extractMapping();
  198.         $response $this->esClient->indices()->putMapping($mapping);
  199.         $this->logger->debug(json_encode($response));
  200.         return $this;
  201.     }
  202.     /**
  203.      * @param Asset $asset
  204.      *
  205.      * @return array
  206.      */
  207.     public function getIndexData($asset)
  208.     {
  209.         /** @var array $systemFields */
  210.         $systemFields $this->getCoreFieldsIndexData($asset);
  211.         /** @var array $standardFields */
  212.         $standardFields = [];
  213.         /** @var array $customFields */
  214.         $customFields = [];
  215.         /** @var Collections|null $collections */
  216.         $collections Collections::getByAssetId($asset->getId());
  217.         foreach ($collections->getCollections() as $configurationName) {
  218.             /** @var Configuration|null $configuration */
  219.             $configuration Configuration\Dao::getByName($configurationName);
  220.             if ($configuration) {
  221.                 /** @var Data[] $fieldDefinitions */
  222.                 $fieldDefinitions = [];
  223.                 /** @var Data[] $localizedFieldDefinitions */
  224.                 $localizedFieldDefinitions = [];
  225.                 Service::extractDataDefinitions($configuration->getLayoutDefinitions(), false$fieldDefinitions$localizedFieldDefinitions);
  226.                 foreach ($fieldDefinitions as $key => $fieldDefinition) {
  227.                     /** @var FieldDefinitionAdapterInterface|null $fieldDefinitionAdapter */
  228.                     $fieldDefinitionAdapter $this->fieldDefinitionService->getFieldDefinitionAdapter($fieldDefinition);
  229.                     if ($fieldDefinitionAdapter) {
  230.                         $standardFields[$configuration->getName()][$key] = $fieldDefinitionAdapter->getIndexData($asset$configuration);
  231.                     }
  232.                 }
  233.                 foreach ($localizedFieldDefinitions as $key => $fieldDefinition) {
  234.                     /** @var FieldDefinitionAdapterInterface|null $fieldDefinitionAdapter */
  235.                     $fieldDefinitionAdapter $this->fieldDefinitionService->getFieldDefinitionAdapter($fieldDefinition);
  236.                     if ($fieldDefinitionAdapter) {
  237.                         $standardFields[$configuration->getName()][$key] = $fieldDefinitionAdapter->getIndexData($asset$configurationtrue);
  238.                     }
  239.                 }
  240.             }
  241.         }
  242.         //dispatch event before building checksum
  243.         /** @var UpdateIndexDataEvent $updateIndexDataEvent */
  244.         $updateIndexDataEvent = new UpdateIndexDataEvent($asset$customFields);
  245.         $this->eventDispatcher->dispatch($updateIndexDataEvent);
  246.         $customFields $updateIndexDataEvent->getCustomFields();
  247.         /** @var string $checksum */
  248.         $checksum $checksum crc32(json_encode([$systemFields$standardFields$customFields]));
  249.         $systemFields[ElasticSearchFields::SYSTEM_FIELDS_CHECKSUM] = $checksum;
  250.         $params = [
  251.             'index' => $this->elasticSearchConfigService->getIndexName('asset'),
  252.             'type' => '_doc',
  253.             'id' => $asset->getId(),
  254.             'refresh' => $this->performIndexRefresh,
  255.             'body' => [
  256.                 ElasticSearchFields::SYSTEM_FIELDS => $systemFields,
  257.                 ElasticSearchFields::STANDARD_FIELDS => $standardFields,
  258.                 ElasticSearchFields::CUSTOM_FIELDS => $customFields
  259.             ]
  260.         ];
  261.         return $params;
  262.     }
  263.     /**
  264.      * @param Asset $asset
  265.      *
  266.      * @return $this
  267.      */
  268.     public function doUpdateIndexData($asset)
  269.     {
  270.         $params = [
  271.             'index' => $this->elasticSearchConfigService->getIndexName('asset'),
  272.             'type' => '_doc',
  273.             'id' => $asset->getId()
  274.         ];
  275.         try {
  276.             $indexDocument $this->esClient->get($params);
  277.             $originalChecksum $indexDocument['_source'][ElasticSearchFields::SYSTEM_FIELDS][ElasticSearchFields::SYSTEM_FIELDS_CHECKSUM] ?? -1;
  278.         } catch (\Exception $e) {
  279.             $this->logger->debug($e->getMessage());
  280.             $originalChecksum = -1;
  281.         }
  282.         $indexUpdateParams $this->getIndexData($asset);
  283.         if ($indexUpdateParams['body'][ElasticSearchFields::SYSTEM_FIELDS][ElasticSearchFields::SYSTEM_FIELDS_CHECKSUM] != $originalChecksum) {
  284.             $response $this->esClient->index($indexUpdateParams);
  285.             $this->logger->info('Updates es index for asset ' $asset->getId());
  286.             $this->logger->debug(json_encode($response));
  287.         } else {
  288.             $this->logger->info('Not updating index for asset ' $asset->getId() . ' - nothing has changed.');
  289.         }
  290.         return $this;
  291.     }
  292.     /**
  293.      * @param int $elementId
  294.      * @param string $elementIndexName
  295.      *
  296.      * @return $this
  297.      */
  298.     public function doDeleteFromIndex($elementId$elementIndexName)
  299.     {
  300.         $params = [
  301.             'index' => $this->elasticSearchConfigService->getIndexName($elementIndexName),
  302.             'type' => '_doc',
  303.             'id' => $elementId,
  304.             'refresh' => $this->performIndexRefresh
  305.         ];
  306.         try {
  307.             $response $this->esClient->delete($params);
  308.             $this->logger->info('Deleting asset ' $elementId ' from es index.');
  309.             $this->logger->debug(json_encode($response));
  310.         } catch (Missing404Exception $e) {
  311.             $this->logger->info('Cannot delete asset ' $elementId ' from es index because not found.');
  312.         }
  313.         return $this;
  314.     }
  315.     /**
  316.      * returns core fields index data array for given $asset
  317.      *
  318.      * @param Asset $asset
  319.      *
  320.      * @return array
  321.      */
  322.     public function getCoreFieldsIndexData($asset)
  323.     {
  324.         $date = new \DateTime();
  325.         return [
  326.             ElasticSearchFields::SYSTEM_FIELDS_ID => $asset->getId(),
  327.             ElasticSearchFields::SYSTEM_FIELDS_CREATION_DATE => $date->setTimestamp($asset->getCreationDate())->format(\DateTime::ISO8601),
  328.             ElasticSearchFields::SYSTEM_FIELDS_MODIFICATION_DATE => $date->setTimestamp($asset->getModificationDate())->format(\DateTime::ISO8601),
  329.             ElasticSearchFields::SYSTEM_FIELDS_TYPE => $asset->getType(),
  330.             ElasticSearchFields::SYSTEM_FIELDS_KEY => $asset->getKey(),
  331.             ElasticSearchFields::SYSTEM_FIELDS_PATH => $asset->getPath(),
  332.             ElasticSearchFields::SYSTEM_FIELDS_FULL_PATH => $asset->getRealFullPath(),
  333.             ElasticSearchFields::SYSTEM_FIELDS_PATH_LEVELS => $this->extractPathLevels($asset->getType() === 'folder' $asset->getRealFullPath() : $asset->getPath()),
  334.             ElasticSearchFields::SYSTEM_FIELDS_TAGS => $this->extractTagIds($asset),
  335.             ElasticSearchFields::SYSTEM_FIELDS_MIME_TYPE => $asset->getMimetype(),
  336.             ElasticSearchFields::SYSTEM_FIELDS_NAME => $this->nameExtractorService->extractAllLanguageNames($asset),
  337.             ElasticSearchFields::SYSTEM_FIELDS_THUMBNAIL => $this->thumbnailService->getThumbnailPath($assetImageThumbnails::ELEMENT_TEASER),
  338.             ElasticSearchFields::SYSTEM_FIELDS_COLLECTIONS => $this->getCollectionIdsByElement($asset),
  339.             ElasticSearchFields::SYSTEM_FIELDS_PUBLIC_SHARES => $this->getPublicShareIdsByElement($asset),
  340.             ElasticSearchFields::SYSTEM_FIELDS_USER_OWNER => $asset->getUserOwner(),
  341.             ElasticSearchFields::SYSTEM_FIELDS_HAS_WORKFLOW_WITH_PERMISSIONS => $this->workflowService->hasWorkflowWithPermissions($asset),
  342.             ElasticSearchFields::SYSTEM_FIELDS_FILE_SIZE => $asset->getFileSize()
  343.         ];
  344.     }
  345.     /**
  346.      * Called in index.yml
  347.      *
  348.      * @param array $coreFieldsConfig
  349.      */
  350.     public function setCoreFieldsConfig(array $coreFieldsConfig)
  351.     {
  352.         if (is_array($coreFieldsConfig['general']) && is_array($coreFieldsConfig['asset'])) {
  353.             $this->coreFieldsConfig array_merge($coreFieldsConfig['general'], $coreFieldsConfig['asset']);
  354.         }
  355.     }
  356. }