From beef99fda27a5da3adf6c6117ab418a33ec2430d Mon Sep 17 00:00:00 2001 From: markus-moser Date: Wed, 24 Jul 2024 09:18:17 +0200 Subject: [PATCH] Implement file size aggregation services for assets (#187) --- .github/ci/files/config/services_test.yaml | 4 ++ config/services/search/search-services.yaml | 3 + .../05_Search_Modifiers/README.md | 9 +-- .../SearchResult/AssetSearchResultItem.php | 2 +- .../Asset/FileSizeSumAggregation.php | 32 ++++++++++ .../SearchResultAggregation.php | 6 ++ .../SearchResultAggregationBucket.php | 2 +- .../Aggregation/AssetAggregations.php | 24 ++++++++ .../FileSizeAggregationService.php | 47 ++++++++++++++ .../FileSizeAggregationServiceInterface.php | 27 ++++++++ .../SearchResultDenormalizer.php | 1 + .../FileSizeAggregationServiceTest.php | 61 +++++++++++++++++++ 12 files changed, 212 insertions(+), 6 deletions(-) create mode 100644 src/Model/Search/Modifier/Aggregation/Asset/FileSizeSumAggregation.php create mode 100644 src/Service/Search/SearchService/Asset/Aggregation/FileSizeAggregationService.php create mode 100644 src/Service/Search/SearchService/Asset/Aggregation/FileSizeAggregationServiceInterface.php create mode 100644 tests/Functional/Search/SearchService/Asset/Aggregation/FileSizeAggregationServiceTest.php diff --git a/.github/ci/files/config/services_test.yaml b/.github/ci/files/config/services_test.yaml index 3cf6de42..48b18ef7 100644 --- a/.github/ci/files/config/services_test.yaml +++ b/.github/ci/files/config/services_test.yaml @@ -30,4 +30,8 @@ services: Pimcore\Bundle\GenericDataIndexBundle\Service\Transformer\SearchResultItem\DataObjectToSearchResultItemTransformerInterface: class: Pimcore\Bundle\GenericDataIndexBundle\Service\Transformer\SearchResultItem\DataObjectToSearchResultItemTransformer + public: true + + Pimcore\Bundle\GenericDataIndexBundle\Service\Search\SearchService\Asset\Aggregation\FileSizeAggregationServiceInterface: + class: Pimcore\Bundle\GenericDataIndexBundle\Service\Search\SearchService\Asset\Aggregation\FileSizeAggregationService public: true \ No newline at end of file diff --git a/config/services/search/search-services.yaml b/config/services/search/search-services.yaml index 429e34f0..4f82fd93 100644 --- a/config/services/search/search-services.yaml +++ b/config/services/search/search-services.yaml @@ -22,6 +22,9 @@ services: Pimcore\Bundle\GenericDataIndexBundle\Service\Search\SearchService\Element\ElementSearchHelperInterface: class: Pimcore\Bundle\GenericDataIndexBundle\Service\Search\SearchService\Element\SearchHelper + Pimcore\Bundle\GenericDataIndexBundle\Service\Search\SearchService\Asset\Aggregation\FileSizeAggregationServiceInterface: + class: Pimcore\Bundle\GenericDataIndexBundle\Service\Search\SearchService\Asset\Aggregation\FileSizeAggregationService + Pimcore\Bundle\GenericDataIndexBundle\Service\Search\SearchService\SearchProviderInterface: class: Pimcore\Bundle\GenericDataIndexBundle\Service\Search\SearchService\SearchProvider diff --git a/doc/04_Searching_For_Data_In_Index/05_Search_Modifiers/README.md b/doc/04_Searching_For_Data_In_Index/05_Search_Modifiers/README.md index cc9aa09a..14ffae5f 100644 --- a/doc/04_Searching_For_Data_In_Index/05_Search_Modifiers/README.md +++ b/doc/04_Searching_For_Data_In_Index/05_Search_Modifiers/README.md @@ -56,10 +56,11 @@ $search->addModifier(new ParentIdFilter(1)) ### Aggregations -| Modifier | Modifier Category | Description | -|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------|-------------------------------------------------------------------------------------------------------| -| [ChildrenCountAggregation](https://github.com/pimcore/generic-data-index-bundle/blob/1.x/src/Model/Search/Modifier/Aggregation/Tree/ChildrenCountAggregation.php) | Tree related aggregation | Get children counts for given element IDs. | -| [AssetMetaDataAggregation](https://github.com/pimcore/generic-data-index-bundle/blob/1.x/src/Model/Search/Modifier/Aggregation/Asset/AssetMetaDataAggregation.php) | Assets | Used for the filters in the asset grid to aggregate the filter options for supported meta data types. | +| Modifier | Modifier Category | Description | +|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [ChildrenCountAggregation](https://github.com/pimcore/generic-data-index-bundle/blob/1.x/src/Model/Search/Modifier/Aggregation/Tree/ChildrenCountAggregation.php) | Tree related aggregation | Get children counts for given element IDs. | +| [AssetMetaDataAggregation](https://github.com/pimcore/generic-data-index-bundle/blob/1.x/src/Model/Search/Modifier/Aggregation/Asset/AssetMetaDataAggregation.php) | Assets | Used for the filters in the asset grid to aggregate the filter options for supported meta data types. | +| [FileSizeSumAggregation](https://github.com/pimcore/generic-data-index-bundle/blob/1.x/src/Model/Search/Modifier/Aggregation/Asset/FileSizeSumAggregation.php) | Assets | Aggregates the sum of file sizes for all assets for a given search. The `FileSizeAggregationServiceInterface` internally uses this aggregation and provides an easy way to use this functionality. | ## Add your own search modifier diff --git a/src/Model/Search/Asset/SearchResult/AssetSearchResultItem.php b/src/Model/Search/Asset/SearchResult/AssetSearchResultItem.php index deb56e2f..84b80ae3 100644 --- a/src/Model/Search/Asset/SearchResult/AssetSearchResultItem.php +++ b/src/Model/Search/Asset/SearchResult/AssetSearchResultItem.php @@ -279,7 +279,7 @@ public function isHasChildren(): bool $this->lazyLoad(); } - return $this->hasChildren; + return $this->hasChildren ?? false; } public function setHasChildren(bool $hasChildren): AssetSearchResultItem diff --git a/src/Model/Search/Modifier/Aggregation/Asset/FileSizeSumAggregation.php b/src/Model/Search/Modifier/Aggregation/Asset/FileSizeSumAggregation.php new file mode 100644 index 00000000..a59cff67 --- /dev/null +++ b/src/Model/Search/Modifier/Aggregation/Asset/FileSizeSumAggregation.php @@ -0,0 +1,32 @@ +aggregationName; + } +} diff --git a/src/Model/SearchIndexAdapter/SearchResultAggregation.php b/src/Model/SearchIndexAdapter/SearchResultAggregation.php index 3827323e..28b1e96a 100644 --- a/src/Model/SearchIndexAdapter/SearchResultAggregation.php +++ b/src/Model/SearchIndexAdapter/SearchResultAggregation.php @@ -24,6 +24,7 @@ public function __construct( private array $buckets, private int $otherDocCount, private int $docCountErrorUpperBound, + private array $aggregationResult = [], ) { } @@ -46,4 +47,9 @@ public function getDocCountErrorUpperBound(): int { return $this->docCountErrorUpperBound; } + + public function getAggregationResult(): array + { + return $this->aggregationResult; + } } diff --git a/src/Model/SearchIndexAdapter/SearchResultAggregationBucket.php b/src/Model/SearchIndexAdapter/SearchResultAggregationBucket.php index 6ce3b666..28f8e9c2 100644 --- a/src/Model/SearchIndexAdapter/SearchResultAggregationBucket.php +++ b/src/Model/SearchIndexAdapter/SearchResultAggregationBucket.php @@ -19,7 +19,7 @@ readonly class SearchResultAggregationBucket { public function __construct( - private string|int $key, + private string|int|float $key, private int $docCount, ) { } diff --git a/src/SearchIndexAdapter/OpenSearch/Search/Modifier/Aggregation/AssetAggregations.php b/src/SearchIndexAdapter/OpenSearch/Search/Modifier/Aggregation/AssetAggregations.php index 6b04d8ad..95dd3347 100644 --- a/src/SearchIndexAdapter/OpenSearch/Search/Modifier/Aggregation/AssetAggregations.php +++ b/src/SearchIndexAdapter/OpenSearch/Search/Modifier/Aggregation/AssetAggregations.php @@ -18,8 +18,11 @@ use Pimcore\Bundle\GenericDataIndexBundle\Attribute\OpenSearch\AsSearchModifierHandler; use Pimcore\Bundle\GenericDataIndexBundle\Exception\InvalidModifierException; +use Pimcore\Bundle\GenericDataIndexBundle\Model\OpenSearch\Aggregation\Aggregation; use Pimcore\Bundle\GenericDataIndexBundle\Model\OpenSearch\Modifier\SearchModifierContextInterface; +use Pimcore\Bundle\GenericDataIndexBundle\Model\Search\Asset\AssetSearch; use Pimcore\Bundle\GenericDataIndexBundle\Model\Search\Modifier\Aggregation\Asset\AssetMetaDataAggregation; +use Pimcore\Bundle\GenericDataIndexBundle\Model\Search\Modifier\Aggregation\Asset\FileSizeSumAggregation; use Pimcore\Bundle\GenericDataIndexBundle\SearchIndexAdapter\Asset\FieldDefinitionServiceInterface; use Pimcore\Twig\Extension\Templating\Placeholder\Exception; @@ -64,4 +67,25 @@ public function handleAssetMetaDataAggregation( throw new InvalidModifierException($e->getMessage(), 0, $e); } } + + #[AsSearchModifierHandler] + public function handleFileSizeAggregation( + FileSizeSumAggregation $aggregation, + SearchModifierContextInterface $context + ): void { + if (!$context->getOriginalSearch() instanceof AssetSearch) { + throw new InvalidModifierException('FileSizeAggregation can only be used with AssetSearch'); + } + + $context->getSearch()->addAggregation( + new Aggregation( + name: $aggregation->getAggregationName(), + params: [ + 'sum' => [ + 'field' => 'system_fields.fileSize', + ], + ] + ) + ); + } } diff --git a/src/Service/Search/SearchService/Asset/Aggregation/FileSizeAggregationService.php b/src/Service/Search/SearchService/Asset/Aggregation/FileSizeAggregationService.php new file mode 100644 index 00000000..5ebaf0ee --- /dev/null +++ b/src/Service/Search/SearchService/Asset/Aggregation/FileSizeAggregationService.php @@ -0,0 +1,47 @@ +addModifier($aggregation) + ->setAggregationsOnly(true) + ; + + $result = $this->assetSearchService->search($assetSearch); + + $sum = $result->getAggregation($aggregation->getAggregationName())?->getAggregationResult()['value'] ?? 0; + + return (int) $sum; + } +} diff --git a/src/Service/Search/SearchService/Asset/Aggregation/FileSizeAggregationServiceInterface.php b/src/Service/Search/SearchService/Asset/Aggregation/FileSizeAggregationServiceInterface.php new file mode 100644 index 00000000..347838a8 --- /dev/null +++ b/src/Service/Search/SearchService/Asset/Aggregation/FileSizeAggregationServiceInterface.php @@ -0,0 +1,27 @@ +hydrateAggregationBuckets($aggregation['buckets'] ?? []), otherDocCount: $aggregation['sum_other_doc_count'] ?? 0, docCountErrorUpperBound: $aggregation['doc_count_error_upper_bound'] ?? 0, + aggregationResult: $aggregation, ); } diff --git a/tests/Functional/Search/SearchService/Asset/Aggregation/FileSizeAggregationServiceTest.php b/tests/Functional/Search/SearchService/Asset/Aggregation/FileSizeAggregationServiceTest.php new file mode 100644 index 00000000..f9eb6670 --- /dev/null +++ b/tests/Functional/Search/SearchService/Asset/Aggregation/FileSizeAggregationServiceTest.php @@ -0,0 +1,61 @@ +tester->enableSynchronousProcessing(); + } + + protected function _after() + { + TestHelper::cleanUp(); + $this->tester->flushIndex(); + $this->tester->cleanupIndex(); + $this->tester->flushIndex(); + } + + public function testGetFileSizeSum(): void + { + $asset = TestHelper::createImageAsset(); + $asset2 = TestHelper::createImageAsset(); + $asset3 = TestHelper::createImageAsset(); + + /** @var FileSizeAggregationServiceInterface $fileSizeAggregationService */ + $fileSizeAggregationService = $this->tester->grabService(FileSizeAggregationServiceInterface::class); + + $fileSizeSum = $asset->getFileSize() + $asset2->getFileSize() + $asset3->getFileSize(); + + $assetSearch = (new AssetSearch()) + ->addModifier(new IdsFilter([$asset->getId(), $asset2->getId(), $asset3->getId()])) + ->setPageSize(3); + + $this->assertEquals($fileSizeSum, $fileSizeAggregationService->getFileSizeSum($assetSearch)); + $this->assertNotSame(0, $fileSizeSum); + } +}