diff --git a/.github/init_solr.sh b/.github/init_solr.sh index f441b693..f21b4a9e 100755 --- a/.github/init_solr.sh +++ b/.github/init_solr.sh @@ -232,7 +232,11 @@ solr_cloud_configure_collection() { # modify solrconfig.xml to remove section that doesn't agree with our schema sed -i.bak '//d' ${TEMPLATE_DIR}/solrconfig.xml # Adapt autoSoftCommit to have a recommended value - sed -i.bak2 's/${solr.autoSoftCommit.maxTime:-1}/${solr.autoSoftCommit.maxTime:20}/' "${TEMPLATE_DIR}/solrconfig.xml" || exit_on_error "Can't modify file '${TEMPLATE_DIR}/solrconfig.xml'" + sed -i.bak 's/${solr.autoSoftCommit.maxTime:-1}/${solr.autoSoftCommit.maxTime:20}/' "${TEMPLATE_DIR}/solrconfig.xml" || exit_on_error "Can't modify file '${TEMPLATE_DIR}/solrconfig.xml'" + # Configure spellcheck component + sed -i.bar 's/_text_<\/str>/meta_content__text_t<\/str>/' "${TEMPLATE_DIR}/solrconfig.xml" + # Add spellcheck component to /select handler + sed -i.bak 's//\n \n spellcheck<\/str>\n <\/arr>/' "${TEMPLATE_DIR}/solrconfig.xml" } solr_cloud_upload_collection_configuration() { diff --git a/bin/generate-solr-config.sh b/bin/generate-solr-config.sh index ab986946..b3547295 100755 --- a/bin/generate-solr-config.sh +++ b/bin/generate-solr-config.sh @@ -121,6 +121,10 @@ fi # Adapt autoSoftCommit to have a recommended value, and remove add-unknown-fields-to-the-schema sed -i.bak '//d' $DESTINATION_DIR/solrconfig.xml sed -i.bak 's/${solr.autoSoftCommit.maxTime:-1}/${solr.autoSoftCommit.maxTime:20}/' $DESTINATION_DIR/solrconfig.xml +# Configure spellcheck component +sed -i.bar 's/_text_<\/str>/meta_content__text_t<\/str>/' $DESTINATION_DIR/solrconfig.xml +# Add spellcheck component to /select handler +sed -i.bak 's//\n \n spellcheck<\/str>\n <\/arr>/' $DESTINATION_DIR/solrconfig.xml rm $DESTINATION_DIR/solrconfig.xml.bak 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');