From 60fa18fd5628ee507e74a0421b72a107f74aecc5 Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Thu, 12 Oct 2023 16:22:53 +0200 Subject: [PATCH 1/6] Added Image criterion visitors --- .../AbstractImageRangeVisitor.php | 32 +++++++++++++ .../AbstractImageTermsVisitor.php | 36 +++++++++++++++ .../CriterionVisitor/AbstractImageVisitor.php | 46 +++++++++++++++++++ .../Query/Image/CriterionVisitor/FileSize.php | 31 +++++++++++++ .../Query/Image/CriterionVisitor/Height.php | 31 +++++++++++++ .../Query/Image/CriterionVisitor/MimeType.php | 31 +++++++++++++ .../Image/CriterionVisitor/Orientation.php | 31 +++++++++++++ .../Query/Image/CriterionVisitor/Width.php | 31 +++++++++++++ .../container/solr/criterion_visitors.yml | 36 +++++++++++++++ 9 files changed, 305 insertions(+) create mode 100644 src/lib/Query/Image/CriterionVisitor/AbstractImageRangeVisitor.php create mode 100644 src/lib/Query/Image/CriterionVisitor/AbstractImageTermsVisitor.php create mode 100644 src/lib/Query/Image/CriterionVisitor/AbstractImageVisitor.php create mode 100644 src/lib/Query/Image/CriterionVisitor/FileSize.php create mode 100644 src/lib/Query/Image/CriterionVisitor/Height.php create mode 100644 src/lib/Query/Image/CriterionVisitor/MimeType.php create mode 100644 src/lib/Query/Image/CriterionVisitor/Orientation.php create mode 100644 src/lib/Query/Image/CriterionVisitor/Width.php diff --git a/src/lib/Query/Image/CriterionVisitor/AbstractImageRangeVisitor.php b/src/lib/Query/Image/CriterionVisitor/AbstractImageRangeVisitor.php new file mode 100644 index 00000000..46dca9c3 --- /dev/null +++ b/src/lib/Query/Image/CriterionVisitor/AbstractImageRangeVisitor.php @@ -0,0 +1,32 @@ +value; + $queries = []; + + foreach ($this->getSearchFieldNames($criterion) as $fieldName) { + $queries[] = $fieldName . ':' . $this->getRange( + $criterion->operator, + $criterionValue[0], + $criterionValue[1] ?? null + ); + } + + return '(' . implode(' OR ', $queries) . ')'; + } +} diff --git a/src/lib/Query/Image/CriterionVisitor/AbstractImageTermsVisitor.php b/src/lib/Query/Image/CriterionVisitor/AbstractImageTermsVisitor.php new file mode 100644 index 00000000..51167542 --- /dev/null +++ b/src/lib/Query/Image/CriterionVisitor/AbstractImageTermsVisitor.php @@ -0,0 +1,36 @@ +|string $criterionValue */ + $criterionValue = $criterion->value; + + foreach ($this->getSearchFieldNames($criterion) as $fieldName) { + if (is_array($criterionValue)) { + foreach ($criterionValue as $value) { + $queries[] = $fieldName . ':' . $value; + } + } + + if (is_string($criterionValue)) { + $queries[] = $fieldName . ':' . $criterionValue; + } + } + + return '(' . implode(' OR ', $queries) . ')'; + } +} diff --git a/src/lib/Query/Image/CriterionVisitor/AbstractImageVisitor.php b/src/lib/Query/Image/CriterionVisitor/AbstractImageVisitor.php new file mode 100644 index 00000000..4077aa4f --- /dev/null +++ b/src/lib/Query/Image/CriterionVisitor/AbstractImageVisitor.php @@ -0,0 +1,46 @@ +fieldNameResolver = $fieldNameResolver; + $this->imageFieldType = $imageFieldType; + } + + abstract protected function getSearchFieldName(): string; + + /** + * @return array + */ + protected function getSearchFieldNames(Criterion $criterion): array + { + return array_keys( + $this->fieldNameResolver->getFieldTypes( + $criterion, + $criterion->target, + $this->imageFieldType->getFieldTypeIdentifier(), + $this->getSearchFieldName() + ) + ); + } +} diff --git a/src/lib/Query/Image/CriterionVisitor/FileSize.php b/src/lib/Query/Image/CriterionVisitor/FileSize.php new file mode 100644 index 00000000..9c2c43fb --- /dev/null +++ b/src/lib/Query/Image/CriterionVisitor/FileSize.php @@ -0,0 +1,31 @@ +operator === Operator::BETWEEN + || $criterion->operator === Operator::GTE + ); + } + + protected function getSearchFieldName(): string + { + return self::SEARCH_FIELD_FILE_SIZE; + } +} diff --git a/src/lib/Query/Image/CriterionVisitor/Height.php b/src/lib/Query/Image/CriterionVisitor/Height.php new file mode 100644 index 00000000..87c2cb39 --- /dev/null +++ b/src/lib/Query/Image/CriterionVisitor/Height.php @@ -0,0 +1,31 @@ +operator === Operator::BETWEEN + || $criterion->operator === Operator::GTE + ); + } + + protected function getSearchFieldName(): string + { + return self::SEARCH_FIELD_HEIGHT; + } +} diff --git a/src/lib/Query/Image/CriterionVisitor/MimeType.php b/src/lib/Query/Image/CriterionVisitor/MimeType.php new file mode 100644 index 00000000..dd42384b --- /dev/null +++ b/src/lib/Query/Image/CriterionVisitor/MimeType.php @@ -0,0 +1,31 @@ +operator === Operator::EQ + || $criterion->operator === Operator::IN + ); + } + + protected function getSearchFieldName(): string + { + return self::SEARCH_FIELD_MIME_TYPE; + } +} diff --git a/src/lib/Query/Image/CriterionVisitor/Orientation.php b/src/lib/Query/Image/CriterionVisitor/Orientation.php new file mode 100644 index 00000000..4bec2dbe --- /dev/null +++ b/src/lib/Query/Image/CriterionVisitor/Orientation.php @@ -0,0 +1,31 @@ +operator === Operator::EQ + || $criterion->operator === Operator::IN + ); + } + + protected function getSearchFieldName(): string + { + return self::SEARCH_FIELD_ORIENTATION; + } +} diff --git a/src/lib/Query/Image/CriterionVisitor/Width.php b/src/lib/Query/Image/CriterionVisitor/Width.php new file mode 100644 index 00000000..18ca6250 --- /dev/null +++ b/src/lib/Query/Image/CriterionVisitor/Width.php @@ -0,0 +1,31 @@ +operator === Operator::BETWEEN + || $criterion->operator === Operator::GTE + ); + } + + protected function getSearchFieldName(): string + { + return self::SEARCH_FIELD_WIDTH; + } +} diff --git a/src/lib/Resources/config/container/solr/criterion_visitors.yml b/src/lib/Resources/config/container/solr/criterion_visitors.yml index 77ff467c..df8fc881 100644 --- a/src/lib/Resources/config/container/solr/criterion_visitors.yml +++ b/src/lib/Resources/config/container/solr/criterion_visitors.yml @@ -293,3 +293,39 @@ services: tags: - {name: ibexa.search.solr.query.content.criterion.visitor} - {name: ibexa.search.solr.query.location.criterion.visitor} + + Ibexa\Solr\Query\Image\CriterionVisitor\AbstractImageVisitor: + abstract: true + arguments: + $fieldNameResolver: '@Ibexa\Core\Search\Common\FieldNameResolver' + $imageFieldType: '@Ibexa\Core\FieldType\Image\Type' + + Ibexa\Solr\Query\Image\CriterionVisitor\MimeType: + parent: Ibexa\Solr\Query\Image\CriterionVisitor\AbstractImageVisitor + tags: + - { name: ibexa.search.solr.query.content.criterion.visitor } + - { name: ibexa.search.solr.query.location.criterion.visitor } + + Ibexa\Solr\Query\Image\CriterionVisitor\FileSize: + parent: Ibexa\Solr\Query\Image\CriterionVisitor\AbstractImageVisitor + tags: + - { name: ibexa.search.solr.query.content.criterion.visitor } + - { name: ibexa.search.solr.query.location.criterion.visitor } + + Ibexa\Solr\Query\Image\CriterionVisitor\Width: + parent: Ibexa\Solr\Query\Image\CriterionVisitor\AbstractImageVisitor + tags: + - { name: ibexa.search.solr.query.content.criterion.visitor } + - { name: ibexa.search.solr.query.location.criterion.visitor } + + Ibexa\Solr\Query\Image\CriterionVisitor\Height: + parent: Ibexa\Solr\Query\Image\CriterionVisitor\AbstractImageVisitor + tags: + - { name: ibexa.search.solr.query.content.criterion.visitor } + - { name: ibexa.search.solr.query.location.criterion.visitor } + + Ibexa\Solr\Query\Image\CriterionVisitor\Orientation: + parent: Ibexa\Solr\Query\Image\CriterionVisitor\AbstractImageVisitor + tags: + - { name: ibexa.search.solr.query.content.criterion.visitor } + - { name: ibexa.search.solr.query.location.criterion.visitor } From 717568e2272591392789acd41ea56ea94128a9e6 Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Wed, 25 Oct 2023 17:42:14 +0200 Subject: [PATCH 2/6] [Tests] Added IbexaTestKernel --- composer.json | 1 + src/contracts/Test/IbexaSolrTestKernel.php | 48 +++++++++++++++ src/lib/Test/SolrTestContainerBuilder.php | 60 +++++++++++++++++++ tests/lib/SetupFactory/LegacySetupFactory.php | 43 ++++--------- 4 files changed, 122 insertions(+), 30 deletions(-) create mode 100644 src/contracts/Test/IbexaSolrTestKernel.php create mode 100644 src/lib/Test/SolrTestContainerBuilder.php diff --git a/composer.json b/composer.json index c35de4f9..37b1e1e5 100644 --- a/composer.json +++ b/composer.json @@ -27,6 +27,7 @@ }, "require-dev": { "symfony/proxy-manager-bridge": "^5.4", + "symfony/phpunit-bridge": "^5.4", "ibexa/doctrine-schema": "~4.6.0@dev", "phpunit/phpunit": "^8.2", "matthiasnoback/symfony-dependency-injection-test": "^4.1", diff --git a/src/contracts/Test/IbexaSolrTestKernel.php b/src/contracts/Test/IbexaSolrTestKernel.php new file mode 100644 index 00000000..6c05ab83 --- /dev/null +++ b/src/contracts/Test/IbexaSolrTestKernel.php @@ -0,0 +1,48 @@ + Handler::class; + } + + public function registerContainerConfiguration(LoaderInterface $loader): void + { + parent::registerContainerConfiguration($loader); + + $loader->load(static function (ContainerBuilder $container): void { + (new SolrTestContainerBuilder())->loadSolrSettings($container); + }); + } +} diff --git a/src/lib/Test/SolrTestContainerBuilder.php b/src/lib/Test/SolrTestContainerBuilder.php new file mode 100644 index 00000000..928e3f7b --- /dev/null +++ b/src/lib/Test/SolrTestContainerBuilder.php @@ -0,0 +1,60 @@ + 'multicore_dedicated.yml', + SearchServiceTranslationLanguageFallbackTest::SETUP_SHARED => 'multicore_shared.yml', + SearchServiceTranslationLanguageFallbackTest::SETUP_SINGLE => 'single_core.yml', + SearchServiceTranslationLanguageFallbackTest::SETUP_CLOUD => 'cloud.yml', + ]; + + public function loadSolrSettings(ContainerBuilder $containerBuilder): void + { + $containerBuilder->setParameter('test.ibexa.solr.host', getenv('SOLR_HOST') ?: 'localhost'); + + $settingsPath = dirname(__DIR__) . '/Resources/config/container/'; + $testSettingsPath = dirname(__DIR__, 3) . '/tests/lib/Resources/config/'; + + $solrLoader = new YamlFileLoader($containerBuilder, new FileLocator($settingsPath)); + $solrLoader->load('solr.yml'); + + $testConfigurationFile = $this->getTestConfigurationFile(); + $solrTestLoader = new YamlFileLoader($containerBuilder, new FileLocator($testSettingsPath)); + $solrTestLoader->load($testConfigurationFile); + + $containerBuilder->addResource(new FileResource($testSettingsPath . $testConfigurationFile)); + } + + public function getTestConfigurationFile(): string + { + $isSolrCloud = getenv('SOLR_CLOUD') === 'yes'; + $coresSetup = $isSolrCloud + ? SearchServiceTranslationLanguageFallbackTest::SETUP_CLOUD + : getenv('CORES_SETUP'); + + if (!isset(self::CONFIGURATION_FILES_MAP[$coresSetup])) { + throw new RuntimeException("Backend cores setup '{$coresSetup}' is not handled"); + } + + return self::CONFIGURATION_FILES_MAP[$coresSetup]; + } +} diff --git a/tests/lib/SetupFactory/LegacySetupFactory.php b/tests/lib/SetupFactory/LegacySetupFactory.php index 54804b23..cece8115 100644 --- a/tests/lib/SetupFactory/LegacySetupFactory.php +++ b/tests/lib/SetupFactory/LegacySetupFactory.php @@ -18,12 +18,9 @@ use Ibexa\Solr\Container\Compiler; use Ibexa\Solr\Gateway\UpdateSerializerInterface; use Ibexa\Solr\Handler as SolrSearchHandler; -use Ibexa\Tests\Integration\Core\Repository\SearchServiceTranslationLanguageFallbackTest; -use RuntimeException; -use Symfony\Component\Config\FileLocator; +use Ibexa\Solr\Test\SolrTestContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; use Symfony\Component\HttpClient\HttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; @@ -33,12 +30,16 @@ */ class LegacySetupFactory extends CoreLegacySetupFactory { - public const CONFIGURATION_FILES_MAP = [ - SearchServiceTranslationLanguageFallbackTest::SETUP_DEDICATED => 'multicore_dedicated.yml', - SearchServiceTranslationLanguageFallbackTest::SETUP_SHARED => 'multicore_shared.yml', - SearchServiceTranslationLanguageFallbackTest::SETUP_SINGLE => 'single_core.yml', - SearchServiceTranslationLanguageFallbackTest::SETUP_CLOUD => 'cloud.yml', - ]; + public const CONFIGURATION_FILES_MAP = SolrTestContainerBuilder::CONFIGURATION_FILES_MAP; + + private SolrTestContainerBuilder $containerBuilder; + + public function __construct() + { + parent::__construct(); + + $this->containerBuilder = new SolrTestContainerBuilder(); + } /** * Returns a configured repository for testing. @@ -71,16 +72,7 @@ protected function externalBuildContainer(ContainerBuilder $containerBuilder): v protected function loadSolrSettings(ContainerBuilder $containerBuilder): void { - $containerBuilder->setParameter('test.ibexa.solr.host', getenv('SOLR_HOST') ?: 'localhost'); - - $settingsPath = realpath(__DIR__ . '/../../../src/lib/Resources/config/container/'); - $testSettingsPath = realpath(__DIR__ . '/../Resources/config/'); - - $solrLoader = new YamlFileLoader($containerBuilder, new FileLocator($settingsPath)); - $solrLoader->load('solr.yml'); - - $solrTestLoader = new YamlFileLoader($containerBuilder, new FileLocator($testSettingsPath)); - $solrTestLoader->load($this->getTestConfigurationFile()); + $this->containerBuilder->loadSolrSettings($containerBuilder); $containerBuilder->addCompilerPass(new Compiler\FieldMapperPass\BlockFieldMapperPass()); $containerBuilder->addCompilerPass(new Compiler\FieldMapperPass\BlockTranslationFieldMapperPass()); @@ -152,16 +144,7 @@ protected function indexAll(): void protected function getTestConfigurationFile(): string { - $isSolrCloud = getenv('SOLR_CLOUD') === 'yes'; - $coresSetup = $isSolrCloud - ? SearchServiceTranslationLanguageFallbackTest::SETUP_CLOUD - : getenv('CORES_SETUP'); - - if (!isset(self::CONFIGURATION_FILES_MAP[$coresSetup])) { - throw new RuntimeException("Backend cores setup '{$coresSetup}' is not handled"); - } - - return self::CONFIGURATION_FILES_MAP[$coresSetup]; + return $this->containerBuilder->getTestConfigurationFile(); } private function configureSymfonyHttpClient(ContainerBuilder $containerBuilder): void From 9c0cbad2b7d8b44fc1bb4837e50e97c250ab528d Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Wed, 25 Oct 2023 17:42:30 +0200 Subject: [PATCH 3/6] [PHPStan] Regenerated baseline --- phpstan-baseline.neon | 5 ----- 1 file changed, 5 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index b7d85079..030590e9 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -2249,8 +2249,3 @@ parameters: message: "#^Parameter \\#1 \\$contentObjects of method Ibexa\\\\Solr\\\\Handler\\:\\:bulkIndexContent\\(\\) expects array\\, iterable\\ given\\.$#" count: 1 path: tests/lib/SetupFactory/LegacySetupFactory.php - - - - message: "#^Parameter \\#1 \\$paths of class Symfony\\\\Component\\\\Config\\\\FileLocator constructor expects array\\\\|string, string\\|false given\\.$#" - count: 2 - path: tests/lib/SetupFactory/LegacySetupFactory.php From 0336594a8db00302df5ab2c76497d1f096ab47d5 Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Thu, 26 Oct 2023 08:57:14 +0200 Subject: [PATCH 4/6] Fixed deprecation notices --- src/lib/Resources/config/container/solr.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/lib/Resources/config/container/solr.yml b/src/lib/Resources/config/container/solr.yml index b8f416c0..8d3d2c0d 100644 --- a/src/lib/Resources/config/container/solr.yml +++ b/src/lib/Resources/config/container/solr.yml @@ -61,7 +61,10 @@ services: - '@ibexa.solr.query.content.facet_builder_visitor.aggregate' - '@ibexa.solr.query.content.aggregation_result_extractor.dispatcher' - '@Ibexa\Solr\Gateway\EndpointRegistry' - deprecated: 'The "%service_id%" service is deprecated since eZ Platform 3.2.0, to be removed in eZ Platform 4.0.0., use ezpublish.search.solr.result_extractor.content.native or ezpublish.search.solr.result_extractor.location.native instead.' + deprecated: + version: 'eZ Platform 3.2.0' + package: 'ibexa/solr' + message: 'The "%service_id%" service is deprecated since eZ Platform 3.2.0, to be removed in eZ Platform 4.0.0., use ezpublish.search.solr.result_extractor.content.native or ezpublish.search.solr.result_extractor.location.native instead.' ibexa.solr.result_extractor.content.native: class: Ibexa\Solr\ResultExtractor\NativeResultExtractor @@ -79,7 +82,10 @@ services: ibexa.solr.result_extractor: alias: ibexa.solr.result_extractor.native - deprecated: 'The "%alias_id%" alias is deprecated since eZ Platform 3.2.0, to be removed in eZ Platform 4.0.0. Use ezpublish.search.solr.result_extractor.content or ezpublish.search.solr.result_extractor.location instead' + deprecated: + version: 'eZ Platform 3.2.0' + package: 'ibexa/solr' + message: 'The "%alias_id%" alias is deprecated since eZ Platform 3.2.0, to be removed in eZ Platform 4.0.0. Use ezpublish.search.solr.result_extractor.content or ezpublish.search.solr.result_extractor.location instead' ibexa.solr.result_extractor.content: alias: ibexa.solr.result_extractor.content.native From 0bb09c91526087b17af4c5eb6ef2e87a2a6cf6b1 Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Tue, 7 Nov 2023 14:23:02 +0100 Subject: [PATCH 5/6] Added exception throwing when searchFileNames array is empty --- .../CriterionVisitor/AbstractImageVisitor.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/lib/Query/Image/CriterionVisitor/AbstractImageVisitor.php b/src/lib/Query/Image/CriterionVisitor/AbstractImageVisitor.php index 4077aa4f..9cd613ec 100644 --- a/src/lib/Query/Image/CriterionVisitor/AbstractImageVisitor.php +++ b/src/lib/Query/Image/CriterionVisitor/AbstractImageVisitor.php @@ -10,6 +10,7 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; use Ibexa\Contracts\Solr\Query\CriterionVisitor; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; use Ibexa\Core\FieldType\Image\Type; use Ibexa\Core\Search\Common\FieldNameResolver; @@ -31,10 +32,12 @@ abstract protected function getSearchFieldName(): string; /** * @return array + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ protected function getSearchFieldNames(Criterion $criterion): array { - return array_keys( + $searchFieldNames = array_keys( $this->fieldNameResolver->getFieldTypes( $criterion, $criterion->target, @@ -42,5 +45,14 @@ protected function getSearchFieldNames(Criterion $criterion): array $this->getSearchFieldName() ) ); + + if (empty($searchFieldNames)) { + throw new InvalidArgumentException( + '$criterion->target', + "No searchable Fields found for the provided Criterion target '{$criterion->target}'." + ); + } + + return $searchFieldNames; } } From 2012855302851fa717a4d0587098a1c4d732b001 Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Thu, 9 Nov 2023 14:14:29 +0100 Subject: [PATCH 6/6] Fixed deprecation message --- src/lib/Resources/config/container/solr.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/Resources/config/container/solr.yml b/src/lib/Resources/config/container/solr.yml index 8d3d2c0d..ee461403 100644 --- a/src/lib/Resources/config/container/solr.yml +++ b/src/lib/Resources/config/container/solr.yml @@ -64,7 +64,7 @@ services: deprecated: version: 'eZ Platform 3.2.0' package: 'ibexa/solr' - message: 'The "%service_id%" service is deprecated since eZ Platform 3.2.0, to be removed in eZ Platform 4.0.0., use ezpublish.search.solr.result_extractor.content.native or ezpublish.search.solr.result_extractor.location.native instead.' + message: 'The "%service_id%" service is deprecated since eZ Platform 3.2.0, to be removed in Ibexa DXP 5.0.0. Use ibexa.solr.result_extractor.content.native or ibexa.solr.result_extractor.location.native instead.' ibexa.solr.result_extractor.content.native: class: Ibexa\Solr\ResultExtractor\NativeResultExtractor @@ -85,7 +85,7 @@ services: deprecated: version: 'eZ Platform 3.2.0' package: 'ibexa/solr' - message: 'The "%alias_id%" alias is deprecated since eZ Platform 3.2.0, to be removed in eZ Platform 4.0.0. Use ezpublish.search.solr.result_extractor.content or ezpublish.search.solr.result_extractor.location instead' + message: 'The "%alias_id%" alias is deprecated since eZ Platform 3.2.0, to be removed in Ibexa DXP 5.0.0. Use ibexa.solr.result_extractor.content or ibexa.solr.result_extractor.location instead' ibexa.solr.result_extractor.content: alias: ibexa.solr.result_extractor.content.native