From f0018c7f923cf83c6ea4e2f524fbbb94b18ec7e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20W=C3=B3js?= Date: Thu, 28 Sep 2023 11:51:00 +0200 Subject: [PATCH] IBX-6649: Added support for spell checking --- src/lib/Handler.php | 12 +++++--- .../QueryConverter/NativeQueryConverter.php | 7 +++++ src/lib/ResultExtractor.php | 28 +++++++++++++++++-- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/lib/Handler.php b/src/lib/Handler.php index b2a7f60c..e61a4746 100644 --- a/src/lib/Handler.php +++ b/src/lib/Handler.php @@ -19,7 +19,7 @@ use Ibexa\Core\Base\Exceptions\NotFoundException; /** - * The Content Search handler retrieves sets of of Content objects, based on a + * The Content Search handler retrieves sets of Content objects, based on a * set of criteria. * * The basic idea of this class is to do the following: @@ -33,7 +33,7 @@ * sensible queries from all criterion definitions. * * 3) The query might be possible to optimize (remove empty statements), - * reduce singular and and or constructs… + * reduce singular and or constructs… * * 4) Additionally we might need a post-query filtering step, which filters * content objects based on criteria, which could not be converted in to @@ -153,7 +153,8 @@ public function findContent(Query $query, array $languageFilter = []) $this->gateway->findContent($query, $languageFilter), $query->facetBuilders, $query->aggregations, - $languageFilter + $languageFilter, + $query->spellcheck ); } @@ -224,7 +225,9 @@ public function findLocations(LocationQuery $query, array $languageFilter = []) return $this->locationResultExtractor->extract( $this->gateway->findLocations($query, $languageFilter), $query->facetBuilders, - $query->aggregations + $query->aggregations, + $languageFilter, + $query->spellcheck ); } @@ -478,6 +481,7 @@ public function supports(int $capabilityFlag): bool case SearchService::CAPABILITY_SCORING: case SearchService::CAPABILITY_FACETS: case SearchService::CAPABILITY_CUSTOM_FIELDS: + case SearchService::CAPABILITY_SPELLCHECK: case SearchService::CAPABILITY_ADVANCED_FULLTEXT: case SearchService::CAPABILITY_AGGREGATIONS: return true; diff --git a/src/lib/Query/Common/QueryConverter/NativeQueryConverter.php b/src/lib/Query/Common/QueryConverter/NativeQueryConverter.php index 31e49b57..2ef6aaea 100644 --- a/src/lib/Query/Common/QueryConverter/NativeQueryConverter.php +++ b/src/lib/Query/Common/QueryConverter/NativeQueryConverter.php @@ -100,6 +100,13 @@ public function convert(Query $query, array $languageSettings = []) } } + if ($query->spellcheck !== null) { + $params['spellcheck'] = 'true'; + $params['spellcheck.q'] = $query->spellcheck->getQuery(); + $params['spellcheck.count'] = 1; + $params['spellcheck.collate'] = 'true'; + } + return $params; } diff --git a/src/lib/ResultExtractor.php b/src/lib/ResultExtractor.php index fd7492d4..cdd0db9a 100644 --- a/src/lib/ResultExtractor.php +++ b/src/lib/ResultExtractor.php @@ -6,9 +6,11 @@ */ namespace Ibexa\Solr; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Spellcheck; use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResultCollection; use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SpellcheckResult; use Ibexa\Contracts\Solr\ResultExtractor\AggregationResultExtractor; use Ibexa\Solr\Gateway\EndpointRegistry; use Ibexa\Solr\Query\FacetFieldVisitor; @@ -49,8 +51,13 @@ public function __construct( * * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult */ - public function extract($data, array $facetBuilders = [], array $aggregations = [], array $languageFilter = []) - { + public function extract( + $data, + array $facetBuilders = [], + array $aggregations = [], + array $languageFilter = [], + ?Spellcheck $spellcheck = null + ) { $result = new SearchResult( [ 'time' => $data->responseHeader->QTime / 1000, @@ -61,6 +68,7 @@ public function extract($data, array $facetBuilders = [], array $aggregations = $result->facets = $this->extractFacets($data, $facetBuilders, $languageFilter); $result->aggregations = $this->extractAggregations($data, $aggregations, $languageFilter); + $result->spellcheck = $this->extractSpellcheck($data, $spellcheck); foreach ($data->response->docs as $doc) { $result->searchHits[] = $this->extractSearchHit($doc, $languageFilter); @@ -186,6 +194,22 @@ protected function extractSearchHit(stdClass $doc, array $languageFilter): Searc ] ); } + + protected function extractSpellcheck(stdClass $data, ?Spellcheck $spellcheck): ?SpellcheckResult + { + if ($spellcheck === null) { + return null; + } + + if (isset($data->spellcheck)) { + $incorrect = !empty($data->spellcheck->collations); + $query = $data->spellcheck->collations[1] ?? $spellcheck->getQuery(); + + return new SpellcheckResult($query, $incorrect); + } + + return new SpellcheckResult($spellcheck->getQuery(), false); + } } class_alias(ResultExtractor::class, 'EzSystems\EzPlatformSolrSearchEngine\ResultExtractor');