From 7bb24ba34722677c665e54fdf61f6f987ae63f4c Mon Sep 17 00:00:00 2001 From: Timo Hund Date: Tue, 7 Aug 2018 09:03:14 +0200 Subject: [PATCH] [TASK] Use solarium to build queries for Solr (#2067) In EXT:solr 9.0.0 we want to use the solarium php api instead of the old SolrPhpClient Since this is a huge task, we splitted it into multiple parts. In this part we change EXT:solr in order to use as much from solarium as we can to build the Solr queries. In a later issue/pr we will also use the solarium client, to perform the http requests to solr and along with that drop the SolrPhpClient. This pr contains several changes that require changes on you code when you use the EXT:solr api: * The ParameterBuilder classes (Domain\Search\Query\ParameterBuilder), no get the QueryBuilder passed in the build() method, instead of the Query itself. * The Query class now inherits from Solarium\QueryType\Select\Query\Query and the QueryBuilder and all ParameterBuilders need to initialize the Query object with the solarium api. * In non composer mode the solarium api and it's dependecies get loaded from Resources\Private\Php\ComposerLibraries, in composer mode solarium is installed by composer. Fixes: #2068 --- Build/Release/ter_tag_uploader.sh | 5 +- .../Domain/Search/Query/ExtractingQuery.php | 75 +- .../Domain/Search/Query/Helper/Pagination.php | 112 --- .../Query/Helper/QueryStringContainer.php | 159 ---- ...rBuilder.php => AbstractDeactivatable.php} | 8 +- .../ParameterBuilder/AbstractFieldList.php | 37 +- .../ParameterBuilder/BigramPhraseFields.php | 32 +- .../Search/Query/ParameterBuilder/Debug.php | 69 -- .../Query/ParameterBuilder/Elevation.php | 54 +- .../Query/ParameterBuilder/Faceting.php | 97 +- .../ParameterBuilder/FieldCollapsing.php | 51 +- .../Search/Query/ParameterBuilder/Filters.php | 13 +- .../Query/ParameterBuilder/Grouping.php | 65 +- .../Query/ParameterBuilder/Highlighting.php | 78 +- .../Query/ParameterBuilder/Operator.php | 29 +- .../ParameterBuilder/ParameterBuilder.php | 9 +- .../Query/ParameterBuilder/PhraseFields.php | 34 +- .../Query/ParameterBuilder/QueryFields.php | 31 +- .../Query/ParameterBuilder/ReturnFields.php | 25 +- .../Search/Query/ParameterBuilder/Slops.php | 92 +- .../Search/Query/ParameterBuilder/Sorting.php | 77 +- .../Query/ParameterBuilder/Sortings.php | 101 ++ .../Query/ParameterBuilder/Spellchecking.php | 37 +- .../ParameterBuilder/TrigramPhraseFields.php | 31 +- Classes/Domain/Search/Query/Query.php | 730 +------------- Classes/Domain/Search/Query/QueryBuilder.php | 322 +++++-- .../Search/Query/QueryParametersContainer.php | 143 --- Classes/Domain/Search/Query/SearchQuery.php | 27 + Classes/Domain/Search/Query/SuggestQuery.php | 39 +- .../Hierarchy/HierarchyFacetParser.php | 14 +- .../Search/ResultSet/SearchResultSet.php | 12 +- .../ResultSet/SearchResultSetService.php | 8 +- .../Statistics/StatisticsWriterProcessor.php | 2 +- .../Domain/Search/Suggest/SuggestService.php | 2 +- Classes/Query/Modifier/Elevation.php | 2 +- Classes/Query/Modifier/Faceting.php | 16 +- Classes/Query/Modifier/Statistics.php | 20 +- Classes/Search.php | 25 +- Classes/Search/AccessComponent.php | 2 +- Classes/Search/AnalysisComponent.php | 22 +- Classes/Search/DebugComponent.php | 20 +- Classes/Search/HighlightingComponent.php | 4 +- Classes/Search/QueryAware.php | 2 +- Classes/Search/RelevanceComponent.php | 2 +- Classes/Search/SortingComponent.php | 46 +- Classes/Search/SpellcheckingComponent.php | 3 +- .../Configuration/TypoScriptConfiguration.php | 2 +- .../System/Solr/Service/SolrWriteService.php | 18 +- .../Controller/ResultPaginateController.php | 2 +- .../Private/Partials/Result/Document.html | 2 +- .../Php/ComposerLibraries/composer.json | 12 + Resources/Private/Php/SolrPhpClient/COPYING | 26 - .../Php/SolrPhpClient/last_synched_revision | 1 - .../Private/Templates/Search/Results.html | 4 +- .../AbstractFrontendControllerTest.php | 66 ++ .../can_render_suggest_controller.xml | 482 ++++++++++ .../Controller/SearchControllerTest.php | 67 +- .../Controller/SuggestControllerTest.php | 106 +++ Tests/Integration/SearchTest.php | 104 +- .../Solr/Service/SolrWriteServiceTest.php | 4 +- .../ApacheSolrDocument/RepositoryTest.php | 2 +- .../BigramPhraseFieldsTest.php | 12 +- .../ParameterBuilder/QueryFieldsTest.php | 2 +- .../Domain/Search/Query/QueryBuilderTest.php | 899 +++++++++--------- .../Domain/Search/Query/SuggestQueryTest.php | 15 +- .../Search/ResultSet/SearchResultSetTest.php | 21 +- .../StatisticsWriterProcessorTest.php | 4 +- .../Search/Suggest/SuggestServiceTest.php | 2 +- Tests/Unit/Query/Modifier/ElevationTest.php | 10 +- Tests/Unit/Query/Modifier/FacetingTest.php | 8 +- Tests/Unit/Search/RelevanceComponentTest.php | 109 ++- Tests/Unit/Search/SortingComponentTest.php | 27 +- composer.json | 9 +- ext_localconf.php | 8 + ext_tables.php | 7 + 75 files changed, 2330 insertions(+), 2485 deletions(-) delete mode 100644 Classes/Domain/Search/Query/Helper/Pagination.php delete mode 100644 Classes/Domain/Search/Query/Helper/QueryStringContainer.php rename Classes/Domain/Search/Query/ParameterBuilder/{AbstractDeactivatableParameterBuilder.php => AbstractDeactivatable.php} (95%) delete mode 100644 Classes/Domain/Search/Query/ParameterBuilder/Debug.php create mode 100644 Classes/Domain/Search/Query/ParameterBuilder/Sortings.php delete mode 100644 Classes/Domain/Search/Query/QueryParametersContainer.php create mode 100644 Classes/Domain/Search/Query/SearchQuery.php create mode 100644 Resources/Private/Php/ComposerLibraries/composer.json delete mode 100644 Resources/Private/Php/SolrPhpClient/COPYING delete mode 100644 Resources/Private/Php/SolrPhpClient/last_synched_revision create mode 100644 Tests/Integration/Controller/AbstractFrontendControllerTest.php create mode 100644 Tests/Integration/Controller/Fixtures/can_render_suggest_controller.xml create mode 100644 Tests/Integration/Controller/SuggestControllerTest.php diff --git a/Build/Release/ter_tag_uploader.sh b/Build/Release/ter_tag_uploader.sh index 06d6cc8a68..d112115ce0 100755 --- a/Build/Release/ter_tag_uploader.sh +++ b/Build/Release/ter_tag_uploader.sh @@ -28,8 +28,11 @@ if [ -n "$TRAVIS_TAG" ] && [ -n "$TYPO3_ORG_USERNAME" ] && [ -n "$TYPO3_ORG_PASS pwd git reset --hard HEAD && git clean -fx + + composer run-script extension-build + echo "Files in this package" - ls -l + ls -lRa TAG_MESSAGE=`git tag -n10 -l $TRAVIS_TAG | sed 's/^[0-9.]*[ ]*//g'` echo "Uploading release ${TRAVIS_TAG} to TER" diff --git a/Classes/Domain/Search/Query/ExtractingQuery.php b/Classes/Domain/Search/Query/ExtractingQuery.php index 46f3c437c4..fab6f7b2dc 100644 --- a/Classes/Domain/Search/Query/ExtractingQuery.php +++ b/Classes/Domain/Search/Query/ExtractingQuery.php @@ -24,13 +24,14 @@ * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ +use Solarium\QueryType\Extract\Query as SolariumExtractQuery; + /** * Specialized query for content extraction using Solr Cell * */ -class ExtractingQuery extends Query +class ExtractingQuery extends SolariumExtractQuery { - protected $file; protected $multiPartPostDataBoundary; /** @@ -40,10 +41,10 @@ class ExtractingQuery extends Query */ public function __construct($file) { - parent::__construct(''); - - $this->file = $file; + parent::__construct(); + $this->setFile($file); $this->multiPartPostDataBoundary = '--' . md5(uniqid(time())); + $this->addParam('extractFormat', 'text'); } /** @@ -56,28 +57,6 @@ public function getMultiPartPostDataBoundary() return $this->multiPartPostDataBoundary; } - /** - * Gets the absolute path to the file to extract content and meta data from. - * - * @return string Absolute path to the file to extract content and meta data from. - */ - public function getFile() - { - return $this->file; - } - - /** - * Sets the absolute path to the file to extract content and meta data from. - * - * @param string $file Absolute path to the file to extract content and meta data from. - */ - public function setFile($file) - { - if (is_file($file)) { - $this->file = $file; - } - } - /** * Gets the filename portion of the file. * @@ -85,7 +64,7 @@ public function setFile($file) */ public function getFileName() { - return basename($this->file); + return basename($this->getFile()); } /** @@ -101,11 +80,9 @@ public function getRawPostFileData($boundary = '') $boundary = $this->multiPartPostDataBoundary; } - $fileData = file_get_contents($this->file); + $fileData = file_get_contents($this->getFile()); if ($fileData === false) { - throw new \Apache_Solr_InvalidArgumentException( - 'Could not retrieve content from file ' . $this->file - ); + throw new \Apache_Solr_InvalidArgumentException('Could not retrieve content from file ' . $this->getFile()); } $data = "--{$boundary}\r\n"; @@ -117,38 +94,4 @@ public function getRawPostFileData($boundary = '') return $data; } - - /** - * En / Disables extraction only - * - * @param bool $extractOnly If TRUE, only extracts content from the given file without indexing - */ - public function setExtractOnly($extractOnly = true) - { - if ($extractOnly) { - $this->queryParametersContainer->set('extractOnly', 'true'); - } else { - $this->queryParametersContainer->remove('extractOnly'); - } - } - - /** - * Builds an array of query parameters to use for the search query. - * - * @return array An array ready to use with query parameters - */ - public function getQueryParameters() - { - $filename = basename($this->file); - - // TODO create an Apache Solr patch to support Apache Tika's -m (and -l) options - $extractingParameters = [ - 'resource.name' => $filename, - 'extractFormat' => 'text', - // Matches the -t command for the tika CLI app. - ]; - - $this->queryParametersContainer->merge($extractingParameters); - return $this->queryParametersContainer->toArray(); - } } diff --git a/Classes/Domain/Search/Query/Helper/Pagination.php b/Classes/Domain/Search/Query/Helper/Pagination.php deleted file mode 100644 index 051af4a380..0000000000 --- a/Classes/Domain/Search/Query/Helper/Pagination.php +++ /dev/null @@ -1,112 +0,0 @@ - - * All rights reserved - * - * This script is part of the TYPO3 project. The TYPO3 project is - * free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * The GNU General Public License can be found at - * http://www.gnu.org/copyleft/gpl.html. - * - * This script is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This copyright notice MUST APPEAR in all copies of the script! - ***************************************************************/ - - -class Pagination { - /** - * @var int - */ - protected $resultsPerPage; - - /** - * @var int - */ - protected $page; - - /** - * Pagination constructor. - * - * @param int $page - * @param int $resultsPerPage - */ - public function __construct(int $page = 0, int $resultsPerPage = 10) - { - $this->page = $page; - $this->resultsPerPage = $resultsPerPage; - } - - /** - * Gets the currently showing page's number - * - * @return int page number currently showing - */ - public function getPage() - { - return $this->page; - } - - /** - * Sets the page that should be shown - * - * @param int $page page number to show - * @return void - */ - public function setPage($page) - { - $this->page = max(intval($page), 0); - } - - /** - * Gets the index of the first result document we're showing - * - * @return int index of the currently first document showing - */ - public function getStartIndex() - { - return ($this->page - 1) * $this->resultsPerPage; - } - - /** - * Gets the index of the last result document we're showing - * - * @return int index of the currently last document showing - */ - public function getEndIndex() - { - return $this->page * $this->resultsPerPage; - } - - /** - * Returns the number of results that should be shown per page - * - * @return int number of results to show per page - */ - public function getResultsPerPage() - { - return $this->resultsPerPage; - } - - /** - * Sets the number of results that should be shown per page - * - * @param int $resultsPerPage Number of results to show per page - * @return void - */ - public function setResultsPerPage($resultsPerPage) - { - $this->resultsPerPage = max(intval($resultsPerPage), 0); - } -} \ No newline at end of file diff --git a/Classes/Domain/Search/Query/Helper/QueryStringContainer.php b/Classes/Domain/Search/Query/Helper/QueryStringContainer.php deleted file mode 100644 index e5c2b88afc..0000000000 --- a/Classes/Domain/Search/Query/Helper/QueryStringContainer.php +++ /dev/null @@ -1,159 +0,0 @@ - - * All rights reserved - * - * This script is part of the TYPO3 project. The TYPO3 project is - * free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * The GNU General Public License can be found at - * http://www.gnu.org/copyleft/gpl.html. - * - * This script is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This copyright notice MUST APPEAR in all copies of the script! - ***************************************************************/ - -class QueryStringContainer { - - /** - * @var string - */ - protected $keywords; - - /** - * @var string - */ - protected $keywordsRaw; - - /** - * @var - */ - protected $queryString; - - /** - * @var bool - */ - private $rawQueryString = false; - - /** - * QueryStrings constructor. - * @param string $keywords - */ - public function __construct($keywords) - { - $this->setKeywords($keywords); - } - - /** - * Builds the query string which is then used for Solr's q parameters - * - * @return string Solr query string - */ - public function getQueryString() - { - if (!$this->rawQueryString) { - $this->buildQueryString(); - } - - return $this->queryString; - } - - /** - * Sets the query string without any escaping. - * - * Be cautious with this function! - * TODO remove this method as it basically just sets the q parameter / keywords - * - * @param string $queryString The raw query string. - */ - public function setQueryString($queryString) - { - $this->queryString = $queryString; - } - - /** - * Creates the string that is later used as the q parameter in the solr query - * - * @return void - */ - protected function buildQueryString() - { - // very simple for now - $this->queryString = $this->keywords; - } - - /** - * Sets whether a raw query sting should be used, that is, whether the query - * string should be escaped or not. - * - * @param bool $useRawQueryString TRUE to use raw queries (like Lucene Query Language) or FALSE for regular, escaped queries - */ - public function useRawQueryString($useRawQueryString) - { - $this->rawQueryString = (boolean)$useRawQueryString; - } - - /** - * Get the query keywords, keywords are escaped. - * - * @return string query keywords - */ - public function getKeywords() - { - return $this->keywords; - } - - /** - * Sets the query keywords, escapes them as needed for Solr/Lucene. - * - * @param string $keywords user search terms/keywords - */ - public function setKeywords($keywords) - { - $this->keywords = EscapeService::escape($keywords); - $this->keywordsRaw = $keywords; - } - - /** - * Gets the cleaned keywords so that it can be used in templates f.e. - * - * @return string The cleaned keywords. - */ - public function getKeywordsCleaned() - { - return EscapeService::clean($this->keywordsRaw); - } - - /** - * Gets the raw, unescaped, unencoded keywords. - * - * USE WITH CAUTION! - * - * @return string raw keywords - */ - public function getKeywordsRaw() - { - return $this->keywordsRaw; - } - - /** - * returns a string representation of the query - * - * @return string the string representation of the query - */ - public function __toString() - { - return $this->getQueryString(); - } -} \ No newline at end of file diff --git a/Classes/Domain/Search/Query/ParameterBuilder/AbstractDeactivatableParameterBuilder.php b/Classes/Domain/Search/Query/ParameterBuilder/AbstractDeactivatable.php similarity index 95% rename from Classes/Domain/Search/Query/ParameterBuilder/AbstractDeactivatableParameterBuilder.php rename to Classes/Domain/Search/Query/ParameterBuilder/AbstractDeactivatable.php index e15b4cff67..604e02f707 100644 --- a/Classes/Domain/Search/Query/ParameterBuilder/AbstractDeactivatableParameterBuilder.php +++ b/Classes/Domain/Search/Query/ParameterBuilder/AbstractDeactivatable.php @@ -1,5 +1,4 @@ isEnabled; } - /** * @param boolean $isEnabled */ @@ -48,5 +43,4 @@ public function setIsEnabled($isEnabled) { $this->isEnabled = $isEnabled; } - } \ No newline at end of file diff --git a/Classes/Domain/Search/Query/ParameterBuilder/AbstractFieldList.php b/Classes/Domain/Search/Query/ParameterBuilder/AbstractFieldList.php index e0802d12bf..e33289b029 100644 --- a/Classes/Domain/Search/Query/ParameterBuilder/AbstractFieldList.php +++ b/Classes/Domain/Search/Query/ParameterBuilder/AbstractFieldList.php @@ -24,13 +24,12 @@ * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; use TYPO3\CMS\Core\Utility\GeneralUtility; /** * The AbstractFieldList class */ -abstract class AbstractFieldList extends AbstractDeactivatableParameterBuilder implements ParameterBuilder +abstract class AbstractFieldList extends AbstractDeactivatable { /** * @var array @@ -83,31 +82,14 @@ protected static function buildFieldList(string $fieldListString, string $delimi * @param string $fieldName * @param float $boost * - * @return ParameterBuilder + * @return AbstractFieldList */ - public function add(string $fieldName, float $boost = 1.0): ParameterBuilder + public function add(string $fieldName, float $boost = 1.0): AbstractFieldList { $this->fieldList[$fieldName] = (float)$boost; return $this; } - /** - * @param Query $query - * @return Query - */ - public function build(Query $query): Query - { - $string = $this->toString(); - // return empty array on empty string - if (trim($string) === '' || !$this->isEnabled) { - $query->getQueryParametersContainer()->remove($this->parameterKey); - return $query; - } - - $query->getQueryParametersContainer()->set($this->parameterKey, $string); - return $query; - } - /** * Creates the string representation * @@ -130,17 +112,4 @@ public function toString(string $delimiter = ' ') return rtrim($fieldListString, $delimiter); } - - /** - * Parses the string representation of the fieldList (e.g. content^100, title^10) to the object representation. - * - * @param string $fieldListString - * @param string $delimiter - * @return AbstractFieldList - */ - protected static function initializeFromString(string $fieldListString, string $delimiter = ',') : AbstractFieldList - { - $fieldList = self::buildFieldList($fieldListString, $delimiter); - return new static(true, $fieldList); - } } diff --git a/Classes/Domain/Search/Query/ParameterBuilder/BigramPhraseFields.php b/Classes/Domain/Search/Query/ParameterBuilder/BigramPhraseFields.php index 65e036a590..20e80f29ad 100644 --- a/Classes/Domain/Search/Query/ParameterBuilder/BigramPhraseFields.php +++ b/Classes/Domain/Search/Query/ParameterBuilder/BigramPhraseFields.php @@ -23,12 +23,14 @@ * * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ + +use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; /** * The BigramPhraseFields class */ -class BigramPhraseFields extends AbstractFieldList +class BigramPhraseFields extends AbstractFieldList implements ParameterBuilder { /** * Parameter key which should be used for Apache Solr URL query @@ -62,4 +64,32 @@ public static function fromTypoScriptConfiguration(TypoScriptConfiguration $solr return self::fromString((string)$solrConfiguration->getSearchQueryBigramPhraseFields()); } + + /** + * Parses the string representation of the fieldList (e.g. content^100, title^10) to the object representation. + * + * @param string $fieldListString + * @param string $delimiter + * @return BigramPhraseFields + */ + protected static function initializeFromString(string $fieldListString, string $delimiter = ',') : BigramPhraseFields + { + $fieldList = self::buildFieldList($fieldListString, $delimiter); + return new BigramPhraseFields(true, $fieldList); + } + + /** + * @param QueryBuilder $parentBuilder + * @return QueryBuilder + */ + public function build(QueryBuilder $parentBuilder): QueryBuilder + { + $bigramPhraseFieldsString = $this->toString(); + if ($bigramPhraseFieldsString === '' || !$this->getIsEnabled()) { + return $parentBuilder; + } + + $parentBuilder->getQuery()->getEDisMax()->setPhraseBigramFields($bigramPhraseFieldsString); + return $parentBuilder; + } } diff --git a/Classes/Domain/Search/Query/ParameterBuilder/Debug.php b/Classes/Domain/Search/Query/ParameterBuilder/Debug.php deleted file mode 100644 index efff2e398b..0000000000 --- a/Classes/Domain/Search/Query/ParameterBuilder/Debug.php +++ /dev/null @@ -1,69 +0,0 @@ - - * All rights reserved - * - * This script is part of the TYPO3 project. The TYPO3 project is - * free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * The GNU General Public License can be found at - * http://www.gnu.org/copyleft/gpl.html. - * - * This script is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * This copyright notice MUST APPEAR in all copies of the script! - ***************************************************************/ -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; - - -/** - * The Debug ParameterProvider is responsible to build the solr query parameters - * that are needed for the debugging. - * - * @package ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder - */ -class Debug extends AbstractDeactivatableParameterBuilder implements ParameterBuilder -{ - /** - * Debug constructor. - * - * @param bool $isEnabled - */ - public function __construct($isEnabled) - { - $this->isEnabled = $isEnabled; - } - - /** - * @param Query $query - * @return Query - */ - public function build(Query $query): Query - { - if (!$this->isEnabled) { - $query->getQueryParametersContainer()->removeMany(['debugQuery', 'echoParams']); - return $query; - } - - $query->getQueryParametersContainer()->merge(['debugQuery' => 'true', 'echoParams' => 'all']); - return $query; - } - - /** - * @return Debug - */ - public static function getEmpty() - { - return new Debug(false); - } -} \ No newline at end of file diff --git a/Classes/Domain/Search/Query/ParameterBuilder/Elevation.php b/Classes/Domain/Search/Query/ParameterBuilder/Elevation.php index aacabf9c94..a87263727b 100644 --- a/Classes/Domain/Search/Query/ParameterBuilder/Elevation.php +++ b/Classes/Domain/Search/Query/ParameterBuilder/Elevation.php @@ -24,7 +24,7 @@ * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; /** @@ -33,7 +33,7 @@ * * @package ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder */ -class Elevation extends AbstractDeactivatableParameterBuilder implements ParameterBuilder +class Elevation extends AbstractDeactivatable implements ParameterBuilder { /** * @var bool @@ -90,31 +90,6 @@ public function setMarkElevatedResults(bool $markElevatedResults) $this->markElevatedResults = $markElevatedResults; } - /** - * @param Query $query - * @return Query - */ - public function build(Query $query): Query - { - if (!$this->isEnabled) { - $query->getQueryParametersContainer()->remove('enableElevation'); - $query->getQueryParametersContainer()->remove('forceElevation'); - $query->getReturnFields()->remove('isElevated:[elevated]'); - $query->getReturnFields()->remove('[elevated]'); // fallback - - return $query; - } - - $query->getQueryParametersContainer()->set('enableElevation', 'true'); - $forceElevationString = $this->isForced ? 'true' : 'false'; - $query->getQueryParametersContainer()->set('forceElevation', $forceElevationString); - if ($this->markElevatedResults) { - $query->getReturnFields()->add('isElevated:[elevated]'); - } - - return $query; - } - /** * @param TypoScriptConfiguration $solrConfiguration * @return Elevation @@ -138,4 +113,29 @@ public static function getEmpty() { return new Elevation(false); } + + /** + * @param QueryBuilder $parentBuilder + * @return QueryBuilder + */ + public function build(QueryBuilder $parentBuilder): QueryBuilder + { + $query = $parentBuilder->getQuery(); + if (!$this->getIsEnabled()) { + $query->addParam('enableElevation', null); + $query->addParam('forceElevation', null); + $query->removeField('isElevated:[elevated]'); + return $parentBuilder; + } + + $query->addParam('enableElevation', 'true'); + $forceElevationString = $this->getIsForced() ? 'true' : 'false'; + $query->addParam('forceElevation', $forceElevationString); + + if ($this->getMarkElevatedResults()) { + $query->addField('isElevated:[elevated]'); + } + + return $parentBuilder; + } } \ No newline at end of file diff --git a/Classes/Domain/Search/Query/ParameterBuilder/Faceting.php b/Classes/Domain/Search/Query/ParameterBuilder/Faceting.php index 1ab97b160f..22983a3de2 100644 --- a/Classes/Domain/Search/Query/ParameterBuilder/Faceting.php +++ b/Classes/Domain/Search/Query/ParameterBuilder/Faceting.php @@ -24,7 +24,7 @@ * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Facets\SortingExpression; use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; @@ -34,7 +34,7 @@ * * @package ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder */ -class Faceting extends AbstractDeactivatableParameterBuilder implements ParameterBuilder +class Faceting extends AbstractDeactivatable implements ParameterBuilder { /** @@ -178,40 +178,6 @@ public function addAdditionalParameter($key, $value) $this->additionalParameters[$key] = $value; } - /** - * @param Query $query - * @return Query - */ - public function build(Query $query): Query - { - if (!$this->isEnabled) { - $query->getQueryParametersContainer()->removeMany(['facet', 'json.facet']); - $query->getQueryParametersContainer()->removeByPrefix('facet.'); - $query->getQueryParametersContainer()->removeByPrefix('f.'); - return $query; - } - - $facetParameters = []; - - $facetParameters['facet'] = 'true'; - $facetParameters['facet.mincount'] = $this->minCount; - $facetParameters['facet.limit'] = $this->limit; - $facetParameters['facet.field'] = $this->fields; - - foreach ($this->additionalParameters as $additionalParameterKey => $additionalParameterValue) { - $facetParameters[$additionalParameterKey] = $additionalParameterValue; - } - - if ($facetParameters['json.facet']) { - $facetParameters['json.facet'] = json_encode($facetParameters['json.facet']); - } - - $facetParameters = $this->applySorting($facetParameters); - $query->getQueryParametersContainer()->merge($facetParameters); - - return $query; - } - /** * Reads the facet sorting configuration and applies it to the queryParameters. * @@ -255,4 +221,63 @@ public static function getEmpty() { return new Faceting(false); } + + /** + * Retrieves all parameters that are required for faceting. + * + * @return array + */ + protected function getFacetParameters() { + $facetParameters = []; + $facetParameters['facet'] = 'true'; + $facetParameters['facet.mincount'] = $this->getMinCount(); + $facetParameters['facet.limit'] = $this->getLimit(); + $facetParameters['facet.field'] = $this->getFields(); + + foreach ($this->getAdditionalParameters() as $additionalParameterKey => $additionalParameterValue) { + $facetParameters[$additionalParameterKey] = $additionalParameterValue; + } + + if ($facetParameters['json.facet']) { + $facetParameters['json.facet'] = json_encode($facetParameters['json.facet']); + } + + $facetParameters = $this->applySorting($facetParameters); + return $facetParameters; + } + + /** + * @param QueryBuilder $parentBuilder + * @return QueryBuilder + */ + public function build(QueryBuilder $parentBuilder): QueryBuilder + { + $query = $parentBuilder->getQuery(); + if (!$this->getIsEnabled()) { + //@todo use unset functionality when present + $query->addParam('facet', null); + $query->addParam('lex', null); + $query->addParam('json.mincount', null); + $query->addParam('json.limit', null); + $query->addParam('json.field', null); + $query->addParam('facet.sort', null); + + $params = $query->getParams(); + foreach($params as $key => $value) { + if (strpos($key, 'f.') !== false) { + $query->addParam($key, null); + } + } + + return $parentBuilder; + } + + //@todo check of $this->queryToBuilder->getFacetSet() can be used + $facetingParameters = $this->getFacetParameters(); + foreach($facetingParameters as $key => $value) { + $query->addParam($key, $value); + } + + return $parentBuilder; + } } diff --git a/Classes/Domain/Search/Query/ParameterBuilder/FieldCollapsing.php b/Classes/Domain/Search/Query/ParameterBuilder/FieldCollapsing.php index 607b2ca91b..39d572e46c 100644 --- a/Classes/Domain/Search/Query/ParameterBuilder/FieldCollapsing.php +++ b/Classes/Domain/Search/Query/ParameterBuilder/FieldCollapsing.php @@ -24,8 +24,10 @@ * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\ParameterBuilder; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; +use Solarium\QueryType\Select\Query\Query; /** * The FieldCollapsing ParameterProvider is responsible to build the solr query parameters @@ -33,7 +35,7 @@ * * @package ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder */ -class FieldCollapsing extends AbstractDeactivatableParameterBuilder implements ParameterBuilder +class FieldCollapsing extends AbstractDeactivatable implements ParameterBuilder { /** * @var string @@ -113,29 +115,6 @@ public function setExpandRowCount(int $expandRowCount) $this->expandRowCount = $expandRowCount; } - /** - * @param Query $query - * @return Query - */ - public function build(Query $query): Query - { - if (!$this->isEnabled) { - $query->getFilters()->removeByName('collapsing'); - $query->getQueryParametersContainer()->remove('expand'); - $query->getQueryParametersContainer()->remove('expand.rows'); - - return $query; - } - - $query->getFilters()->add('{!collapse field=' . $this->collapseFieldName . '}', 'collapsing'); - if ($this->expand) { - $query->getQueryParametersContainer()->set('expand', 'true'); - $query->getQueryParametersContainer()->set('expand.rows', $this->expandRowCount); - } - - return $query; - } - /** * @param TypoScriptConfiguration $solrConfiguration * @return FieldCollapsing @@ -148,7 +127,7 @@ public static function fromTypoScriptConfiguration(TypoScriptConfiguration $solr } $collapseField = $solrConfiguration->getSearchVariantsField(); - $expand = $solrConfiguration->getSearchVariantsExpand(); + $expand = (bool)$solrConfiguration->getSearchVariantsExpand(); $expandRows = $solrConfiguration->getSearchVariantsLimit(); return new FieldCollapsing(true, $collapseField, $expand, $expandRows); @@ -161,4 +140,24 @@ public static function getEmpty() { return new FieldCollapsing(false); } + + /** + * @param QueryBuilder $parentBuilder + * @return QueryBuilder + */ + public function build(QueryBuilder $parentBuilder): QueryBuilder + { + $query = $parentBuilder->getQuery(); + if(!$this->getIsEnabled()) { + return $parentBuilder; + } + + $parentBuilder->useFilter('{!collapse field=' . $this->getCollapseFieldName(). '}', 'fieldCollapsing'); + if($this->getIsExpand()) { + $query->addParam('expand', 'true'); + $query->addParam('expand.rows', $this->getExpandRowCount()); + } + + return $parentBuilder; + } } \ No newline at end of file diff --git a/Classes/Domain/Search/Query/ParameterBuilder/Filters.php b/Classes/Domain/Search/Query/ParameterBuilder/Filters.php index 8a4cdb1006..70ccb6be6f 100644 --- a/Classes/Domain/Search/Query/ParameterBuilder/Filters.php +++ b/Classes/Domain/Search/Query/ParameterBuilder/Filters.php @@ -24,7 +24,6 @@ * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -34,7 +33,7 @@ * * @package ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder */ -class Filters implements ParameterBuilder +class Filters { /** @@ -142,16 +141,6 @@ public function getValues() return $this->filters; } - /** - * @param Query $query - * @return Query - */ - public function build(Query $query): Query - { - $query->getQueryParametersContainer()->set('fq', array_values($this->filters)); - return $query; - } - /** * @param TypoScriptConfiguration $solrConfiguration * @return Filters diff --git a/Classes/Domain/Search/Query/ParameterBuilder/Grouping.php b/Classes/Domain/Search/Query/ParameterBuilder/Grouping.php index 6b09ad0244..149d72a39c 100644 --- a/Classes/Domain/Search/Query/ParameterBuilder/Grouping.php +++ b/Classes/Domain/Search/Query/ParameterBuilder/Grouping.php @@ -24,8 +24,10 @@ * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\ParameterBuilder; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; +use Solarium\QueryType\Select\Query\Query; /** * The Grouping ParameterProvider is responsible to build the solr query parameters @@ -33,7 +35,7 @@ * * @package ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder */ -class Grouping extends AbstractDeactivatableParameterBuilder implements ParameterBuilder +class Grouping extends AbstractDeactivatable implements ParameterBuilder { /** @@ -81,42 +83,6 @@ public function __construct($isEnabled, array $fields = [], array $sortings = [] $this->resultsPerGroup = $resultsPerGroup; } - /** - * @param Query $query - * @return Query - */ - public function build(Query $query): Query - { - if (!$this->isEnabled) { - $query->getQueryParametersContainer()->removeMany(['group', 'group.format', 'group.ngroups', 'group.limit', 'group.query', 'group.sort', 'group.field']); - - return $query; - } - $groupingParameter = []; - $groupingParameter ['group'] = 'true'; - $groupingParameter ['group.format'] = 'grouped'; - $groupingParameter ['group.ngroups'] = 'true'; - - if ($this->resultsPerGroup) { - $groupingParameter['group.limit'] = $this->resultsPerGroup; - } - - if (count($this->queries) > 0) { - $groupingParameter['group.query'] = $this->queries; - } - - if (count($this->sortings) > 0) { - $groupingParameter['group.sort'] = $this->sortings; - } - - if (count($this->fields) > 0) { - $groupingParameter['group.field'] = $this->fields; - } - - $query->getQueryParametersContainer()->merge($groupingParameter); - return $query; - } - /** * @return array */ @@ -264,4 +230,27 @@ public static function getEmpty() { return new Grouping(false); } + + /** + * @param QueryBuilder $parentBuilder + * @return QueryBuilder + */ + public function build(QueryBuilder $parentBuilder): QueryBuilder + { + $query = $parentBuilder->getQuery(); + if(!$this->getIsEnabled()) { + $query->removeComponent($query->getGrouping()); + return $parentBuilder; + } + + $query->getGrouping()->setFields($this->getFields()); + $query->getGrouping()->setLimit($this->getResultsPerGroup()); + $query->getGrouping()->setQueries($this->getQueries()); + $query->getGrouping()->setFormat('grouped'); + $query->getGrouping()->setNumberOfGroups(true); + + $sorting = implode(' ', $this->getSortings()); + $query->getGrouping()->setSort($sorting); + return $parentBuilder; + } } \ No newline at end of file diff --git a/Classes/Domain/Search/Query/ParameterBuilder/Highlighting.php b/Classes/Domain/Search/Query/ParameterBuilder/Highlighting.php index 95b1421e39..ef55a92e1d 100644 --- a/Classes/Domain/Search/Query/ParameterBuilder/Highlighting.php +++ b/Classes/Domain/Search/Query/ParameterBuilder/Highlighting.php @@ -24,8 +24,9 @@ * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; +use TYPO3\CMS\Core\Utility\GeneralUtility; /** * The Highlighting ParameterProvider is responsible to build the solr query parameters @@ -33,7 +34,7 @@ * * @package ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder */ -class Highlighting extends AbstractDeactivatableParameterBuilder implements ParameterBuilder +class Highlighting extends AbstractDeactivatable implements ParameterBuilder { /** * @var int @@ -138,44 +139,14 @@ public function setPostfix(string $postfix) } /** - * @param Query $query - * @return Query + * @return bool */ - public function build(Query $query): Query + public function getUseFastVectorHighlighter() { - if (!$this->isEnabled) { - $query->getQueryParametersContainer()->removeMany(['hl', 'hl.fragsize', 'hl.fl', 'hl.useFastVectorHighlighter', 'hl.tag.pre', 'hl.tag.post', 'hl.simple.pre', 'hl.simple.post']); - - return $query; - } - - $highlightingParameter = []; - $highlightingParameter['hl'] = 'true'; - $highlightingParameter['hl.fragsize'] = (int)$this->fragmentSize; - - if ($this->highlightingFieldList != '') { - $highlightingParameter['hl.fl'] = $this->highlightingFieldList; - } - - // the fast vector highlighter can only be used, when the fragmentSize is - // higher then 17 otherwise solr throws an exception - $useFastVectorHighlighter = ($this->fragmentSize >= 18); - - if ($useFastVectorHighlighter) { - $highlightingParameter['hl.useFastVectorHighlighter'] = 'true'; - $highlightingParameter['hl.tag.pre'] = $this->prefix; - $highlightingParameter['hl.tag.post'] = $this->postfix; - } - - if ($this->prefix !== '' && $this->postfix !== '') { - $highlightingParameter['hl.simple.pre'] = $this->prefix; - $highlightingParameter['hl.simple.post'] = $this->postfix; - } - - $query->getQueryParametersContainer()->merge($highlightingParameter); - return $query; + return ($this->fragmentSize >= 18); } + /** * @param TypoScriptConfiguration $solrConfiguration * @return Highlighting @@ -204,4 +175,39 @@ public static function getEmpty() { return new Highlighting(false); } + + + /** + * @param QueryBuilder $parentBuilder + * @return QueryBuilder + */ + public function build(QueryBuilder $parentBuilder): QueryBuilder + { + $query = $parentBuilder->getQuery(); + if(!$this->getIsEnabled()) { + $query->removeComponent($query->getHighlighting()); + return $parentBuilder; + } + + $query->getHighlighting()->setFragSize($this->getFragmentSize()); + $query->getHighlighting()->setFields(GeneralUtility::trimExplode(",", $this->getHighlightingFieldList())); + + if ($this->getUseFastVectorHighlighter()) { + $query->getHighlighting()->setUseFastVectorHighlighter(true); + $query->getHighlighting()->setTagPrefix($this->getPrefix()); + $query->getHighlighting()->setTagPostfix($this->getPostfix()); + } else { + $query->getHighlighting()->setUseFastVectorHighlighter(false); + $query->getHighlighting()->setTagPrefix(null); + $query->getHighlighting()->setTagPostfix(null); + } + + if ($this->getPrefix() !== '' && $this->getPostfix() !== '') { + $query->getHighlighting()->setSimplePrefix($this->getPrefix()); + $query->getHighlighting()->setSimplePostfix($this->getPostfix()); + } + + return $parentBuilder; + + } } \ No newline at end of file diff --git a/Classes/Domain/Search/Query/ParameterBuilder/Operator.php b/Classes/Domain/Search/Query/ParameterBuilder/Operator.php index 06f1c1cc3b..3c44aa8bdc 100644 --- a/Classes/Domain/Search/Query/ParameterBuilder/Operator.php +++ b/Classes/Domain/Search/Query/ParameterBuilder/Operator.php @@ -24,8 +24,6 @@ * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; -use Doctrine\Common\Proxy\Exception\InvalidArgumentException; /** * The Operator ParameterProvider is responsible to build the solr query parameters @@ -33,7 +31,7 @@ * * @package ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder */ -class Operator extends AbstractDeactivatableParameterBuilder implements ParameterBuilder +class Operator extends AbstractDeactivatable { const OPERATOR_AND = 'AND'; const OPERATOR_OR = 'OR'; @@ -61,12 +59,20 @@ public function __construct($isEnabled, $operator = Operator::OPERATOR_AND) public function setOperator($operator) { if (!in_array($operator, [self::OPERATOR_AND, self::OPERATOR_OR])) { - throw new InvalidArgumentException("Invalid operator"); + throw new \InvalidArgumentException("Invalid operator"); } $this->operator = $operator; } + /** + * @return string + */ + public function getOperator(): string + { + return $this->operator; + } + /** * @return Operator */ @@ -91,21 +97,6 @@ public static function getOr(): Operator return new Operator(true, static::OPERATOR_OR); } - /** - * @param Query $query - * @return Query - */ - public function build(Query $query): Query - { - if (!$this->isEnabled) { - $query->getQueryParametersContainer()->remove('q.op'); - return $query; - } - - $query->getQueryParametersContainer()->set('q.op', $this->operator); - return $query; - } - /** * @param string $operator * @return Operator diff --git a/Classes/Domain/Search/Query/ParameterBuilder/ParameterBuilder.php b/Classes/Domain/Search/Query/ParameterBuilder/ParameterBuilder.php index 86d68f5e09..6dbbf8a3ac 100644 --- a/Classes/Domain/Search/Query/ParameterBuilder/ParameterBuilder.php +++ b/Classes/Domain/Search/Query/ParameterBuilder/ParameterBuilder.php @@ -25,7 +25,8 @@ * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; + /** * The implementation of ParameterBuilder is responsible to build an array with @@ -38,8 +39,8 @@ interface ParameterBuilder { /** - * @param Query $query - * @return Query + * @param QueryBuilder $parentBuilder + * @return QueryBuilder */ - public function build(Query $query): Query; + public function build(QueryBuilder $parentBuilder): QueryBuilder; } \ No newline at end of file diff --git a/Classes/Domain/Search/Query/ParameterBuilder/PhraseFields.php b/Classes/Domain/Search/Query/ParameterBuilder/PhraseFields.php index 85d091f551..7f0d66906c 100644 --- a/Classes/Domain/Search/Query/ParameterBuilder/PhraseFields.php +++ b/Classes/Domain/Search/Query/ParameterBuilder/PhraseFields.php @@ -23,12 +23,14 @@ * * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ + +use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; /** * The PhraseFields class */ -class PhraseFields extends AbstractFieldList +class PhraseFields extends AbstractFieldList implements ParameterBuilder { /** * Parameter key which should be used for Apache Solr URL query @@ -51,7 +53,7 @@ public static function fromString(string $fieldListString, string $delimiter = ' /** * @param TypoScriptConfiguration $solrConfiguration - * @return BigramPhraseFields + * @return PhraseFields */ public static function fromTypoScriptConfiguration(TypoScriptConfiguration $solrConfiguration) { @@ -62,4 +64,32 @@ public static function fromTypoScriptConfiguration(TypoScriptConfiguration $solr return self::fromString((string)$solrConfiguration->getSearchQueryPhraseFields()); } + + /** + * Parses the string representation of the fieldList (e.g. content^100, title^10) to the object representation. + * + * @param string $fieldListString + * @param string $delimiter + * @return PhraseFields + */ + protected static function initializeFromString(string $fieldListString, string $delimiter = ',') : PhraseFields + { + $fieldList = self::buildFieldList($fieldListString, $delimiter); + return new PhraseFields(true, $fieldList); + } + + /** + * @param QueryBuilder $parentBuilder + * @return QueryBuilder + */ + public function build(QueryBuilder $parentBuilder): QueryBuilder + { + $phraseFieldString = $this->toString(); + if ($phraseFieldString === '' || !$this->getIsEnabled()) { + return $parentBuilder; + } + + $parentBuilder->getQuery()->getEDisMax()->setPhraseFields($phraseFieldString); + return $parentBuilder; + } } diff --git a/Classes/Domain/Search/Query/ParameterBuilder/QueryFields.php b/Classes/Domain/Search/Query/ParameterBuilder/QueryFields.php index a4fb05a9a0..9d83a4af7e 100644 --- a/Classes/Domain/Search/Query/ParameterBuilder/QueryFields.php +++ b/Classes/Domain/Search/Query/ParameterBuilder/QueryFields.php @@ -24,7 +24,7 @@ * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; use TYPO3\CMS\Core\Utility\GeneralUtility; /** @@ -45,7 +45,7 @@ class QueryFields implements ParameterBuilder * * @param array $queryFields */ - public function __construct(array $queryFields) + public function __construct(array $queryFields = []) { $this->queryFields = $queryFields; } @@ -59,23 +59,6 @@ public function set($fieldName, $boost = 1.0) $this->queryFields[$fieldName] = (float)$boost; } - /** - * @param Query $query - * @return Query - */ - public function build(Query $query): Query - { - $string = $this->toString(); - // return empty array on empty string - - if (trim($string) === '') { - return $query; - } - - $query->getQueryParametersContainer()->set('qf', $string); - return $query; - } - /** * Creates the string representation * @@ -123,4 +106,14 @@ public static function fromString($queryFieldsString, $delimiter = ',') { return new QueryFields($queryFields); } + + /** + * @param QueryBuilder $parentBuilder + * @return QueryBuilder + */ + public function build(QueryBuilder $parentBuilder): QueryBuilder + { + $parentBuilder->getQuery()->getEDisMax()->setQueryFields($this->toString()); + return $parentBuilder; + } } \ No newline at end of file diff --git a/Classes/Domain/Search/Query/ParameterBuilder/ReturnFields.php b/Classes/Domain/Search/Query/ParameterBuilder/ReturnFields.php index 9a16d10f46..e714c63fa2 100644 --- a/Classes/Domain/Search/Query/ParameterBuilder/ReturnFields.php +++ b/Classes/Domain/Search/Query/ParameterBuilder/ReturnFields.php @@ -1,4 +1,5 @@ fieldList = $fieldList; } @@ -80,16 +81,6 @@ public function remove($fieldName) } } - /** - * @param Query $query - * @return Query - */ - public function build(Query $query): Query - { - $query->getQueryParametersContainer()->set('fl', $this->toString()); - return $query; - } - /** * @param string $delimiter * @return string @@ -126,4 +117,14 @@ public function getValues() { return array_unique(array_values($this->fieldList)); } + + /** + * @param QueryBuilder $parentBuilder + * @return QueryBuilder + */ + public function build(QueryBuilder $parentBuilder): QueryBuilder + { + $parentBuilder->getQuery()->setFields($this->getValues()); + return $parentBuilder; + } } \ No newline at end of file diff --git a/Classes/Domain/Search/Query/ParameterBuilder/Slops.php b/Classes/Domain/Search/Query/ParameterBuilder/Slops.php index a304ff70bb..d3e075df4d 100644 --- a/Classes/Domain/Search/Query/ParameterBuilder/Slops.php +++ b/Classes/Domain/Search/Query/ParameterBuilder/Slops.php @@ -1,4 +1,5 @@ querySlop !== null; + } + + /** + * @return int|null + */ + public function getQuerySlop() { return $this->querySlop; } @@ -90,9 +100,18 @@ public function setQuerySlop(int $querySlop) } /** - * @return int + * @return boolean */ - public function getPhraseSlop(): int + public function getHasPhraseSlop() + { + return $this->phraseSlop !== null; + } + + + /** + * @return int|null + */ + public function getPhraseSlop() { return $this->phraseSlop; } @@ -106,9 +125,17 @@ public function setPhraseSlop(int $phraseSlop) } /** - * @return int + * @return boolean + */ + public function getHasBigramPhraseSlop() + { + return $this->bigramPhraseSlop !== null; + } + + /** + * @return int|null */ - public function getBigramPhraseSlop(): int + public function getBigramPhraseSlop() { return $this->bigramPhraseSlop; } @@ -122,33 +149,27 @@ public function setBigramPhraseSlop(int $bigramPhraseSlop) } /** - * @return int + * @return boolean */ - public function getTrigramPhraseSlop(): int + public function getHasTrigramPhraseSlop() { - return $this->trigramPhraseSlop; + return $this->trigramPhraseSlop !== null; } /** - * @param int $trigramPhraseSlop + * @return int|null */ - public function setTrigramPhraseSlop(int $trigramPhraseSlop) + public function getTrigramPhraseSlop() { - $this->trigramPhraseSlop = $trigramPhraseSlop; + return $this->trigramPhraseSlop; } /** - * @param Query $query - * @return Query + * @param int $trigramPhraseSlop */ - public function build(Query $query): Query + public function setTrigramPhraseSlop(int $trigramPhraseSlop) { - $query->getQueryParametersContainer()->setWhenIntOrUnsetWhenNull('qs', $this->querySlop); - $query->getQueryParametersContainer()->setWhenIntOrUnsetWhenNull('ps', $this->phraseSlop); - $query->getQueryParametersContainer()->setWhenIntOrUnsetWhenNull('ps2', $this->bigramPhraseSlop); - $query->getQueryParametersContainer()->setWhenIntOrUnsetWhenNull('ps3', $this->trigramPhraseSlop); - - return $query; + $this->trigramPhraseSlop = $trigramPhraseSlop; } /** @@ -208,4 +229,31 @@ protected static function getTrigramPhraseSlopFromConfiguration($searchConfigura $trigramSlopConfigured = !empty($searchConfiguration['query.']['trigramPhrase.']['slop']); return ($trigramPhraseEnabled && $trigramSlopConfigured) ? $searchConfiguration['query.']['trigramPhrase.']['slop'] : self::NO_SLOP; } + + /** + * @param QueryBuilder $parentBuilder + * @return QueryBuilder + */ + public function build(QueryBuilder $parentBuilder): QueryBuilder + { + $query = $parentBuilder->getQuery(); + + if ($this->getHasPhraseSlop()) { + $query->getEDisMax()->setPhraseSlop($this->getPhraseSlop()); + } + + if ($this->getHasBigramPhraseSlop()) { + $query->getEDisMax()->setPhraseBigramSlop($this->getBigramPhraseSlop()); + } + + if ($this->getHasTrigramPhraseSlop()) { + $query->getEDisMax()->setPhraseTrigramSlop($this->getTrigramPhraseSlop()); + } + + if ($this->getHasQuerySlop()) { + $query->getEDisMax()->setQueryPhraseSlop($this->getQuerySlop()); + } + + return $parentBuilder; + } } \ No newline at end of file diff --git a/Classes/Domain/Search/Query/ParameterBuilder/Sorting.php b/Classes/Domain/Search/Query/ParameterBuilder/Sorting.php index cf00cc82a7..4ba80b553c 100644 --- a/Classes/Domain/Search/Query/ParameterBuilder/Sorting.php +++ b/Classes/Domain/Search/Query/ParameterBuilder/Sorting.php @@ -23,7 +23,7 @@ * * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; +use TYPO3\CMS\Core\Utility\GeneralUtility; /** @@ -32,7 +32,7 @@ * * @package ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder */ -class Sorting extends AbstractDeactivatableParameterBuilder implements ParameterBuilder +class Sorting extends AbstractDeactivatable { const SORT_ASC = 'ASC'; const SORT_DESC = 'DESC'; @@ -40,68 +40,75 @@ class Sorting extends AbstractDeactivatableParameterBuilder implements Parameter /** * @var string */ - protected $sortField = ''; + protected $fieldName = ''; + + /** + * @var string + */ + protected $direction = self::SORT_ASC; /** * Debug constructor. * * @param bool $isEnabled - * @param string $sortField + * @param string $fieldName + * @param string $direction */ - public function __construct($isEnabled = false, $sortField = '') + public function __construct($isEnabled = false, $fieldName = '', $direction = self::SORT_ASC) { $this->isEnabled = $isEnabled; - $this->setSortField($sortField); + $this->setFieldName($fieldName); + $this->setDirection($direction); } /** - * @param Query $query - * @return Query + * @return Sorting */ - public function build(Query $query): Query + public static function getEmpty() { - $isEnabledAndField = $this->isEnabled && !empty($this->sortField); - if (!$isEnabledAndField) { - $query->getQueryParametersContainer()->remove('sort'); - return $query; - } - - $query->getQueryParametersContainer()->set('sort', $this->sortField); - return $query; + return new Sorting(false); } /** - * @return Sorting + * @return string */ - public static function getEmpty() + public function getFieldName(): string { - return new Sorting(false); + return $this->fieldName; } /** - * @param string $sortField + * @param string $fieldName */ - public function setSortField($sortField) + public function setFieldName(string $fieldName) { - $sortField = $this->removeRelevanceSortField($sortField); - $this->sortField = $sortField; + $this->fieldName = $fieldName; } /** - * Removes the relevance sort field if present in the sorting field definition. - * - * @param string $sorting * @return string */ - protected function removeRelevanceSortField($sorting) + public function getDirection(): string { - $sortParameter = $sorting; - list($sortField) = explode(' ', $sorting); - if ($sortField === 'relevance') { - $sortParameter = ''; - return $sortParameter; - } + return $this->direction; + } - return $sortParameter; + /** + * @param string $direction + */ + public function setDirection(string $direction) + { + $this->direction = $direction; + } + + /** + * Parses a sorting representation " " + * @param string $sortingString + * @return Sorting + */ + public static function fromString($sortingString) + { + $parts = GeneralUtility::trimExplode(' ', $sortingString); + return new Sorting(true, $parts[0], $parts[1]); } } \ No newline at end of file diff --git a/Classes/Domain/Search/Query/ParameterBuilder/Sortings.php b/Classes/Domain/Search/Query/ParameterBuilder/Sortings.php new file mode 100644 index 0000000000..360d9b7371 --- /dev/null +++ b/Classes/Domain/Search/Query/ParameterBuilder/Sortings.php @@ -0,0 +1,101 @@ + + * All rights reserved + * + * This script is part of the TYPO3 project. The TYPO3 project is + * free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * The GNU General Public License can be found at + * http://www.gnu.org/copyleft/gpl.html. + * + * This script is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This copyright notice MUST APPEAR in all copies of the script! + ***************************************************************/ +use TYPO3\CMS\Core\Utility\GeneralUtility; + + +/** + * The Sorting ParameterProvider is responsible to build the solr query parameters + * that are needed for the sorting. + * + * @package ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder + */ +class Sortings extends AbstractDeactivatable +{ + /** + * @var array + */ + protected $sortings = []; + + /** + * Sortings constructor. + * @param bool $isEnabled + * @param array $sortings + */ + public function __construct($isEnabled = false, $sortings = []) + { + $this->isEnabled = $isEnabled; + $this->setSortings($sortings); + } + + /** + * @return Sortings + */ + public static function getEmpty() + { + return new Sortings(false); + } + + /** + * @return Sorting[] + */ + public function getSortings(): array + { + return $this->sortings; + } + + /** + * @param array $sortings + */ + public function setSortings(array $sortings) + { + $this->sortings = $sortings; + } + + /** + * @param Sorting $sorting + */ + public function addSorting(Sorting $sorting) + { + $this->sortings[] = $sorting; + } + + /** + * Parses a sortings representation " , " + * @param string $sortingsString + * @return Sortings + */ + public static function fromString($sortingsString) + { + $sortFields = GeneralUtility::trimExplode(',',$sortingsString); + $sortings = []; + foreach($sortFields as $sortField) { + $sorting = Sorting::fromString($sortField); + $sortings[] = $sorting; + } + + return new Sortings(true, $sortings); + } +} \ No newline at end of file diff --git a/Classes/Domain/Search/Query/ParameterBuilder/Spellchecking.php b/Classes/Domain/Search/Query/ParameterBuilder/Spellchecking.php index d326bd87f3..25557dfa37 100644 --- a/Classes/Domain/Search/Query/ParameterBuilder/Spellchecking.php +++ b/Classes/Domain/Search/Query/ParameterBuilder/Spellchecking.php @@ -24,7 +24,7 @@ * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; /** @@ -33,7 +33,7 @@ * * @package ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder */ -class Spellchecking extends AbstractDeactivatableParameterBuilder implements ParameterBuilder +class Spellchecking extends AbstractDeactivatable implements ParameterBuilder { /** @@ -54,23 +54,11 @@ public function __construct($isEnabled = false, int $maxCollationTries = 0) } /** - * @param Query $query - * @return Query + * @return int */ - public function build(Query $query): Query + public function getMaxCollationTries(): int { - if (!$this->isEnabled) { - $query->getQueryParametersContainer()->removeMany(['spellcheck', 'spellcheck.collate', 'spellcheck.maxCollationTries']); - return $query; - } - - $spellcheckingParameters = []; - $spellcheckingParameters['spellcheck'] = 'true'; - $spellcheckingParameters['spellcheck.collate'] = 'true'; - $spellcheckingParameters['spellcheck.maxCollationTries'] = $this->maxCollationTries; - - $query->getQueryParametersContainer()->merge($spellcheckingParameters); - return $query; + return $this->maxCollationTries; } /** @@ -96,4 +84,19 @@ public static function getEmpty() { return new Spellchecking(false); } + + /** + * @param QueryBuilder $parentBuilder + * @return QueryBuilder + */ + public function build(QueryBuilder $parentBuilder): QueryBuilder + { + $query = $parentBuilder->getQuery(); + if (!$this->getIsEnabled()) { + $query->removeComponent($query->getSpellcheck()); + return $parentBuilder; + } + $query->getSpellcheck()->setMaxCollationTries($this->getMaxCollationTries()); + return $parentBuilder; + } } \ No newline at end of file diff --git a/Classes/Domain/Search/Query/ParameterBuilder/TrigramPhraseFields.php b/Classes/Domain/Search/Query/ParameterBuilder/TrigramPhraseFields.php index aab2596591..ae4e857ef7 100644 --- a/Classes/Domain/Search/Query/ParameterBuilder/TrigramPhraseFields.php +++ b/Classes/Domain/Search/Query/ParameterBuilder/TrigramPhraseFields.php @@ -23,12 +23,14 @@ * * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ + +use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; /** * The TrigramPhraseFields class */ -class TrigramPhraseFields extends AbstractFieldList +class TrigramPhraseFields extends AbstractFieldList implements ParameterBuilder { /** * Parameter key which should be used for Apache Solr URL query @@ -62,4 +64,31 @@ public static function fromTypoScriptConfiguration(TypoScriptConfiguration $solr return self::fromString((string)$solrConfiguration->getSearchQueryTrigramPhraseFields()); } + + /** + * Parses the string representation of the fieldList (e.g. content^100, title^10) to the object representation. + * + * @param string $fieldListString + * @param string $delimiter + * @return TrigramPhraseFields + */ + protected static function initializeFromString(string $fieldListString, string $delimiter = ',') : TrigramPhraseFields + { + $fieldList = self::buildFieldList($fieldListString, $delimiter); + return new TrigramPhraseFields(true, $fieldList); + } + + /** + * @param QueryBuilder $parentBuilder + * @return QueryBuilder + */ + public function build(QueryBuilder $parentBuilder): QueryBuilder + { + $trigramPhraseFieldsString = $this->toString(); + if ($trigramPhraseFieldsString === '' || !$this->getIsEnabled()) { + return $parentBuilder; + } + $parentBuilder->getQuery()->getEDisMax()->setPhraseTrigramFields($trigramPhraseFieldsString); + return $parentBuilder; + } } diff --git a/Classes/Domain/Search/Query/Query.php b/Classes/Domain/Search/Query/Query.php index 4009d8d73f..d063a80546 100644 --- a/Classes/Domain/Search/Query/Query.php +++ b/Classes/Domain/Search/Query/Query.php @@ -24,736 +24,24 @@ * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Helper\Pagination; -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Helper\QueryStringContainer; -use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\BigramPhraseFields; -use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Debug; -use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Elevation; -use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Faceting; -use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\FieldCollapsing; -use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Filters; -use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Grouping; -use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Highlighting; -use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Operator; -use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\PhraseFields; -use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\QueryFields; -use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\ReturnFields; -use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Slops; -use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Sorting; -use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Spellchecking; -use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\TrigramPhraseFields; +use Solarium\QueryType\Select\Query\Query as SolariumQuery; -/** - * A Solr search query - * - * @author Ingo Renner - * @author Timo Hund - */ -class Query -{ - /** - * @var QueryStringContainer - */ - protected $queryStringContainer = null; - - /** - * @var Pagination - */ - protected $pagination = null; - - /** - * @var Operator - */ - protected $operator = null; - - /** - * ParameterBuilder for filters. - * - * @var Filters - */ - protected $filters = null; - - /** - * Holds the query fields with their associated boosts. The key represents - * the field name, value represents the field's boost. These are the fields - * that will actually be searched. - * - * Used in Solr's qf parameter - * - * @var QueryFields - * @see http://wiki.apache.org/solr/DisMaxQParserPlugin#qf_.28Query_Fields.29 - */ - protected $queryFields = null; - - /** - * Holds the phrase fields with their associated boosts. The key represents - * the field name, value represents the field's boost. These are the fields - * for those Apache Solr should build phrase quieries and by phrase occurrences should be boosted. - * - * @var PhraseFields - * @see https://lucene.apache.org/solr/guide/7_0/the-dismax-query-parser.html#pf-phrase-fields-parameter - */ - protected $phraseFields; - - /** - * Holds the bigram phrase fields with their associated boosts. The key represents - * the field name, value represents the field's boost. These are the fields - * for those Apache Solr should build the phrases from triplets and sentences. - * - * @var BigramPhraseFields - * @see "pf2" https://lucene.apache.org/solr/guide/7_0/the-extended-dismax-query-parser.html#extended-dismax-parameters - */ - protected $bigramPhraseFields; - - /** - * Holds the trigram phrase fields with their associated boosts. The key represents - * the field name, value represents the field's boost. These are the fields - * for those Apache Solr should build the phrases from triplets and sentences. - * - * @var TrigramPhraseFields - * @see "pf3" https://lucene.apache.org/solr/guide/7_0/the-extended-dismax-query-parser.html#extended-dismax-parameters - */ - protected $trigramPhraseFields; - - /** - * List of fields that will be returned in the result documents. - * - * used in Solr's fl parameter - * - * @var ReturnFields - * @see http://wiki.apache.org/solr/CommonQueryParameters#fl - */ - protected $returnFields = null; +class Query extends SolariumQuery { /** - * ParameterBuilder for the highlighting. + * Returns the query parameters that should be used. * - * @var Highlighting - */ - protected $highlighting = null; - - /** - * ParameterBuilder for the faceting. - * - * @var Faceting - */ - protected $faceting = null; - - /** - * ParameterBuilder for the spellchecking - * - * @var Spellchecking - */ - protected $spellchecking = null; - - /** - * ParameterBuilder for the grouping. - * - * @var Grouping - */ - protected $grouping = null; - - /** - * ParameterBuilder for the field collapsing (variants) - * - * @var FieldCollapsing - */ - protected $fieldCollapsing = null; - - /** - * ParameterBuilder for the debugging. - * - * @var Debug - */ - protected $debug = null; - - /** - * ParameterBuilder for the sorting - * - * @var Sorting - */ - protected $sorting = null; - - /** - * ParameterBuilder for the slops (qs,ps,ps2,ps3) - * - * @var Slops - */ - protected $slops = null; - - /** - * ParameterBuilder for the elevation - * - * @var Elevation - */ - protected $elevation = null; - - /** - * @var QueryParametersContainer - */ - protected $queryParametersContainer = null; - - /** - * Query constructor. - * @param string $keywords - */ - public function __construct($keywords) - { - $this->queryStringContainer = new QueryStringContainer((string)$keywords); - $this->pagination = new Pagination(); - $this->filters = new Filters(); - $this->queryFields = QueryFields::fromString('*'); - $this->returnFields = ReturnFields::fromArray(['*', 'score']); - - $this->faceting = new Faceting(false); - $this->grouping = new Grouping(false); - $this->highlighting = new Highlighting(false); - $this->bigramPhraseFields = new BigramPhraseFields(false); - $this->trigramPhraseFields = new TrigramPhraseFields(false); - $this->phraseFields = new PhraseFields(false); - $this->spellchecking = new Spellchecking(false); - $this->debug = new Debug(false); - $this->sorting = new Sorting(false); - $this->fieldCollapsing = new FieldCollapsing(false); - $this->elevation = new Elevation(false); - $this->operator = new Operator(false); - $this->slops = new Slops(); - - $this->queryParametersContainer = new QueryParametersContainer(); - } - - /** - * @param QueryFields $queryFields - */ - public function setQueryFields(QueryFields $queryFields) - { - $this->queryFields = $queryFields; - } - - /** - * @return QueryFields - */ - public function getQueryFields() - { - return $this->queryFields; - } - - /** - * @param PhraseFields $phraseFields - * @return void - */ - public function setPhraseFields(PhraseFields $phraseFields) - { - $this->phraseFields = $phraseFields; - } - - /** - * @return PhraseFields - */ - public function getPhraseFields() - { - return $this->phraseFields; - } - - /** - * @return BigramPhraseFields - */ - public function getBigramPhraseFields() - { - return $this->bigramPhraseFields; - } - - /** - * @param BigramPhraseFields $bigramPhraseFields - * @return void - */ - public function setBigramPhraseFields(BigramPhraseFields $bigramPhraseFields) - { - $this->bigramPhraseFields = $bigramPhraseFields; - } - - /** - * @return TrigramPhraseFields - */ - public function getTrigramPhraseFields() - { - return $this->trigramPhraseFields; - } - - /** - * @param TrigramPhraseFields $trigramPhraseFields - * @return void + * @return array */ - public function setTrigramPhraseFields(TrigramPhraseFields $trigramPhraseFields) - { - $this->trigramPhraseFields = $trigramPhraseFields; + public function getQueryParameters() { + return $this->getParams(); } /** - * returns a string representation of the query - * - * @return string the string representation of the query + * @return string */ public function __toString() { - return $this->queryStringContainer->__toString(); - } - - /** - * Builds the query string which is then used for Solr's q parameters - * - * @return QueryStringContainer - */ - public function getQueryStringContainer() - { - return $this->queryStringContainer; - } - - /** - * @param QueryStringContainer $queryStringContainer - */ - public function setQueryStringContainer(QueryStringContainer $queryStringContainer) - { - $this->queryStringContainer = $queryStringContainer; - } - - /** - * @return Pagination - */ - public function getPagination(): Pagination - { - return $this->pagination; - } - - /** - * @param Pagination $pagination - */ - public function setPagination(Pagination $pagination) - { - $this->pagination = $pagination; - } - - // query elevation - - /** - * @param Elevation $elevation - */ - public function setElevation(Elevation $elevation) - { - $this->elevation = $elevation; - } - - /** - * @return Elevation - */ - public function getElevation(): Elevation - { - return $this->elevation; - } - - // collapsing - - /** - * @param FieldCollapsing $fieldCollapsing - */ - public function setFieldCollapsing(FieldCollapsing $fieldCollapsing) - { - $this->fieldCollapsing = $fieldCollapsing; - } - - /** - * @return FieldCollapsing - */ - public function getFieldCollapsing(): FieldCollapsing - { - return $this->fieldCollapsing; - } - - // grouping - - /** - * Activates and deactivates grouping for the current query. - * - * @param Grouping $grouping TRUE to enable grouping, FALSE to disable grouping - * @return void - */ - public function setGrouping(Grouping $grouping) - { - $this->grouping = $grouping; - } - - /** - * @return Grouping - */ - public function getGrouping(): Grouping - { - return $this->grouping; - } - - /** - * Returns the number of results that should be shown per page or the number of groups, when grouping is active - * - * @return int number of results to show per page - */ - public function getRows() - { - if ($this->getGrouping() instanceof Grouping && $this->getGrouping()->getIsEnabled()) { - return $this->getGrouping()->getNumberOfGroups(); - } - - return $this->getPagination()->getResultsPerPage(); - } - - // faceting - - /** - * Activates and deactivates faceting for the current query. - * - * @param Faceting $faceting TRUE to enable faceting, FALSE to disable faceting - * @return void - */ - public function setFaceting(Faceting $faceting) - { - $this->faceting = $faceting; - } - - /** - * @return Faceting - */ - public function getFaceting(): Faceting - { - return $this->faceting; - } - - /** - * Sets the filters to use. - * - * @param Filters $filters - */ - public function setFilters(Filters $filters) - { - $this->filters = $filters; - } - - /** - * Gets all currently applied filters. - * - * @return Filters Array of filters - */ - public function getFilters(): Filters - { - return $this->filters; - } - - /** - * @param Filters $filtersToAdd - * @return Filters - */ - public function addFilters(Filters $filtersToAdd) - { - foreach($filtersToAdd->getValues() as $key => $value) { - $this->getFilters()->add($value, $key); - } - - return $this->filters; - } - - /** - * @param ReturnFields $returnFields - */ - public function setReturnFields(ReturnFields $returnFields) - { - $this->returnFields = $returnFields; - } - - /** - * @return ReturnFields - */ - public function getReturnFields(): ReturnFields - { - return $this->returnFields; - } - - /** - * Gets the query type, Solr's qt parameter. - * - * @return string Query type, qt parameter. - */ - public function getQueryType() - { - return $this->queryParametersContainer->get('qt'); - } - - /** - * Sets the query type, Solr's qt parameter. - * - * @param string|bool $queryType String query type or boolean FALSE to disable / reset the qt parameter. - * @see http://wiki.apache.org/solr/CoreQueryParameters#qt - */ - public function setQueryType($queryType) - { - $this->queryParametersContainer->setWhenStringOrUnsetWhenEmpty('qt', $queryType); - } - - /** - * Set the operator that should be used for the query. Operators an be created e.g. by using - * Operator::and() - * - * @param Operator $operator - */ - public function setOperator(Operator $operator) - { - $this->operator = $operator; - } - - /** - * Returns the operator of the query. - * - * @return Operator - */ - public function getOperator(): Operator - { - return $this->operator; - } - - /** - * @return Slops - */ - public function getSlops(): Slops - { - return $this->slops; - } - - /** - * @param Slops $slops - */ - public function setSlops(Slops $slops) - { - $this->slops = $slops; - } - - /** - * Gets the alternative query, Solr's q.alt parameter. - * - * @return string Alternative query, q.alt parameter. - */ - public function getAlternativeQuery() - { - return $this->queryParametersContainer->get('q.alt'); - } - - /** - * Sets an alternative query, Solr's q.alt parameter. - * - * This query supports the complete Lucene Query Language. - * - * @param string $alternativeQuery String alternative query or boolean FALSE to disable / reset the q.alt parameter. - * @see http://wiki.apache.org/solr/DisMaxQParserPlugin#q.alt - */ - public function setAlternativeQuery($alternativeQuery) - { - $this->queryParametersContainer->setWhenStringOrUnsetWhenEmpty('q.alt', $alternativeQuery); - } - - // keywords - - /** - * Set the query to omit the response header - * - * @param bool $omitHeader TRUE (default) to omit response headers, FALSE to re-enable - */ - public function setOmitHeader($omitHeader = true) - { - $omitHeader = ($omitHeader === true) ? 'true' : $omitHeader; - $this->queryParametersContainer->setWhenStringOrUnsetWhenEmpty('omitHeader', $omitHeader); - } - - /** - * Sets the minimum match (mm) parameter - * - * @param mixed $minimumMatch Minimum match parameter as string or boolean FALSE to disable / reset the mm parameter - * @see http://wiki.apache.org/solr/DisMaxRequestHandler#mm_.28Minimum_.27Should.27_Match.29 - */ - public function setMinimumMatch($minimumMatch) - { - $this->queryParametersContainer->setWhenStringOrUnsetWhenEmpty('mm', $minimumMatch); - } - - /** - * Sets the boost function (bf) parameter - * - * @param mixed $boostFunction boost function parameter as string or boolean FALSE to disable / reset the bf parameter - * @see http://wiki.apache.org/solr/DisMaxRequestHandler#bf_.28Boost_Functions.29 - */ - public function setBoostFunction($boostFunction) - { - $this->queryParametersContainer->setWhenStringOrUnsetWhenEmpty('bf', $boostFunction); - } - - /** - * Sets the boost query (bq) parameter - * - * @param mixed $boostQuery boost query parameter as string or array to set a boost query or boolean FALSE to disable / reset the bq parameter - * @see http://wiki.apache.org/solr/DisMaxQParserPlugin#bq_.28Boost_Query.29 - */ - public function setBoostQuery($boostQuery) - { - if (is_array($boostQuery)) { - $this->queryParametersContainer->set('bq', $boostQuery); - return; - } - $this->queryParametersContainer->setWhenStringOrUnsetWhenEmpty('bq', $boostQuery); - } - - /** - * Set the tie breaker (tie) parameter - * - * @param mixed $tieParameter tie breaker parameter as string or boolean FALSE to disable / reset the tie parameter - * @return void - */ - public function setTieParameter($tieParameter) - { - $this->queryParametersContainer->setWhenStringOrUnsetWhenEmpty('tie', $tieParameter); - } - - /** - * Gets a specific query parameter by its name. - * - * @param string $parameterName The parameter to return - * @param mixed $defaultIfEmpty - * @return mixed The parameter's value or $defaultIfEmpty if not set - */ - public function getQueryParameter($parameterName, $defaultIfEmpty = null) - { - $parameters = $this->getQueryParameters(); - return isset($parameters[$parameterName]) ? $parameters[$parameterName] : $defaultIfEmpty; - } - - /** - * The build method calls build on all ParameterBuilder that fill the QueryParameterContainer - * - * @return void - */ - protected function build() - { - $this->getQueryFields()->build($this); - $this->getPhraseFields()->build($this); - $this->getBigramPhraseFields()->build($this); - $this->getTrigramPhraseFields()->build($this); - $this->getHighlighting()->build($this); - $this->getFaceting()->build($this); - $this->getGrouping()->build($this); - $this->getSpellchecking()->build($this); - $this->getFieldCollapsing()->build($this); - $this->getElevation()->build($this); - - $this->debug->build($this); - $this->sorting->build($this); - $this->operator->build($this); - $this->slops->build($this); - - // it is important that this parameters get build in the end because other builders add filters and return fields - $this->getReturnFields()->build($this); - $this->getFilters()->build($this); - } - - /** - * Builds an array of query parameters to use for the search query. - * - * @return array An array ready to use with query parameters - */ - public function getQueryParameters() - { - $this->build(); - return $this->queryParametersContainer->toArray(); - } - - /** - * @return QueryParametersContainer - */ - public function getQueryParametersContainer(): QueryParametersContainer - { - return $this->queryParametersContainer; - } - - /** - * Adds a parameter to the query. - * - * @param string $parameterName - * @param mixed $value - */ - public function addQueryParameter($parameterName, $value) - { - $this->queryParametersContainer->set($parameterName, $value); - } - - // general query parameters - - /** - * Enables or disables highlighting of search terms in result teasers. - * - * @param Highlighting $highlighting - * @see http://wiki.apache.org/solr/HighlightingParameters - * @return void - */ - public function setHighlighting(Highlighting $highlighting) - { - $this->highlighting = $highlighting; - } - - /** - * @return Highlighting - */ - public function getHighlighting(): Highlighting - { - return $this->highlighting; - } - - // misc - /** - * @param Spellchecking $spellchecking - */ - public function setSpellchecking(Spellchecking $spellchecking) - { - $this->spellchecking = $spellchecking; - } - - /** - * @return Spellchecking - */ - public function getSpellchecking(): Spellchecking - { - return $this->spellchecking; - } - - /** - * Sets the sort parameter. - * - * $sorting must include a field name (or the pseudo-field score), - * followed by a space, - * followed by a sort direction (asc or desc). - * - * Multiple fallback sortings can be separated by comma, - * ie: [, ]... - * - * @param string|bool $sorting Either a comma-separated list of sort fields and directions or FALSE to reset sorting to the default behavior (sort by score / relevance) - * @see http://wiki.apache.org/solr/CommonQueryParameters#sort - */ - public function setSorting($sorting) - { - $sorting = trim((string)$sorting); - $enabled = $sorting !== ''; - $this->sorting->setIsEnabled($enabled); - $this->sorting->setSortField($sorting); - } - - /** - * Enables or disables the debug parameter for the query. - * - * @param bool $debugMode Enables debugging when set to TRUE, deactivates debugging when set to FALSE, defaults to TRUE. - */ - public function setDebugMode($debugMode = true) - { - $this->debug->setIsEnabled($debugMode); + return $this->getQuery(); } -} +} \ No newline at end of file diff --git a/Classes/Domain/Search/Query/QueryBuilder.php b/Classes/Domain/Search/Query/QueryBuilder.php index 61c960b3d6..56df8e377c 100644 --- a/Classes/Domain/Search/Query/QueryBuilder.php +++ b/Classes/Domain/Search/Query/QueryBuilder.php @@ -36,8 +36,11 @@ use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\QueryFields; use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\ReturnFields; use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Slops; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Sorting; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Sortings; use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Spellchecking; use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\TrigramPhraseFields; +use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Facets\SortingExpression; use ApacheSolrForTypo3\Solr\Domain\Site\SiteHashService; use ApacheSolrForTypo3\Solr\Domain\Site\SiteRepository; use ApacheSolrForTypo3\Solr\FieldProcessor\PageUidToHierarchy; @@ -111,7 +114,7 @@ public function startFrom(Query $query): QueryBuilder */ public function newSearchQuery($queryString): QueryBuilder { - $this->queryToBuild = $this->getQueryInstance($queryString); + $this->queryToBuild = $this->getSearchQueryInstance($queryString); return $this; } @@ -140,21 +143,22 @@ public function getQuery(): Query * @param string|null $rawQuery * @param int $resultsPerPage * @param array $additionalFiltersFromRequest - * @return Query + * @return SearchQuery */ - public function buildSearchQuery($rawQuery, $resultsPerPage = 10, array $additionalFiltersFromRequest = []) : Query + public function buildSearchQuery($rawQuery, $resultsPerPage = 10, array $additionalFiltersFromRequest = []) : SearchQuery { if ($this->typoScriptConfiguration->getLoggingQuerySearchWords()) { $this->logger->log(SolrLogManager::INFO, 'Received search query', [$rawQuery]); } - /* @var $query Query */ + + /* @var $query SearchQuery */ return $this->newSearchQuery($rawQuery) ->useResultsPerPage($resultsPerPage) ->useReturnFieldsFromTypoScript() ->useQueryFieldsFromTypoScript() ->useInitialQueryFromTypoScript() ->useFiltersFromTypoScript() - ->useFilterArray($this->getQuery()->getFilters()->addMultiple($additionalFiltersFromRequest)->getValues()) + ->useFilterArray($additionalFiltersFromRequest) ->useFacetingFromTypoScript() ->useVariantsFromTypoScript() ->useGroupingFromTypoScript() @@ -179,9 +183,9 @@ public function buildSuggestQuery(string $queryString, array $additionalFilters, $this->newSuggestQuery($queryString) ->useFiltersFromTypoScript() ->useSiteHashFromTypoScript($requestedPageId) - ->useUserAccessGroups(explode(',', $groupList)); + ->useUserAccessGroups(explode(',', $groupList)) + ->useOmitHeader(); - $this->queryToBuild->setOmitHeader(); if (!empty($additionalFilters)) { $this->useFilterArray($additionalFilters); @@ -190,6 +194,17 @@ public function buildSuggestQuery(string $queryString, array $additionalFilters, return $this->queryToBuild; } + /** + * @param bool $omitHeader + * @return QueryBuilder + */ + public function useOmitHeader($omitHeader = true): QueryBuilder + { + $this->queryToBuild->setOmitHeader($omitHeader); + + return $this; + } + /** * Uses an array of filters and applies them to the query. * @@ -221,9 +236,8 @@ public function buildPageQuery($pageId) ->useFilter('(type:pages AND uid:' . $pageId . ') OR (*:* AND pid:' . $pageId . ' NOT type:pages)', 'type') ->useFilter('siteHash:' . $site->getSiteHash(), 'siteHash') ->useReturnFields(ReturnFields::fromString('*')) - ->useSorting('type asc, title asc') + ->useSortings(Sortings::fromString('type asc, title asc')) ->useQueryType('standard') - ->useRawQueryString() ->getQuery(); } @@ -242,9 +256,8 @@ public function buildRecordQuery($type, $uid, $pageId): Query ->useFilter('type:' . $type . ' AND uid:' . $uid, 'type') ->useFilter('siteHash:' . $site->getSiteHash(), 'siteHash') ->useReturnFields(ReturnFields::fromString('*')) - ->useSorting('type asc, title asc') + ->useSortings(Sortings::fromString('type asc, title asc')) ->useQueryType('standard') - ->useRawQueryString() ->getQuery(); } @@ -256,43 +269,73 @@ public function buildRecordQuery($type, $uid, $pageId): Query */ public function useQueryString($queryString): QueryBuilder { - $this->queryToBuild->getQueryStringContainer()->setQueryString($queryString); + $this->queryToBuild->setQuery($queryString); return $this; } /** - * Applies the useRawQueryString flag to the queryString. + * Applies the passed queryType to the query. * - * @param boolean $boolean + * @param string $queryType * @return QueryBuilder */ - public function useRawQueryString($boolean = true): QueryBuilder + public function useQueryType(string $queryType): QueryBuilder { - $this->queryToBuild->getQueryStringContainer()->useRawQueryString($boolean); + $this->queryToBuild->addParam('qt', $queryType); return $this; } /** - * Applies the passed queryType to the query. + * Remove the queryType (qt) from the query. * - * @param string $queryType * @return QueryBuilder */ - public function useQueryType($queryType): QueryBuilder + public function removeQueryType(): QueryBuilder + { + $this->queryToBuild->addParam('qt', null); + return $this; + } + + /** + * Can be used to remove all sortings from the query. + * + * @return QueryBuilder + */ + public function removeAllSortings(): QueryBuilder + { + $this->queryToBuild->clearSorts(); + return $this; + } + + /** + * Applies the passed sorting to the query. + * + * @param Sorting $sorting + * @return QueryBuilder + */ + public function useSorting(Sorting $sorting): QueryBuilder { - $this->queryToBuild->setQueryType($queryType); + if (strpos($sorting->getFieldName(), 'relevance') !== false) { + $this->removeAllSortings(); + return $this; + } + + $this->queryToBuild->addSort($sorting->getFieldName(), $sorting->getDirection()); return $this; } /** * Applies the passed sorting to the query. * - * @param string $sorting + * @param Sortings $sortings * @return QueryBuilder */ - public function useSorting($sorting): QueryBuilder + public function useSortings(Sortings $sortings): QueryBuilder { - $this->queryToBuild->setSorting($sorting); + foreach($sortings->getSortings() as $sorting) { + $this->useSorting($sorting); + } + return $this; } @@ -302,7 +345,7 @@ public function useSorting($sorting): QueryBuilder */ public function useResultsPerPage($resultsPerPage): QueryBuilder { - $this->queryToBuild->getPagination()->setResultsPerPage($resultsPerPage); + $this->queryToBuild->setRows($resultsPerPage); return $this; } @@ -312,7 +355,7 @@ public function useResultsPerPage($resultsPerPage): QueryBuilder */ public function usePage($page): QueryBuilder { - $this->queryToBuild->getPagination()->setPage($page); + $this->queryToBuild->setStart($page); return $this; } @@ -322,7 +365,18 @@ public function usePage($page): QueryBuilder */ public function useOperator(Operator $operator): QueryBuilder { - $this->queryToBuild->setOperator($operator); + $this->queryToBuild->setQueryDefaultOperator( $operator->getOperator()); + return $this; + } + + /** + * Remove the default query operator. + * + * @return QueryBuilder + */ + public function removeOperator(): QueryBuilder + { + $this->queryToBuild->setQueryDefaultOperator(null); return $this; } @@ -340,8 +394,7 @@ public function useSlopsFromTypoScript(): QueryBuilder */ public function useSlops(Slops $slops): QueryBuilder { - $this->queryToBuild->setSlops($slops); - return $this; + return $slops->build($this); } /** @@ -373,7 +426,27 @@ public function useBoostQueriesFromTypoScript(): QueryBuilder */ public function useBoostQueries($boostQueries): QueryBuilder { - $this->queryToBuild->setBoostQuery($boostQueries); + $boostQueryArray = []; + if(is_array($boostQueries)) { + foreach($boostQueries as $boostQuery) { + $boostQueryArray[] = ['key' => md5($boostQuery), 'query' => $boostQuery]; + } + } else { + $boostQueryArray[] = ['key' => md5($boostQueries), 'query' => $boostQueries]; + } + + $this->queryToBuild->getEDisMax()->setBoostQueries($boostQueryArray); + return $this; + } + + /** + * Removes all boost queries from the query. + * + * @return QueryBuilder + */ + public function removeAllBoostQueries(): QueryBuilder + { + $this->queryToBuild->getEDisMax()->clearBoostQueries(); return $this; } @@ -398,9 +471,20 @@ public function useBoostFunctionFromTypoScript(): QueryBuilder * @param string $boostFunction * @return QueryBuilder */ - public function useBoostFunction($boostFunction): QueryBuilder + public function useBoostFunction(string $boostFunction): QueryBuilder + { + $this->queryToBuild->getEDisMax()->setBoostFunctions($boostFunction); + return $this; + } + + /** + * Removes all previously configured boost functions. + * + * @return $this + */ + public function removeAllBoostFunctions() { - $this->queryToBuild->setBoostFunction($boostFunction); + $this->queryToBuild->getEDisMax()->setBoostFunctions(null); return $this; } @@ -422,12 +506,23 @@ public function useMinimumMatchFromTypoScript(): QueryBuilder /** * Uses the passed minimumMatch(mm) for the query. * - * @param string $boostFunction + * @param string $minimumMatch * @return QueryBuilder */ - public function useMinimumMatch($boostFunction): QueryBuilder + public function useMinimumMatch(string $minimumMatch): QueryBuilder { - $this->queryToBuild->setMinimumMatch($boostFunction); + $this->queryToBuild->getEDisMax()->setMinimumMatch($minimumMatch); + return $this; + } + + /** + * Remove any previous passed minimumMatch parameter. + * + * @return QueryBuilder + */ + public function removeMinimumMatch(): QueryBuilder + { + $this->queryToBuild->getEDisMax()->setMinimumMatch(null); return $this; } @@ -452,7 +547,7 @@ public function useTieParameterFromTypoScript(): QueryBuilder */ public function useTieParameter($tie): QueryBuilder { - $this->queryToBuild->setTieParameter($tie); + $this->queryToBuild->getEDisMax()->setTie($tie); return $this; } @@ -474,8 +569,7 @@ public function useQueryFieldsFromTypoScript(): QueryBuilder */ public function useQueryFields(QueryFields $queryFields): QueryBuilder { - $this->queryToBuild->setQueryFields($queryFields); - return $this; + return $queryFields->build($this); } /** @@ -497,8 +591,7 @@ public function useReturnFieldsFromTypoScript(): QueryBuilder */ public function useReturnFields(ReturnFields $returnFields): QueryBuilder { - $this->queryToBuild->setReturnFields($returnFields); - return $this; + return $returnFields->build($this); } /** @@ -548,7 +641,75 @@ public function useSiteHashFromAllowedSites($allowedSites): QueryBuilder */ public function useFilter($filterString, $filterName = ''): QueryBuilder { - $this->queryToBuild->getFilters()->add($filterString, $filterName); + $filterName = $filterName === '' ? $filterString : $filterName; + $this->queryToBuild->addFilterQuery(['key' => $filterName, 'query' => $filterString]); + return $this; + } + + /** + * Removes a filter by the fieldName. + * + * @param string $fieldName + * @return QueryBuilder + */ + public function removeFilterByFieldName($fieldName): QueryBuilder + { + return $this->removeFilterByFunction( + function($key, $query) use ($fieldName) { + $queryString = $query->getQuery(); + $storedFieldName = substr($queryString,0, strpos($queryString, ":")); + return $storedFieldName == $fieldName; + } + ); + } + + /** + * Removes a filter by the name of the filter (also known as key). + * + * @param string $name + * @return QueryBuilder + */ + public function removeFilterByName($name): QueryBuilder + { + return $this->removeFilterByFunction( + function($key, $query) use ($name) { + $key = $query->getKey(); + return $key == $name; + } + ); + } + + /** + * Removes a filter by the filter value. + * + * @param string $value + * @return QueryBuilder + */ + public function removeFilterByValue($value): QueryBuilder + { + return $this->removeFilterByFunction( + function($key, $query) use ($value) { + $query = $query->getQuery(); + return $query == $value; + } + ); + } + + /** + * @param \Closure $filterFunction + * @return QueryBuilder + */ + public function removeFilterByFunction($filterFunction) : QueryBuilder + { + $queries = $this->queryToBuild->getFilterQueries(); + foreach($queries as $key => $query) { + $canBeRemoved = $filterFunction($key, $query); + if($canBeRemoved) { + unset($queries[$key]); + } + } + + $this->queryToBuild->setFilterQueries($queries); return $this; } @@ -566,7 +727,8 @@ public function useUserAccessGroups(array $groups): QueryBuilder sort($groups, SORT_NUMERIC); $accessFilter = '{!typo3access}' . implode(',', $groups); - return $this->useFilter($accessFilter, 'accessFilter'); + $this->queryToBuild->removeFilterQuery('access'); + return $this->useFilter($accessFilter, 'access'); } /** @@ -579,16 +741,38 @@ public function useInitialQueryFromTypoScript(): QueryBuilder if ($this->typoScriptConfiguration->getSearchInitializeWithEmptyQuery() || $this->typoScriptConfiguration->getSearchQueryAllowEmptyQuery()) { // empty main query, but using a "return everything" // alternative query in q.alt - $this->queryToBuild->setAlternativeQuery('*:*'); + $this->useAlternativeQuery('*:*'); } if ($this->typoScriptConfiguration->getSearchInitializeWithQuery()) { - $this->queryToBuild->setAlternativeQuery($this->typoScriptConfiguration->getSearchInitializeWithQuery()); + $this->useAlternativeQuery($this->typoScriptConfiguration->getSearchInitializeWithQuery()); } return $this; } + /** + * Passes the alternative query to the Query + * @param string $query + * @return QueryBuilder + */ + public function useAlternativeQuery(string $query): QueryBuilder + { + $this->queryToBuild->getEDisMax()->setQueryAlternative($query); + return $this; + } + + /** + * Remove the alternative query from the Query. + * + * @return QueryBuilder + */ + public function removeAlternativeQuery(): QueryBuilder + { + $this->queryToBuild->getEDisMax()->setQueryAlternative(null); + return $this; + } + /** * Applies the configured facets from the typoscript configuration on the query. * @@ -607,8 +791,7 @@ public function useFacetingFromTypoScript(): QueryBuilder */ public function useFaceting(Faceting $faceting): QueryBuilder { - $this->queryToBuild->setFaceting($faceting); - return $this; + return $faceting->build($this); } /** @@ -627,8 +810,7 @@ public function useVariantsFromTypoScript(): QueryBuilder */ public function useFieldCollapsing(FieldCollapsing $fieldCollapsing): QueryBuilder { - $this->queryToBuild->setFieldCollapsing($fieldCollapsing); - return $this; + return $fieldCollapsing->build($this); } /** @@ -649,7 +831,24 @@ public function useGroupingFromTypoScript(): QueryBuilder */ public function useGrouping(Grouping $grouping): QueryBuilder { - $this->queryToBuild->setGrouping($grouping); + return $grouping->build($this); + } + + /** + * @param boolean $debugMode + * @return QueryBuilder + */ + public function useDebug($debugMode): QueryBuilder + { + if (!$debugMode) { + $this->queryToBuild->addParam('debugQuery', null); + $this->queryToBuild->addParam('echoParams', null); + return $this; + } + + $this->queryToBuild->addParam('debugQuery', 'true'); + $this->queryToBuild->addParam('echoParams', 'all'); + return $this; } @@ -669,8 +868,7 @@ public function useHighlightingFromTypoScript(): QueryBuilder */ public function useHighlighting(Highlighting $highlighting): QueryBuilder { - $this->queryToBuild->setHighlighting($highlighting); - return $this; + return $highlighting->build($this); } /** @@ -681,7 +879,7 @@ public function useHighlighting(Highlighting $highlighting): QueryBuilder public function useFiltersFromTypoScript(): QueryBuilder { $filters = Filters::fromTypoScriptConfiguration($this->typoScriptConfiguration); - $this->queryToBuild->setFilters($filters); + $this->queryToBuild->setFilterQueries($filters->getValues()); $this->useFilterArray($this->getAdditionalFilters()); @@ -717,8 +915,7 @@ public function useElevationFromTypoScript(): QueryBuilder */ public function useElevation(Elevation $elevation): QueryBuilder { - $this->queryToBuild->setElevation($elevation); - return $this; + return $elevation->build($this); } /** @@ -737,8 +934,7 @@ public function useSpellcheckingFromTypoScript(): QueryBuilder */ public function useSpellchecking(Spellchecking $spellchecking): QueryBuilder { - $this->queryToBuild->setSpellchecking($spellchecking); - return $this; + return $spellchecking->build($this); } /** @@ -782,8 +978,7 @@ public function usePhraseFieldsFromTypoScript(): QueryBuilder */ public function usePhraseFields(PhraseFields $phraseFields): QueryBuilder { - $this->queryToBuild->setPhraseFields($phraseFields); - return $this; + return $phraseFields->build($this); } /** @@ -804,8 +999,7 @@ public function useBigramPhraseFieldsFromTypoScript(): QueryBuilder */ public function useBigramPhraseFields(BigramPhraseFields $bigramPhraseFields): QueryBuilder { - $this->queryToBuild->setBigramPhraseFields($bigramPhraseFields); - return $this; + return $bigramPhraseFields->build($this); } /** @@ -826,8 +1020,7 @@ public function useTrigramPhraseFieldsFromTypoScript(): QueryBuilder */ public function useTrigramPhraseFields(TrigramPhraseFields $trigramPhraseFields): QueryBuilder { - $this->queryToBuild->setTrigramPhraseFields($trigramPhraseFields); - return $this; + return $trigramPhraseFields->build($this); } /** @@ -874,11 +1067,12 @@ public function getAdditionalFilters() : array /** * @param string $rawQuery - * @return Query|object + * @return SearchQuery */ - protected function getQueryInstance($rawQuery): Query + protected function getSearchQueryInstance($rawQuery): SearchQuery { - $query = GeneralUtility::makeInstance(Query::class, /** @scrutinizer ignore-type */ $rawQuery); + $query = GeneralUtility::makeInstance(SearchQuery::class); + $query->setQuery($rawQuery); return $query; } diff --git a/Classes/Domain/Search/Query/QueryParametersContainer.php b/Classes/Domain/Search/Query/QueryParametersContainer.php deleted file mode 100644 index 14e1f9828a..0000000000 --- a/Classes/Domain/Search/Query/QueryParametersContainer.php +++ /dev/null @@ -1,143 +0,0 @@ -set($parameterName, $value); - } else { - unset($this->queryParameters[$parameterName]); - } - } - - /** - * This method can be used to set a query parameter when the value is a int and not empty or unset it - * in any other case. Extracted to avoid duplicate code. - * - * @param string $parameterName - * @param int $value - */ - public function setWhenIntOrUnsetWhenNull(string $parameterName, int $value = null) - { - if (null === $value) { - unset($this->queryParameters[$parameterName]); - return; - } - $this->set($parameterName, $value); - } - - /** - * Adds any generic query parameter. - * - * @param string $parameterName Query parameter name - * @param mixed $parameterValue Parameter value - */ - public function set($parameterName, $parameterValue) - { - $this->queryParameters[$parameterName] = $parameterValue; - } - - /** - * Removes a queryParameter. - * - * @param mixed $parameterName - */ - public function remove($parameterName) - { - unset($this->queryParameters[$parameterName]); - } - - /** - * Removes multiple query parameters by name - * - * @param array $parameterNames - */ - public function removeMany(array $parameterNames) - { - foreach ($parameterNames as $parameterName) { - $this->remove($parameterName); - } - } - - /** - * @param string $prefix - */ - public function removeByPrefix($prefix) - { - foreach ($this->queryParameters as $parameterName => $parameterValue) { - if (GeneralUtility::isFirstPartOfStr($parameterName, $prefix)) { - unset($this->queryParameters[$parameterName]); - } - } - } - - /** - * Returns a queryParameter - * @param string $parameterName - * @return mixed - */ - public function get($parameterName) - { - return $this->queryParameters[$parameterName]; - } - - /** - * @param array $toMerge - */ - public function merge(array $toMerge) - { - $this->queryParameters = array_merge($this->queryParameters, $toMerge); - } - - /** - * @return array - */ - public function toArray() - { - return $this->queryParameters; - } -} \ No newline at end of file diff --git a/Classes/Domain/Search/Query/SearchQuery.php b/Classes/Domain/Search/Query/SearchQuery.php new file mode 100644 index 0000000000..69e18acfc0 --- /dev/null +++ b/Classes/Domain/Search/Query/SearchQuery.php @@ -0,0 +1,27 @@ + + * All rights reserved + * + * This script is part of the TYPO3 project. The TYPO3 project is + * free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * The GNU General Public License can be found at + * http://www.gnu.org/copyleft/gpl.html. + * + * This script is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This copyright notice MUST APPEAR in all copies of the script! + ***************************************************************/ + +class SearchQuery extends Query {} \ No newline at end of file diff --git a/Classes/Domain/Search/Query/SuggestQuery.php b/Classes/Domain/Search/Query/SuggestQuery.php index e57bcd1097..8021d38239 100644 --- a/Classes/Domain/Search/Query/SuggestQuery.php +++ b/Classes/Domain/Search/Query/SuggestQuery.php @@ -24,6 +24,7 @@ * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ +use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\ReturnFields; use ApacheSolrForTypo3\Solr\Domain\Search\Query\Helper\EscapeService; use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; use ApacheSolrForTypo3\Solr\Util; @@ -53,13 +54,12 @@ class SuggestQuery extends Query */ public function __construct($keywords, $solrConfiguration = null) { + parent::__construct(); $keywords = (string)$keywords; - if ($solrConfiguration == null) { - $solrConfiguration = Util::getSolrConfiguration(); - } - parent::__construct(''); + $solrConfiguration = $solrConfiguration ?? Util::getSolrConfiguration(); + $this->setQuery($keywords); $this->configuration = $solrConfiguration->getObjectByPathOrDefault('plugin.tx_solr.suggest.', []); if (!empty($this->configuration['treatMultipleTermsAsSingleTerm'])) { @@ -70,31 +70,16 @@ public function __construct($keywords, $solrConfiguration = null) $fullKeywords = trim($matches[2]); $partialKeyword = trim($matches[3]); - $this->getQueryStringContainer()->setKeywords($fullKeywords); + $this->setQuery($fullKeywords); $this->prefix = EscapeService::escape($partialKeyword); } - $this->setAlternativeQuery('*:*'); - } - - /** - * Returns the query parameters that should be used. - * - * @return array - */ - public function getQueryParameters() - { - $suggestParameters = [ - 'facet' => 'on', - 'facet.prefix' => $this->prefix, - 'facet.field' => $this->configuration['suggestField'], - 'facet.limit' => $this->configuration['numberOfSuggestions'], - 'facet.mincount' => '1', - 'fl' => $this->configuration['suggestField'] - ]; - - $this->filters->build($this); - $this->queryParametersContainer->merge($suggestParameters); - return $this->queryParametersContainer->toArray(); + $this->getEDisMax()->setQueryAlternative('*:*'); + $this->setFields(ReturnFields::fromString($this->configuration['suggestField'])->getValues()); + $this->addParam('facet', 'on'); + $this->addParam('facet.prefix', $this->prefix); + $this->addParam('facet.field', $this->configuration['suggestField']); + $this->addParam('facet.limit', $this->configuration['numberOfSuggestions']); + $this->addParam('facet.mincount', 1); } } diff --git a/Classes/Domain/Search/ResultSet/Facets/OptionBased/Hierarchy/HierarchyFacetParser.php b/Classes/Domain/Search/ResultSet/Facets/OptionBased/Hierarchy/HierarchyFacetParser.php index b6f66a9079..e37a881905 100644 --- a/Classes/Domain/Search/ResultSet/Facets/OptionBased/Hierarchy/HierarchyFacetParser.php +++ b/Classes/Domain/Search/ResultSet/Facets/OptionBased/Hierarchy/HierarchyFacetParser.php @@ -14,8 +14,6 @@ * The TYPO3 project - inspiring people to share! */ -use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Faceting; -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Facets\AbstractFacetParser; use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\SearchResultSet; @@ -56,7 +54,7 @@ public function parse(SearchResultSet $resultSet, $facetName, array $facetConfig $nodesToCreate = $this->getMergedFacetValueFromSearchRequestAndSolrResponse($optionsFromSolrResponse, $optionsFromRequest); - if ($this->facetOptionsMustBeResorted($facetConfiguration, $resultSet) === true) { + if ($this->facetOptionsMustBeResorted($facetConfiguration)) { $nodesToCreate = $this->sortFacetOptionsInNaturalOrder($nodesToCreate); } @@ -105,23 +103,15 @@ protected function sortFacetOptionsInNaturalOrder(array $flatOptionsListForHiera * see: https://lucene.apache.org/solr/guide/6_6/faceting.html#Faceting-Thefacet.sortParameter * see: https://wiki.apache.org/solr/SimpleFacetParameters#facet.sort : "This parameter can be specified on a per field basis." * - * If plugin.tx_solr.search.faceting.facets.[facetName].sortBy is not set, then trying to get global parameter from ResultSet. - * * @param array $facetConfiguration - * @param SearchResultSet $resultSet * @return bool */ - protected function facetOptionsMustBeResorted(array $facetConfiguration, SearchResultSet $resultSet) + protected function facetOptionsMustBeResorted(array $facetConfiguration) { if (isset($facetConfiguration['sortBy']) && $facetConfiguration['sortBy'] === 'index') { return true; } - if (!isset($facetConfiguration['sortBy']) - && $resultSet->getUsedQuery()->getFaceting()->getSorting() === 'index') { - return true; - } - return false; } diff --git a/Classes/Domain/Search/ResultSet/SearchResultSet.php b/Classes/Domain/Search/ResultSet/SearchResultSet.php index 830dbb3718..dd78ab491d 100644 --- a/Classes/Domain/Search/ResultSet/SearchResultSet.php +++ b/Classes/Domain/Search/ResultSet/SearchResultSet.php @@ -269,7 +269,7 @@ public function getUsedAdditionalFilters() } /** - * @param \ApacheSolrForTypo3\Solr\Domain\Search\Query\Query $usedQuery + * @param Query $usedQuery */ public function setUsedQuery($usedQuery) { @@ -279,7 +279,7 @@ public function setUsedQuery($usedQuery) /** * Retrieves the query object that has been used to build this result set. * - * @return \ApacheSolrForTypo3\Solr\Domain\Search\Query\Query + * @return Query */ public function getUsedQuery() { @@ -304,14 +304,6 @@ public function getUsedPage() return $this->usedPage; } - /** - * @return int - */ - public function getResultsPerPage() - { - return $this->usedQuery->getPagination()->getResultsPerPage(); - } - /** * @param \ApacheSolrForTypo3\Solr\Domain\Search\SearchRequest $usedSearchRequest */ diff --git a/Classes/Domain/Search/ResultSet/SearchResultSetService.php b/Classes/Domain/Search/ResultSet/SearchResultSetService.php index d492962e99..a8d376f261 100644 --- a/Classes/Domain/Search/ResultSet/SearchResultSetService.php +++ b/Classes/Domain/Search/ResultSet/SearchResultSetService.php @@ -26,8 +26,9 @@ ***************************************************************/ use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\QueryFields; -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\SearchQuery; use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Result\Parser\ResultParserRegistry; use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Result\SearchResult; use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Result\SearchResultCollection; @@ -411,9 +412,8 @@ protected function modifyQuery(Query $query, SearchRequest $searchRequest, Searc */ public function getDocumentById($documentId) { - /* @var $query Query */ - $query = GeneralUtility::makeInstance(Query::class, /** @scrutinizer ignore-type */ $documentId); - $query->setQueryFields(QueryFields::fromString('id')); + /* @var $query SearchQuery */ + $query = $this->queryBuilder->newSearchQuery($documentId)->useQueryFields(QueryFields::fromString('id'))->getQuery(); $response = $this->search->search($query, 0, 1); $parsedData = $response->getParsedData(); $resultDocument = isset($parsedData->response->docs[0]) ? $parsedData->response->docs[0] : null; diff --git a/Classes/Domain/Search/Statistics/StatisticsWriterProcessor.php b/Classes/Domain/Search/Statistics/StatisticsWriterProcessor.php index ed1057d135..6be59bdf34 100644 --- a/Classes/Domain/Search/Statistics/StatisticsWriterProcessor.php +++ b/Classes/Domain/Search/Statistics/StatisticsWriterProcessor.php @@ -106,7 +106,7 @@ public function process(SearchResultSet $resultSet) { */ protected function getProcessedKeywords(Query $query, $lowerCaseQuery = false) { - $keywords = $query->getQueryStringContainer()->getKeywords(); + $keywords = $query->getQuery(); $keywords = $this->sanitizeString($keywords); if ($lowerCaseQuery) { $keywords = mb_strtolower($keywords); diff --git a/Classes/Domain/Search/Suggest/SuggestService.php b/Classes/Domain/Search/Suggest/SuggestService.php index d50759831f..31543f0ec1 100644 --- a/Classes/Domain/Search/Suggest/SuggestService.php +++ b/Classes/Domain/Search/Suggest/SuggestService.php @@ -186,7 +186,7 @@ protected function getSolrSuggestions(SuggestQuery $suggestQuery) : array */ protected function getSuggestionArray(SuggestQuery $suggestQuery, $solrSuggestions, $maxSuggestions) : array { - $queryString = $suggestQuery->getQueryStringContainer()->getKeywords(); + $queryString = $suggestQuery->getQuery(); $suggestionCount = 0; $suggestions = []; foreach ($solrSuggestions as $string => $count) { diff --git a/Classes/Query/Modifier/Elevation.php b/Classes/Query/Modifier/Elevation.php index 0842b5f1c7..d3898bc118 100644 --- a/Classes/Query/Modifier/Elevation.php +++ b/Classes/Query/Modifier/Elevation.php @@ -24,8 +24,8 @@ * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; use TYPO3\CMS\Core\Utility\GeneralUtility; /** diff --git a/Classes/Query/Modifier/Faceting.php b/Classes/Query/Modifier/Faceting.php index 195bff9e3f..748fae449f 100644 --- a/Classes/Query/Modifier/Faceting.php +++ b/Classes/Query/Modifier/Faceting.php @@ -24,6 +24,8 @@ * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ +use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Faceting as FacetingBuilder; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Facets\FacetRegistry; use ApacheSolrForTypo3\Solr\Domain\Search\SearchRequest; @@ -78,16 +80,16 @@ public function setSearchRequest(SearchRequest $searchRequest) */ public function modifyQuery(Query $query) { - $query->getFaceting()->setIsEnabled(true); $typoScriptConfiguration = $this->searchRequest->getContextTypoScriptConfiguration(); - $allFacets = $typoScriptConfiguration->getSearchFacetingFacets(); + $faceting = FacetingBuilder::fromTypoScriptConfiguration($typoScriptConfiguration); + $allFacets = $typoScriptConfiguration->getSearchFacetingFacets(); $facetParameters = $this->buildFacetingParameters($allFacets, $typoScriptConfiguration); foreach ($facetParameters as $facetParameter => $value) { if(strtolower($facetParameter) === 'facet.field') { - $query->getFaceting()->setFields($value); + $faceting->setFields($value); } else { - $query->getFaceting()->addAdditionalParameter($facetParameter, $value); + $faceting->addAdditionalParameter($facetParameter, $value); } } @@ -99,10 +101,8 @@ public function modifyQuery(Query $query) $keepAllFacetsOnSelection = $typoScriptConfiguration->getSearchFacetingKeepAllFacetsOnSelection(); $facetFilters = $this->addFacetQueryFilters($searchArguments, $allFacets, $keepAllFacetsOnSelection); - foreach ($facetFilters as $filter) { - $query->getFilters()->add($filter); - } - + $queryBuilder = new QueryBuilder($typoScriptConfiguration); + $queryBuilder->startFrom($query)->useFaceting($faceting)->useFilterArray($facetFilters); return $query; } diff --git a/Classes/Query/Modifier/Statistics.php b/Classes/Query/Modifier/Statistics.php index 07f06a53e2..697348ff10 100644 --- a/Classes/Query/Modifier/Statistics.php +++ b/Classes/Query/Modifier/Statistics.php @@ -23,7 +23,10 @@ * * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ + use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; +use TYPO3\CMS\Core\Utility\GeneralUtility; /** * Enables tracking of detailed statistics @@ -32,6 +35,19 @@ */ class Statistics implements Modifier { + /** + * @var QueryBuilder + */ + protected $queryBuilder; + + /** + * Elevation constructor. + * @param QueryBuilder|null $builder + */ + public function __construct(QueryBuilder $builder = null) + { + $this->queryBuilder = $builder ?? GeneralUtility::makeInstance(QueryBuilder::class); + } /** * Enables the query's debug mode to get more detailed information. @@ -41,8 +57,6 @@ class Statistics implements Modifier */ public function modifyQuery(Query $query) { - $query->setDebugMode(true); - - return $query; + return $this->queryBuilder->startFrom($query)->useDebug(true)->getQuery(); } } diff --git a/Classes/Search.php b/Classes/Search.php index 86d1adcffc..88b9a8e56a 100644 --- a/Classes/Search.php +++ b/Classes/Search.php @@ -24,13 +24,11 @@ * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Result\Parser\DocumentEscapeService; -use ApacheSolrForTypo3\Solr\Search\FacetsModifier; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager; use ApacheSolrForTypo3\Solr\System\Solr\SolrCommunicationException; use ApacheSolrForTypo3\Solr\System\Solr\SolrConnection; -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query as NewQuery; use TYPO3\CMS\Core\Utility\GeneralUtility; /** @@ -51,7 +49,7 @@ class Search /** * The search query * - * @var NewQuery + * @var Query */ protected $query = null; @@ -131,7 +129,7 @@ public function setSolrConnection(SolrConnection $solrConnection) * @return \Apache_Solr_Response Solr response * @throws \Exception */ - public function search(NewQuery $query, $offset = 0, $limit = 10) + public function search(Query $query, $offset = 0, $limit = 10) { $this->query = $query; @@ -140,22 +138,17 @@ public function search(NewQuery $query, $offset = 0, $limit = 10) } try { - $response = $this->solr->getReadService()->search( - (string)$query->getQueryStringContainer()->getQueryString(), - $offset, - $limit, - $query->getQueryParameters() - ); + $param = $query->getRequestBuilder()->build($query)->getParams(); + $response = $this->solr->getReadService()->search((string)$query->getQuery(), $offset, $limit, $param); if ($this->configuration->getLoggingQueryQueryString()) { $this->logger->log( SolrLogManager::INFO, 'Querying Solr, getting result', [ - 'query string' => $query->getQueryStringContainer()->getQueryString(), - 'query parameters' => $query->getQueryParameters(), - 'response' => json_decode($response->getRawResponse(), - true) + 'query string' => $query->getQuery(), + 'query parameters' => $param, + 'response' => json_decode($response->getRawResponse(), true) ] ); } @@ -216,7 +209,7 @@ public function ping($useCache = true) /** * Gets the query object. * - * @return NewQuery Query + * @return Query */ public function getQuery() { diff --git a/Classes/Search/AccessComponent.php b/Classes/Search/AccessComponent.php index af9fa19832..a82daf3e4c 100644 --- a/Classes/Search/AccessComponent.php +++ b/Classes/Search/AccessComponent.php @@ -24,8 +24,8 @@ * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; use TYPO3\CMS\Core\Utility\GeneralUtility; /** diff --git a/Classes/Search/AnalysisComponent.php b/Classes/Search/AnalysisComponent.php index 536dd44ab1..bc389c6125 100644 --- a/Classes/Search/AnalysisComponent.php +++ b/Classes/Search/AnalysisComponent.php @@ -24,7 +24,9 @@ * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ +use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; +use TYPO3\CMS\Core\Utility\GeneralUtility; /** * Analysis search component @@ -42,14 +44,28 @@ class AnalysisComponent extends AbstractComponent implements QueryAware protected $query; /** - * Initializes the search component. - * + * QueryBuilder * + * @var QueryBuilder|object + */ + protected $queryBuilder; + + /** + * AccessComponent constructor. + * @param QueryBuilder|null + */ + public function __construct(QueryBuilder $queryBuilder = null) + { + $this->queryBuilder = $queryBuilder ?? GeneralUtility::makeInstance(QueryBuilder::class); + } + + /** + * Initializes the search component. */ public function initializeSearchComponent() { if ($this->searchConfiguration['results.']['showDocumentScoreAnalysis']) { - $this->query->setDebugMode(); + $this->queryBuilder->startFrom($this->query)->useDebug(true); } } diff --git a/Classes/Search/DebugComponent.php b/Classes/Search/DebugComponent.php index 0fb2c4440d..d8bec0a9eb 100644 --- a/Classes/Search/DebugComponent.php +++ b/Classes/Search/DebugComponent.php @@ -24,9 +24,11 @@ * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ +use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; use ApacheSolrForTypo3\Solr\Domain\Search\SearchRequest; use ApacheSolrForTypo3\Solr\Domain\Search\SearchRequestAware; +use TYPO3\CMS\Core\Utility\GeneralUtility; /** * Debug search component @@ -49,6 +51,22 @@ class DebugComponent extends AbstractComponent implements QueryAware, SearchRequ */ protected $seachRequest; + /** + * QueryBuilder + * + * @var QueryBuilder|object + */ + protected $queryBuilder; + + /** + * AccessComponent constructor. + * @param QueryBuilder|null + */ + public function __construct(QueryBuilder $queryBuilder = null) + { + $this->queryBuilder = $queryBuilder ?? GeneralUtility::makeInstance(QueryBuilder::class); + } + /** * Provides a component that is aware of the current SearchRequest * @@ -68,7 +86,7 @@ public function setSearchRequest(SearchRequest $searchRequest) public function initializeSearchComponent() { if ($this->seachRequest->getContextTypoScriptConfiguration()->getEnabledDebugMode()) { - $this->query->setDebugMode(); + $this->queryBuilder->startFrom($this->query)->useDebug(true); } } diff --git a/Classes/Search/HighlightingComponent.php b/Classes/Search/HighlightingComponent.php index dee85c807b..74bfd9cbcd 100644 --- a/Classes/Search/HighlightingComponent.php +++ b/Classes/Search/HighlightingComponent.php @@ -24,10 +24,8 @@ * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Highlighting; -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; -use ApacheSolrForTypo3\Solr\Util; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; use TYPO3\CMS\Core\Utility\GeneralUtility; /** diff --git a/Classes/Search/QueryAware.php b/Classes/Search/QueryAware.php index 651476e3ef..7e864fb897 100644 --- a/Classes/Search/QueryAware.php +++ b/Classes/Search/QueryAware.php @@ -26,8 +26,8 @@ * * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; /** * Query awareness interface for extension components. diff --git a/Classes/Search/RelevanceComponent.php b/Classes/Search/RelevanceComponent.php index e6a9ea2a62..ade659b7e9 100644 --- a/Classes/Search/RelevanceComponent.php +++ b/Classes/Search/RelevanceComponent.php @@ -24,8 +24,8 @@ * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; use TYPO3\CMS\Core\Utility\GeneralUtility; /** diff --git a/Classes/Search/SortingComponent.php b/Classes/Search/SortingComponent.php index 236e5fc8ad..80ada544de 100644 --- a/Classes/Search/SortingComponent.php +++ b/Classes/Search/SortingComponent.php @@ -24,8 +24,10 @@ * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Sortings; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Sorting\SortingHelper; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; use ApacheSolrForTypo3\Solr\Domain\Search\SearchRequest; use ApacheSolrForTypo3\Solr\Domain\Search\SearchRequestAware; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -52,6 +54,22 @@ class SortingComponent extends AbstractComponent implements QueryAware, SearchRe */ protected $searchRequest; + /** + * QueryBuilder + * + * @var QueryBuilder|object + */ + protected $queryBuilder; + + /** + * AccessComponent constructor. + * @param QueryBuilder|null + */ + public function __construct(QueryBuilder $queryBuilder = null) + { + $this->queryBuilder = $queryBuilder ?? GeneralUtility::makeInstance(QueryBuilder::class); + } + /** * Initializes the search component. * @@ -59,17 +77,31 @@ class SortingComponent extends AbstractComponent implements QueryAware, SearchRe */ public function initializeSearchComponent() { + $this->queryBuilder->startFrom($this->query); + if (!empty($this->searchConfiguration['query.']['sortBy'])) { - $this->query->setSorting($this->searchConfiguration['query.']['sortBy']); + $this->queryBuilder->useSortings(Sortings::fromString($this->searchConfiguration['query.']['sortBy'])); + $this->query = $this->queryBuilder->getQuery(); } - $arguments = $this->searchRequest->getArguments(); + $isSortingEnabled = !empty($this->searchConfiguration['sorting']) && ((int)$this->searchConfiguration['sorting']) === 1; + if(!$isSortingEnabled) { + return; + } - if (!empty($this->searchConfiguration['sorting']) && $this->hasValidSorting($arguments)) { - $sortHelper = GeneralUtility::makeInstance(SortingHelper::class, /** @scrutinizer ignore-type */ $this->searchConfiguration['sorting.']['options.']); - $sortField = $sortHelper->getSortFieldFromUrlParameter($arguments['sort']); - $this->query->setSorting($sortField); + $arguments = $this->searchRequest->getArguments(); + $isSortingPassedAsArgument = !empty($arguments['sort']) && preg_match('/^([a-z0-9_]+ (asc|desc)[, ]*)*([a-z0-9_]+ (asc|desc))+$/i', $arguments['sort']); + if (!$isSortingPassedAsArgument) { + return; } + + // a passed sorting has allways priority an overwrites the configured initial sorting + $this->query->clearSorts(); + /** @var $sortHelper SortingHelper */ + $sortHelper = GeneralUtility::makeInstance(SortingHelper::class, $this->searchConfiguration['sorting.']['options.']); + $sortFields = $sortHelper->getSortFieldFromUrlParameter($arguments['sort']); + $this->queryBuilder->useSortings(Sortings::fromString($sortFields)); + $this->query = $this->queryBuilder->getQuery(); } /** diff --git a/Classes/Search/SpellcheckingComponent.php b/Classes/Search/SpellcheckingComponent.php index 376fa081a8..41467333fe 100644 --- a/Classes/Search/SpellcheckingComponent.php +++ b/Classes/Search/SpellcheckingComponent.php @@ -23,8 +23,9 @@ * * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; + use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; use TYPO3\CMS\Core\Utility\GeneralUtility; /** diff --git a/Classes/System/Configuration/TypoScriptConfiguration.php b/Classes/System/Configuration/TypoScriptConfiguration.php index bce59e9bb3..0f3d7d0c70 100644 --- a/Classes/System/Configuration/TypoScriptConfiguration.php +++ b/Classes/System/Configuration/TypoScriptConfiguration.php @@ -1624,7 +1624,7 @@ public function getSearchResultsSiteHighlighting($defaultIfEmpty = true) * plugin.tx_solr.search.results.resultsHighlighting * * @param boolean $defaultIfEmpty - * @return string + * @return boolean */ public function getSearchResultsHighlighting($defaultIfEmpty = false) { diff --git a/Classes/System/Solr/Service/SolrWriteService.php b/Classes/System/Solr/Service/SolrWriteService.php index 068e462c42..02d5a2105b 100644 --- a/Classes/System/Solr/Service/SolrWriteService.php +++ b/Classes/System/Solr/Service/SolrWriteService.php @@ -46,20 +46,17 @@ public function extractByQuery(ExtractingQuery $query) ]; try { - $response = $this->requestServlet( - self::EXTRACT_SERVLET, - $query->getQueryParameters(), - 'POST', - $headers, - $query->getRawPostFileData() - ); + $param = $query->getRequestBuilder()->build($query)->getParams(); + $response = $this->requestServlet(self::EXTRACT_SERVLET, $param, 'POST', $headers, $query->getRawPostFileData()); + + return [$response->extracted, (array)$response->extracted_metadata]; } catch (\Exception $e) { $this->logger->log( SolrLogManager::ERROR, 'Extracting text and meta data through Solr Cell over HTTP POST', [ 'query' => (array)$query, - 'parameters' => $query->getQueryParameters(), + 'parameters' => $param, 'file' => $query->getFile(), 'headers' => $headers, 'query url' => self::EXTRACT_SERVLET, @@ -68,10 +65,7 @@ public function extractByQuery(ExtractingQuery $query) ); } - return [ - $response->extracted, - (array)$response->extracted_metadata - ]; + return []; } /** diff --git a/Classes/ViewHelpers/Widget/Controller/ResultPaginateController.php b/Classes/ViewHelpers/Widget/Controller/ResultPaginateController.php index 759959d797..086e80ceda 100644 --- a/Classes/ViewHelpers/Widget/Controller/ResultPaginateController.php +++ b/Classes/ViewHelpers/Widget/Controller/ResultPaginateController.php @@ -52,7 +52,7 @@ public function initializeAction() */ protected function getItemsPerPage() { - $perPage = (int)$this->resultSet->getUsedSearch()->getQuery()->getPagination()->getResultsPerPage(); + $perPage = (int)$this->resultSet->getUsedSearch()->getQuery()->getRows(); return $perPage > 0 ? $perPage : 10; } diff --git a/Resources/Private/Partials/Result/Document.html b/Resources/Private/Partials/Result/Document.html index b78906c9d7..9b8d4f8398 100644 --- a/Resources/Private/Partials/Result/Document.html +++ b/Resources/Private/Partials/Result/Document.html @@ -15,7 +15,7 @@
diff --git a/Resources/Private/Php/ComposerLibraries/composer.json b/Resources/Private/Php/ComposerLibraries/composer.json new file mode 100644 index 0000000000..8aada62a87 --- /dev/null +++ b/Resources/Private/Php/ComposerLibraries/composer.json @@ -0,0 +1,12 @@ +{ + "config": { + "platform": { + "php": "7.0" + }, + "classmap-authoritative": true, + "prepend-autoloader": false + }, + "require": { + "solarium/solarium": "4.1.0-rc.1" + } +} \ No newline at end of file diff --git a/Resources/Private/Php/SolrPhpClient/COPYING b/Resources/Private/Php/SolrPhpClient/COPYING deleted file mode 100644 index 5e588e7e8a..0000000000 --- a/Resources/Private/Php/SolrPhpClient/COPYING +++ /dev/null @@ -1,26 +0,0 @@ -Copyright (c) 2007-2009, Conduit Internet Technologies, Inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Conduit Internet Technologies, Inc. nor the names of - its contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. diff --git a/Resources/Private/Php/SolrPhpClient/last_synched_revision b/Resources/Private/Php/SolrPhpClient/last_synched_revision deleted file mode 100644 index 1479e19b5f..0000000000 --- a/Resources/Private/Php/SolrPhpClient/last_synched_revision +++ /dev/null @@ -1 +0,0 @@ -65 diff --git a/Resources/Private/Templates/Search/Results.html b/Resources/Private/Templates/Search/Results.html index 1a13c3d9c8..2af4bdd473 100644 --- a/Resources/Private/Templates/Search/Results.html +++ b/Resources/Private/Templates/Search/Results.html @@ -45,9 +45,9 @@ - + - Searched for "%s" + Searched for "%s" diff --git a/Tests/Integration/Controller/AbstractFrontendControllerTest.php b/Tests/Integration/Controller/AbstractFrontendControllerTest.php new file mode 100644 index 0000000000..1160934da1 --- /dev/null +++ b/Tests/Integration/Controller/AbstractFrontendControllerTest.php @@ -0,0 +1,66 @@ +getConfiguredTSFE([], $importPageId); + $GLOBALS['TSFE'] = $fakeTSFE; + $fakeTSFE->newCObj(); + $fakeTSFE->preparePageContentGeneration(); + PageGenerator::renderContent(); + /** @var $pageIndexer \ApacheSolrForTypo3\Solr\Typo3PageIndexer */ + $pageIndexer = GeneralUtility::makeInstance(Typo3PageIndexer::class, $fakeTSFE); + $pageIndexer->indexPage(); + } + + /** @var $beUser \TYPO3\CMS\Core\Authentication\BackendUserAuthentication */ + $beUser = GeneralUtility::makeInstance(BackendUserAuthentication::class); + $GLOBALS['BE_USER'] = $beUser; + $this->waitToBeVisibleInSolr(); + } + + /** + * @param string $controllerName + * @param string $actionName + * @param string $plugin + * @return Request + */ + protected function getPreparedRequest($controllerName = 'Search', $actionName = 'results', $plugin = 'pi_result') + { + /** @var Request $request */ + $request = $this->objectManager->get(Request::class); + $request->setControllerName($controllerName); + $request->setControllerActionName($actionName); + $request->setControllerVendorName('ApacheSolrForTypo3'); + $request->setPluginName($plugin); + $request->setFormat('html'); + $request->setControllerExtensionName('Solr'); + + return $request; + } + + + /** + * @return Response + */ + protected function getPreparedResponse() + { + /** @var $response Response */ + $response = $this->objectManager->get(Response::class); + + return $response; + } +} \ No newline at end of file diff --git a/Tests/Integration/Controller/Fixtures/can_render_suggest_controller.xml b/Tests/Integration/Controller/Fixtures/can_render_suggest_controller.xml new file mode 100644 index 0000000000..7522b657e8 --- /dev/null +++ b/Tests/Integration/Controller/Fixtures/can_render_suggest_controller.xml @@ -0,0 +1,482 @@ + + + + + 4711 + tx_solr + servers + a:1:{s:3:"1|0";a:9:{s:13:"connectionKey";s:3:"1|0";s:13:"rootPageTitle";s:15:"Congratulations";s:11:"rootPageUid";s:1:"1";s:10:"solrScheme";s:4:"http";s:8:"solrHost";s:9:"localhost";s:8:"solrPort";s:4:"8999";s:8:"solrPath";s:14:"/solr/core_en/";s:8:"language";i:0;s:5:"label";s:74:"Congratulations (pid: 1, language: default) - localhost:8999/solr/core_en/";}} + + + + 1 + 1 + 1 + 3 + + + + # very simple rendering + page.10 = CONTENT + page.10 { + table = tt_content + select.orderBy = sorting + select.where = colPos=0 + renderObj = COA + renderObj { + 10 = TEXT + 10.field = bodytext + } + } + + page.10.wrap = | + + plugin.tx_solr { + + enabled = 1 + + enableDebugMode = 0 + + general { + dateFormat.date = d.m.Y H:i + baseWrap { + value =
|
+ } + } + + solr { + scheme = http + host = localhost + port = 8999 + path = /solr/core_en/ + } + + index { + additionalFields { + + } + + // assigns processing instructions to Solr fields during indexing, Solr field = processing instruction + fieldProcessingInstructions { + changed = timestampToIsoDate + created = timestampToIsoDate + endtime = timestampToUtcIsoDate + rootline = pageUidToHierarchy + pageHierarchy_stringM = pathToHierarchy + category_stringM = categoryUidToHierarchy + } + + queue { + + // mapping tableName.fields.SolrFieldName => TableFieldName (+ cObj processing) + + pages = 1 + pages { + initialization = ApacheSolrForTypo3\Solr\IndexQueue\Initializer\Page + + // allowed page types (doktype) when indexing records from table "pages" + allowedPageTypes = 1,7,4 + + indexingPriority = 0 + + indexer = ApacheSolrForTypo3\Solr\IndexQueue\PageIndexer + indexer { + // add options for the indexer here + } + + // Only index standard pages and mount points that are not overlayed. + additionalWhereClause = (doktype = 1 OR doktype=4 OR (doktype=7 AND mount_pid_ol=0)) AND no_search = 0 + + //exclude some html parts inside TYPO3SEARCH markers by classname (comma list) + excludeContentByClass = typo3-search-exclude + + fields { + sortSubTitle_stringS = subtitle + + category_stringM = SOLR_RELATION + category_stringM { + localField = categories + foreignLabelField = uid + multiValue = 1 + } + } + } + + } + } + + search { + // fields that are allowed to contain html and should be skipped during escaping after retrieval from Solr + // by default all fields expect url get escaped, you might need to add other url fields here as well because of & + // characters in the url. + trustedFields = url + + targetPage = {$plugin.tx_solr.search.targetPage} + + initializeWithEmptyQuery = 0 + showResultsOfInitialEmptyQuery = 0 + + initializeWithQuery = + showResultsOfInitialQuery = 0 + + keepExistingParametersForNewSearches = 1 + + query { + allowEmptyQuery = 1 + + allowedSites = __solr_current_site + + // qf parameter http://wiki.apache.org/solr/DisMaxQParserPlugin#qf_.28Query_Fields.29 + queryFields = content^40.0, title^5.0, keywords^2.0, tagsH1^5.0, tagsH2H3^3.0, tagsH4H5H6^2.0, tagsInline^1.0, description^4.0, abstract^1.0, subtitle^1.0, navtitle^1.0, author^1.0 + + // fl parameter http://wiki.apache.org/solr/CommonQueryParameters#fl + returnFields = *, score + + // see http://wiki.apache.org/solr/DisMaxRequestHandler#mm_.28Minimum_.27Should.27_Match.29 + minimumMatch = + + // see http://wiki.apache.org/solr/DisMaxRequestHandler#bf_.28Boost_Functions.29 + boostFunction = + + // see http://wiki.apache.org/solr/DisMaxQParserPlugin#bq_.28Boost_Query.29 + boostQuery = + + filter { + + } + + sortBy = + } + + results { + resultsHighlighting = 1 + resultsHighlighting { + // can be used to increase the highlighting performance requires the field is termVectors=on, + // termPositions=on and termOffsets=on which is set for content. NOTE: fragmentSize needs to be larger + // then 18 + useFastVectorHighlighter = 0 + highlightFields = content + fragmentSize = 20 + fragmentSeparator = [...] + + wrap = | + } + siteHighlighting = 0 + + resultsPerPage = 5 + resultsPerPageSwitchOptions = 5, 10, 25, 50 + + pagebrowser { + enabled = 1 + + pagesBefore = 3 + pagesAfter = 3 + + enableMorePages = 1 + enableLessPages = 1 + } + + showDocumentScoreAnalysis = 1 + } + + spellchecking = 1 + spellchecking { + wrap = |
###LLL:didYouMean### |
| + searchUsingSpellCheckerSuggestion = 0 + numberOfSuggestionsToTry = 0 + } + + lastSearches = 1 + lastSearches { + limit = 10 + // tracking mode "user" or "global" + mode = user + } + + frequentSearches = 1 + frequentSearches { + useLowercaseKeywords = 0 + + minSize = 15 + maxSize = 40 + limit = 20 + + select { + SELECT = keywords as search_term, count(*) as hits + FROM = tx_solr_statistics + ADD_WHERE = + GROUP_BY = keywords + ORDER_BY = hits DESC, search_term ASC + checkRootPageId = 0 + checkLanguage = 0 + } + + // cache lifetime in seconds (default is 86400s = 24h) + cacheLifetime = 0 + } + + sorting = 1 + sorting { + defaultOrder = asc + + options { + relevance { + field = relevance + label = Relevance + } + + title { + field = sortTitle + label = Title + } + + type { + field = type + label = Type + } + + author { + field = sortAuthor + label = Author + } + + created { + field = created + label = Creation Date + } + } + } + + faceting = 1 + faceting { + minimumCount = 1 + sortBy = count + limit = 10 + singleFacetMode = 0 + showEmptyFacets = 0 + keepAllFacetsOnSelection = 0 + + facetLinkUrlParameters = &foo=bar + + facets { + type { + label = Content Type + field = type + } + subtitle { + label = Subtitle + field = subTitle + keepAllOptionsOnSelection = 1 + } + pageHierarchy { + field = rootline + label = Rootline + + type = hierarchy + + hierarchy = HMENU + hierarchy { + 1 = TMENU + 1 { + NO = 1 + NO { + wrapItemAndSub =
  • |
  • + } + } + + 2 < .1 + 2.wrap =
      |
    + + 3 < .2 + } + } + + pid { + label = Uid Range + field = uid + + type = queryGroup + queryGroup { + small { + query = [* TO 2] + } + medium { + query = [2 TO 5] + } + + large { + query = [5 TO *] + } + } + + renderingInstruction = CASE + renderingInstruction { + key.field = optionValue + + default = TEXT + default.field = optionValue + + small = TEXT + small.value = Small (1 & 2) + + medium = TEXT + medium.value = Medium (2 to 5) + + large = TEXT + large.value = Large (5 to *) + } + } + } + + showAllLink.wrap =
  • |
  • + } + + elevation = 1 + elevation { + markElevatedResults = 1 + forceElevation = 1 + } + + } + + suggest = 1 + suggest { + numberOfSuggestions = 10 + suggestField = spell + forceHttps = 0 + } + + statistics = 1 + statistics { + anonymizeIP = 0 + } + + logging { + exceptions = 1 + + indexing { + indexQueueInitialization = 0 + missingTypo3SearchMarkers = 1 + pageIndexed = 0 + + queue { + pages = 0 + } + } + + query { + filters = 0 + searchWords = 0 + queryString = 0 + rawPost = 0 + rawGet = 0 + rawDelete = 0 + } + } + + } + ]]> +
    + 100 + 0 +
    + + 1 + 1 + 1 + Products + + + 2 + 1 + 0 + 1 + Socks + Men + + + 3 + 1 + 0 + 1 + Jeans + Men + + + 4 + 1 + 0 + 1 + Shoes + Woman + + + 5 + 1 + 0 + 1 + T-Shirts + Woman + + + 6 + 1 + 0 + 1 + T-Shirts + Men + + + 7 + 1 + 0 + 1 + Sweatshirts + Men + + + 8 + 1 + 0 + 1 + Sweatshirts + Woman + + + + 1 + 2 + text + Our awesome new sock products prices starting at 10 euro + 0 + + + + 2 + 3 + text + Our awesome men jeans products prices starting at 50 euro + 0 + + + + 1 + 100 + shoes + +
    \ No newline at end of file diff --git a/Tests/Integration/Controller/SearchControllerTest.php b/Tests/Integration/Controller/SearchControllerTest.php index 20a7ee95f3..7500560b0f 100644 --- a/Tests/Integration/Controller/SearchControllerTest.php +++ b/Tests/Integration/Controller/SearchControllerTest.php @@ -1,5 +1,5 @@ importDataSetFromFixture('can_render_search_controller.xml'); $GLOBALS['TSFE'] = $this->getConfiguredTSFE([], 1); - $formRequest = $this->getPreparedRequest('form'); + $formRequest = $this->getPreparedRequest('Search','form'); $formResponse = $this->getPreparedResponse(); $this->searchController->processRequest($formRequest, $formResponse); @@ -965,7 +963,7 @@ public function searchingAndRenderingFrequentSearchesIsShowingTheTermAsFrequentS $this->indexPages([1]); - $searchRequest = $this->getPreparedRequest('frequentlySearched', 'pi_frequentlySearched'); + $searchRequest = $this->getPreparedRequest('Search', 'frequentlySearched', 'pi_frequentlySearched'); $searchResponse = $this->getPreparedResponse(); $this->searchController->processRequest($searchRequest, $searchResponse); @@ -979,7 +977,7 @@ public function searchingAndRenderingFrequentSearchesIsShowingTheTermAsFrequentS */ public function canRenderDetailAction() { - $request = $this->getPreparedRequest('detail'); + $request = $this->getPreparedRequest('Search', 'detail'); $request->setArgument('documentId', '23c51a0d5cf548afecc043a7068902e8f82a22a0/pages/1/0/0/0'); $this->importDataSetFromFixture('can_render_search_controller.xml'); @@ -995,7 +993,7 @@ public function canRenderDetailAction() */ public function canRenderSearchFormOnly() { - $request = $this->getPreparedRequest('form', 'pi_search'); + $request = $this->getPreparedRequest('Search', 'form', 'pi_search'); $this->importDataSetFromFixture('can_render_search_controller.xml'); $GLOBALS['TSFE'] = $this->getConfiguredTSFE([], 1); @@ -1120,57 +1118,4 @@ protected function assertPaginationVisible($content) $this->assertContains('class="solr-pagination"', $content, 'No pagination container visible'); $this->assertContains('ul class="pagination"', $content, 'Could not see pagination list'); } - - /** - * @param $importPageIds - */ - protected function indexPages($importPageIds) - { - foreach ($importPageIds as $importPageId) { - $fakeTSFE = $this->getConfiguredTSFE([], $importPageId); - $GLOBALS['TSFE'] = $fakeTSFE; - $fakeTSFE->newCObj(); - $fakeTSFE->preparePageContentGeneration(); - PageGenerator::renderContent(); - /** @var $pageIndexer \ApacheSolrForTypo3\Solr\Typo3PageIndexer */ - $pageIndexer = GeneralUtility::makeInstance(Typo3PageIndexer::class, $fakeTSFE); - $pageIndexer->indexPage(); - } - - /** @var $beUser \TYPO3\CMS\Core\Authentication\BackendUserAuthentication */ - $beUser = GeneralUtility::makeInstance(BackendUserAuthentication::class); - $GLOBALS['BE_USER'] = $beUser; - $this->waitToBeVisibleInSolr(); - } - - /** - * @param string $actionName - * @param string $plugin - * @return Request - */ - protected function getPreparedRequest($actionName = 'results', $plugin = 'pi_result') - { - /** @var Request $request */ - $request = $this->objectManager->get(Request::class); - $request->setControllerName('Search'); - $request->setControllerActionName($actionName); - $request->setControllerVendorName('ApacheSolrForTypo3'); - $request->setPluginName($plugin); - $request->setFormat('html'); - $request->setControllerExtensionName('Solr'); - - return $request; - } - - - /** - * @return Response - */ - protected function getPreparedResponse() - { - /** @var $response Response */ - $response = $this->objectManager->get(Response::class); - - return $response; - } } diff --git a/Tests/Integration/Controller/SuggestControllerTest.php b/Tests/Integration/Controller/SuggestControllerTest.php new file mode 100644 index 0000000000..f01f9e45b9 --- /dev/null +++ b/Tests/Integration/Controller/SuggestControllerTest.php @@ -0,0 +1,106 @@ + + * All rights reserved + * + * This script is part of the TYPO3 project. The TYPO3 project is + * free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * The GNU General Public License can be found at + * http://www.gnu.org/copyleft/gpl.html. + * + * This script is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This copyright notice MUST APPEAR in all copies of the script! + ***************************************************************/ + +use ApacheSolrForTypo3\Solr\IndexQueue\FrontendHelper\PageFieldMappingIndexer; +use ApacheSolrForTypo3\Solr\Typo3PageIndexer; +use ApacheSolrForTypo3\Solr\System\Configuration\ConfigurationManager; +use ApacheSolrForTypo3\Solr\Tests\Integration\IntegrationTest; +use ApacheSolrForTypo3\Solr\Controller\SuggestController; +use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; +use TYPO3\CMS\Core\TimeTracker\TimeTracker; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException; +use TYPO3\CMS\Extbase\Mvc\Request; +use TYPO3\CMS\Extbase\Mvc\Web\Response; +use TYPO3\CMS\Extbase\Object\ObjectManager; +use TYPO3\CMS\Extbase\Object\ObjectManagerInterface; +use TYPO3\CMS\Fluid\View\Exception\InvalidTemplateResourceException; +use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; +use TYPO3\CMS\Frontend\Page\PageGenerator; + +/** + * Integration testcase to test for the SuggestController + * + * @author Timo Hund + */ +class SuggestControllerTest extends AbstractFrontendControllerTest +{ + /** + * @var ObjectManagerInterface The object manager + */ + protected $objectManager; + + /** + * @var SuggestController + */ + protected $suggestController; + + /** + * @var Request + */ + protected $suggestRequest; + + /** + * @var Response + */ + protected $suggestResponse; + + public function setUp() + { + parent::setUp(); + + $this->objectManager = GeneralUtility::makeInstance(ObjectManager::class); + + $GLOBALS['TT'] = $this->getMockBuilder(TimeTracker::class)->disableOriginalConstructor()->getMock(); + + /** @var $searchController SearchController */ + $this->suggestController = $this->objectManager->get(SuggestController::class); + $this->suggestRequest = $this->getPreparedRequest('Suggest', 'suggest'); + $this->suggestResponse = $this->getPreparedResponse(); + + $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['Indexer']['indexPageSubstitutePageDocument']['ApacheSolrForTypo3\\Solr\\IndexQueue\\FrontendHelper\\PageFieldMappingIndexer'] = PageFieldMappingIndexer::class; + + } + + /** + * @test + */ + public function canDoABasicSuggest() + { + $this->importDataSetFromFixture('can_render_suggest_controller.xml'); + $GLOBALS['TSFE'] = $this->getConfiguredTSFE([], 1); + $this->indexPages([1, 2, 3, 4, 5, 6, 7, 8]); + + $this->suggestRequest->setArgument('queryString', 'Sweat'); + $this->suggestRequest->setArgument('callback', 'rand'); + + $this->suggestController->processRequest($this->suggestRequest, $this->suggestResponse); + $result = $this->suggestResponse->getContent(); + + //we assume to get suggestions like Sweatshirt + $this->assertContains('suggestions":{"sweatshirts":2}', $result, 'Response did not contain sweatshirt suggestions'); + } +} \ No newline at end of file diff --git a/Tests/Integration/SearchTest.php b/Tests/Integration/SearchTest.php index ff59778d7f..faf1ca268c 100644 --- a/Tests/Integration/SearchTest.php +++ b/Tests/Integration/SearchTest.php @@ -27,13 +27,16 @@ use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\BigramPhraseFields; use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\PhraseFields; use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\QueryFields; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\Slops; use ApacheSolrForTypo3\Solr\Domain\Search\Query\ParameterBuilder\TrigramPhraseFields; -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; use ApacheSolrForTypo3\Solr\Search; use ApacheSolrForTypo3\Solr\System\Configuration\ConfigurationManager; +use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; use ApacheSolrForTypo3\Solr\Typo3PageIndexer; use TYPO3\CMS\Core\TimeTracker\TimeTracker; use TYPO3\CMS\Core\Utility\GeneralUtility; +use Solarium\QueryType\Select\Query\Query; /** * Test class to perform a search on a real solr server @@ -42,6 +45,19 @@ */ class SearchTest extends IntegrationTest { + + /** + * @var QueryBuilder + */ + protected $queryBuilder; + + + public function setUp() + { + parent::setUp(); + $this->queryBuilder = new QueryBuilder(new TypoScriptConfiguration([])); + } + public function tearDown() { parent::tearDown(); @@ -68,11 +84,10 @@ public function canSearchForADocument() /** @var $searchInstance \ApacheSolrForTypo3\Solr\Search */ $searchInstance = GeneralUtility::makeInstance(Search::class); - /** @var $query \ApacheSolrForTypo3\Solr\Domain\Search\Query\Query */ - $query = GeneralUtility::makeInstance(Query::class, ''); - $query->getQueryStringContainer()->useRawQueryString(true); - $query->setQueryFields(QueryFields::fromString('content^40.0, title^5.0, keywords^2.0, tagsH1^5.0, tagsH2H3^3.0, tagsH4H5H6^2.0, tagsInline^1.0, description^4.0, abstract^1.0, subtitle^1.0, navtitle^1.0, author^1.0')); - $query->getQueryStringContainer()->setQueryString('hello'); + $query = $this->queryBuilder + ->newSearchQuery('hello') + ->useQueryFields(QueryFields::fromString('content^40.0, title^5.0, keywords^2.0, tagsH1^5.0, tagsH2H3^3.0, tagsH4H5H6^2.0, tagsInline^1.0, description^4.0, abstract^1.0, subtitle^1.0, navtitle^1.0, author^1.0')) + ->getQuery(); $searchResponse = $searchInstance->search($query); $rawResponse = $searchResponse->getRawResponse(); @@ -90,8 +105,9 @@ public function implicitPhraseSearchingBoostsDocsWithOccurringPhrase() /** @var $searchInstance \ApacheSolrForTypo3\Solr\Search */ $searchInstance = GeneralUtility::makeInstance(Search::class); - $query = $this->getQueryForSolr(); - $query->getQueryStringContainer()->setQueryString('Hello World'); + $query = $this->queryBuilder + ->newSearchQuery('Hello World') + ->getQuery(); $searchResponse = $searchInstance->search($query); $parsedData = $searchResponse->getParsedData(); @@ -100,7 +116,7 @@ public function implicitPhraseSearchingBoostsDocsWithOccurringPhrase() $this->assertNotEquals('Hello World for phrase serching', $parsedData->response->docs[0]->getTitle(), 'Unexpected score calculation. Expected Document shouldn\'t be at first place.'); // Boost the document with query to make it first. - $query->setPhraseFields(PhraseFields::fromString('title^10.0')); + $query = $this->queryBuilder->startFrom($query)->usePhraseFields(PhraseFields::fromString('title^10.0'))->getQuery(); $searchResponse = $searchInstance->search($query); $parsedData = $searchResponse->getParsedData(); @@ -118,17 +134,21 @@ public function implicitPhraseSearchSloppyPhraseBoostCanBeAdjustedByPhraseSlop() /** @var $searchInstance \ApacheSolrForTypo3\Solr\Search */ $searchInstance = GeneralUtility::makeInstance(Search::class); - $query = $this->getQueryForSolr(); - $query->getQueryStringContainer()->setQueryString('Hello World'); + $query = $this->queryBuilder + ->newSearchQuery('Hello World') + ->getQuery(); + // Boost the document with query to make it first. - $query->setPhraseFields(PhraseFields::fromString('title^10.0')); + $query = $this->queryBuilder->startFrom($query)->usePhraseFields(PhraseFields::fromString('title^10.0'))->getQuery(); // do following things // test different phrase slop values $parsedDatasByPhraseSlop = []; for ($i = 0; $i <= 2; $i++) { - $query->getSlops()->setPhraseSlop($i); + $slops = new Slops(); + $slops->setPhraseSlop($i); + $query = $this->queryBuilder->startFrom($query)->useSlops($slops)->getQuery(); $searchResponse = $searchInstance->search($query); $parsedDatasByPhraseSlop[$i] = $searchResponse->getParsedData(); @@ -184,18 +204,21 @@ public function implicitPhraseSearchSloppyPhraseBoostCanBeAdjustedByBigramPhrase $this->switchPhraseSearchFeature('bigramPhrase', 1); - $query = $this->getQueryForSolr(); - $query->getQueryStringContainer()->setQueryString('Bigram Phrase Search'); + $query = $this->getSearchQueryForSolr(); + $this->queryBuilder->useQueryString('Bigram Phrase Search'); + // Boost the document with query to make it first. - $query->setBigramPhraseFields(BigramPhraseFields::fromString('title^100.0')); + $this->queryBuilder->useBigramPhraseFields(BigramPhraseFields::fromString('title^100.0')); // do following things // test different phrase slop values $parsedDatasByPhraseSlop = []; for ($i = 0; $i <= 2; $i++) { - $query->getSlops()->setBigramPhraseSlop($i); - $searchResponse = $searchInstance->search($query); + $slops = new Slops(); + $slops->setBigramPhraseSlop($i); + $this->queryBuilder->useSlops($slops); + $searchResponse = $searchInstance->search($this->queryBuilder->getQuery()); $parsedDatasByPhraseSlop[$i] = $searchResponse->getParsedData(); } @@ -262,18 +285,21 @@ public function implicitPhraseSearchSloppyPhraseBoostCanBeAdjustedByTrigramPhras $this->switchPhraseSearchFeature('trigramPhrase', 1); - $query = $this->getQueryForSolr(); - $query->getQueryStringContainer()->setQueryString('Awesome Trigram Phrase Search'); + $query = $this->getSearchQueryForSolr(); + $this->queryBuilder + ->useQueryString('Awesome Trigram Phrase Search') + // Boost the document with query to make it first. + ->useTrigramPhraseFields(TrigramPhraseFields::fromString('title^100.0')); - // Boost the document with query to make it first. - $query->setTrigramPhraseFields(TrigramPhraseFields::fromString('title^100.0')); // do following things // test different phrase slop values $parsedDatasByPhraseSlop = []; for ($i = 0; $i <= 2; $i++) { - $query->getSlops()->setTrigramPhraseSlop($i); - $searchResponse = $searchInstance->search($query); + $slops = new Slops(); + $slops->setTrigramPhraseSlop($i); + $this->queryBuilder->useSlops($slops); + $searchResponse = $searchInstance->search($this->queryBuilder->getQuery()); $parsedDatasByPhraseSlop[$i] = $searchResponse->getParsedData(); } @@ -334,10 +360,9 @@ public function explicitPhraseSearchMatchesMorePrecise() /** @var $searchInstance \ApacheSolrForTypo3\Solr\Search */ $searchInstance = GeneralUtility::makeInstance(Search::class); - $query = $this->getQueryForSolr(); - $query->getQueryStringContainer()->setQueryString('"Hello World"'); - - $searchResponse = $searchInstance->search($query); + $query = $this->getSearchQueryForSolr(); + $this->queryBuilder->startFrom($query)->useQueryString('"Hello World"'); + $searchResponse = $searchInstance->search($this->queryBuilder->getQuery()); $parsedData = $searchResponse->getParsedData(); // document with "Hello World for phrase searching" is not on first place! @@ -355,8 +380,8 @@ public function explicitPhraseSearchPrecisionCanBeAdjustedByQuerySlop() /** @var $searchInstance \ApacheSolrForTypo3\Solr\Search */ $searchInstance = GeneralUtility::makeInstance(Search::class); - $query = $this->getQueryForSolr(); - $query->getQueryStringContainer()->setQueryString('"Hello World"'); + $query = $this->getSearchQueryForSolr(); + $this->queryBuilder->useQueryString('"Hello World"'); $searchResponse = $searchInstance->search($query); $parsedData = $searchResponse->getParsedData(); @@ -366,13 +391,17 @@ public function explicitPhraseSearchPrecisionCanBeAdjustedByQuerySlop() $this->assertSame('Hello World for phrase searching', $parsedData->response->docs[0]->getTitle(), 'Document containing "Hello World for phrase serching" should be found'); // simulate Lucenes "Hello World"~1 - $query->getSlops()->setQuerySlop(1); + $slops = new Slops(); + $slops->setQuerySlop(1); + $query = $this->queryBuilder->useSlops($slops)->getQuery(); $searchResponse = $searchInstance->search($query); $parsedData = $searchResponse->getParsedData(); $this->assertSame(3, $parsedData->response->numFound, 'Could not index document into solr'); // simulate Lucenes "Hello World"~2 - $query->getSlops()->setQuerySlop(2); + $slops->setQuerySlop(2); + $query = $this->queryBuilder->useSlops($slops)->getQuery(); + $searchResponse = $searchInstance->search($query); $parsedData = $searchResponse->getParsedData(); $this->assertSame(7, $parsedData->response->numFound, 'Found wrong number of decuments by explicit phrase search query.'); @@ -400,13 +429,12 @@ protected function fillIndexForPhraseSearchTests(string $fixture = 'phrase_searc /** * @return Query */ - protected function getQueryForSolr() : Query + protected function getSearchQueryForSolr() : Query { - /** @var $query \ApacheSolrForTypo3\Solr\Domain\Search\Query\Query */ - $query = GeneralUtility::makeInstance(Query::class, ''); - $query->getQueryStringContainer()->useRawQueryString(true); - $query->setQueryFields(QueryFields::fromString('content^40.0, title^5.0, keywords^2.0, tagsH1^5.0, tagsH2H3^3.0, tagsH4H5H6^2.0, tagsInline^1.0, description^4.0, abstract^1.0, subtitle^1.0, navtitle^1.0, author^1.0')); - return $query; + return $this->queryBuilder + ->newSearchQuery('') + ->useQueryFields(QueryFields::fromString('content^40.0, title^5.0, keywords^2.0, tagsH1^5.0, tagsH2H3^3.0, tagsH4H5H6^2.0, tagsInline^1.0, description^4.0, abstract^1.0, subtitle^1.0, navtitle^1.0, author^1.0')) + ->getQuery(); } /** diff --git a/Tests/Integration/System/Solr/Service/SolrWriteServiceTest.php b/Tests/Integration/System/Solr/Service/SolrWriteServiceTest.php index 859261e069..a14bbbfa26 100644 --- a/Tests/Integration/System/Solr/Service/SolrWriteServiceTest.php +++ b/Tests/Integration/System/Solr/Service/SolrWriteServiceTest.php @@ -57,9 +57,9 @@ public function setUp() public function canExtractByQuery() { $testFilePath = $this->getFixturePathByName('testpdf.pdf'); - /** @var $extractQuery \ApacheSolrForTypo3\Solr\ExtractingQuery */ + /** @var $extractQuery \ApacheSolrForTypo3\Solr\Domain\Search\Query\ExtractingQuery*/ $extractQuery = GeneralUtility::makeInstance(ExtractingQuery::class, $testFilePath); - $extractQuery->setExtractOnly(); + $extractQuery->setExtractOnly(true); $response = $this->solrWriteService->extractByQuery($extractQuery); $this->assertContains('PDF Test', $response[0], 'Could not extract text'); } diff --git a/Tests/Unit/Domain/Search/ApacheSolrDocument/RepositoryTest.php b/Tests/Unit/Domain/Search/ApacheSolrDocument/RepositoryTest.php index ae27c5cb0f..51f3431ede 100644 --- a/Tests/Unit/Domain/Search/ApacheSolrDocument/RepositoryTest.php +++ b/Tests/Unit/Domain/Search/ApacheSolrDocument/RepositoryTest.php @@ -26,8 +26,8 @@ use ApacheSolrForTypo3\Solr\ConnectionManager; use ApacheSolrForTypo3\Solr\Domain\Search\ApacheSolrDocument\Repository; -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Result\Parser\DocumentEscapeService; use ApacheSolrForTypo3\Solr\NoSolrConnectionFoundException; use ApacheSolrForTypo3\Solr\Search; diff --git a/Tests/Unit/Domain/Search/Query/ParameterBuilder/BigramPhraseFieldsTest.php b/Tests/Unit/Domain/Search/Query/ParameterBuilder/BigramPhraseFieldsTest.php index faf7fd116b..c487f0c84c 100644 --- a/Tests/Unit/Domain/Search/Query/ParameterBuilder/BigramPhraseFieldsTest.php +++ b/Tests/Unit/Domain/Search/Query/ParameterBuilder/BigramPhraseFieldsTest.php @@ -1,5 +1,5 @@ getDumbMock(TypoScriptConfiguration::class); - $query = new Query('foo', $configurationMock); - - $bigramPhraseFields->build($query); - - $this->assertEmpty($query->getQueryParameters()['pf2'], 'Build on Phrase field does not create empty array when calling build'); + $this->assertSame('', $bigramPhraseFields->toString()); } } \ No newline at end of file diff --git a/Tests/Unit/Domain/Search/Query/ParameterBuilder/QueryFieldsTest.php b/Tests/Unit/Domain/Search/Query/ParameterBuilder/QueryFieldsTest.php index f548d5f98c..e8e846f118 100644 --- a/Tests/Unit/Domain/Search/Query/ParameterBuilder/QueryFieldsTest.php +++ b/Tests/Unit/Domain/Search/Query/ParameterBuilder/QueryFieldsTest.php @@ -1,5 +1,5 @@ builder = new QueryBuilder($this->configurationMock, $this->loggerMock, $this->siteHashServiceMock); } + /** + * @param Query $searchQuery + * @return array + */ + protected function getAllQueryParameters(Query $searchQuery) + { + $requestBuilder = new RequestBuilder(); + $request = $requestBuilder->build($searchQuery); + return $request->getParams(); + } + /** * @param string $queryString * @param TypoScriptConfiguration|null $fakeConfiguration - * @return Query + * @return SearchQuery */ - protected function getInitializedTestSearchQuery(string $queryString = '', TypoScriptConfiguration $fakeConfiguration = null): Query + protected function getInitializedTestSearchQuery(string $queryString = '', TypoScriptConfiguration $fakeConfiguration = null): SearchQuery { $builder = new QueryBuilder($fakeConfiguration, $this->loggerMock); return $builder->buildSearchQuery($queryString); @@ -97,7 +120,6 @@ public function buildSearchQueryPassesDefaultPerPage() { $query = $this->builder->buildSearchQuery('one'); $this->assertSame(10, $query->getRows(), 'Query was not created with default perPage value'); - $this->assertSame(10, $query->getPagination()->getResultsPerPage(), 'Query was not created with default perPage value'); } @@ -108,7 +130,6 @@ public function buildSearchQueryPassesCustomPerPage() { $query = $this->builder->buildSearchQuery('one', 22); $this->assertSame(22, $query->getRows(), 'Query was not created with default perPage value'); - $this->assertSame(22, $query->getPagination()->getResultsPerPage(), 'Query was not created with default perPage value'); } /** @@ -118,7 +139,7 @@ public function buildSearchQueryInitializesQueryFieldsFromConfiguration() { $this->configurationMock->expects($this->once())->method('getSearchQueryQueryFields')->willReturn('title^10, content^123'); $query = $this->builder->buildSearchQuery('foo'); - $this->assertSame('title^10.0 content^123.0', $query->getQueryFields()->toString(), 'The queryFields have not been initialized as expected'); + $this->assertSame('title^10.0 content^123.0', $this->getAllQueryParameters($query)['qf'], 'The queryFields have not been initialized as expected'); } /** @@ -130,7 +151,7 @@ public function buildSearchQueryInitializesTrigramPhraseFields() $this->configurationMock->expects($this->once())->method('getSearchQueryTrigramPhraseFields')->willReturn('content^10.0, title^10.0'); $query = $this->builder->buildSearchQuery('trigram'); - $this->assertSame('content^10.0 title^10.0', $query->getTrigramPhraseFields()->toString(), 'The trigramPhraseFields have not been initialized as expected'); + $this->assertSame('content^10.0 title^10.0', $this->getAllQueryParameters($query)['pf3'], 'The trigramPhraseFields have not been initialized as expected'); } /** @@ -140,7 +161,7 @@ public function buildSearchIsSettingWildCardQueryOnInitializeWithEmptyQuery() { $this->configurationMock->expects($this->once())->method('getSearchInitializeWithEmptyQuery')->willReturn(true); $query = $this->builder->buildSearchQuery('initializeWithEmpty'); - $this->assertSame('*:*', $query->getAlternativeQuery(), 'The alterativeQuery has not been initialized as expected'); + $this->assertSame('*:*', $this->getAllQueryParameters($query)['q.alt'], 'The alterativeQuery has not been initialized as expected'); } /** @@ -150,7 +171,7 @@ public function buildSearchIsSettingWildCardQueryOnInitializeWithAllowEmptyQuery { $this->configurationMock->expects($this->once())->method('getSearchQueryAllowEmptyQuery')->willReturn(true); $query = $this->builder->buildSearchQuery('initializeWithEmpty'); - $this->assertSame('*:*', $query->getAlternativeQuery(), 'The alterativeQuery has not been initialized as expected'); + $this->assertSame('*:*', $this->getAllQueryParameters($query)['q.alt'], 'The alterativeQuery has not been initialized as expected'); } /** @@ -160,7 +181,7 @@ public function buildSearchIsSettingQuerystringForConfiguredInitialQuery() { $this->configurationMock->expects($this->exactly(2))->method('getSearchInitializeWithQuery')->willReturn('myinitialsearch'); $query = $this->builder->buildSearchQuery('initializeWithEmpty'); - $this->assertSame('myinitialsearch', $query->getAlternativeQuery(), 'The alterativeQuery has not been initialized from a configured initial query'); + $this->assertSame('myinitialsearch', $this->getAllQueryParameters($query)['q.alt'], 'The alterativeQuery has not been initialized from a configured initial query'); } /** @@ -170,10 +191,11 @@ public function buildSearchIsSettingConfiguredAdditionalFilters() { $this->configurationMock->expects($this->any())->method('getSearchQueryFilterConfiguration')->willReturn(['noPage' => '-type:pages']); $query = $this->builder->buildSearchQuery('applies configured filters'); + $filterValue = $this->getAllQueryParameters($query)['fq']; + $filterArray = explode(" ", $filterValue); - $filterValues = $query->getQueryParameter('fq'); - $this->assertCount(1, $filterValues, 'Unpexcted amount of filters for query'); - $this->assertSame('-type:pages', $filterValues[0], 'First filter has unexpected value'); + $this->assertCount(1, $filterArray, 'Unpexcted amount of filters for query'); + $this->assertSame('-type:pages', $filterValue, 'First filter has unexpected value'); } /** @@ -182,7 +204,7 @@ public function buildSearchIsSettingConfiguredAdditionalFilters() public function buildSearchIsSettingNoAlternativeQueryByDefault() { $query = $this->builder->buildSearchQuery('initializeWithEmpty'); - $this->assertNull($query->getAlternativeQuery(), 'The alterativeQuery is not null when nothing was set'); + $this->assertNull($this->getAllQueryParameters($query)['q.alt'], 'The alterativeQuery is not null when nothing was set'); } /** @@ -190,10 +212,13 @@ public function buildSearchIsSettingNoAlternativeQueryByDefault() */ public function canEnableHighlighting() { - /** @var $query \ApacheSolrForTypo3\Solr\Domain\Search\Query\Query */ + /** @var $query \ApacheSolrForTypo3\Solr\Domain\Search\Query\SearchQuery */ $query = $this->getInitializedTestSearchQuery(); - $query->getHighlighting()->setIsEnabled(true); - $queryParameters = $query->getQueryParameters(); + $highlighting = new Highlighting(); + $highlighting->setIsEnabled(true); + + $query = $this->builder->startFrom($query)->useHighlighting($highlighting)->getQuery(); + $queryParameters = $this->getAllQueryParameters($query); $this->assertSame('true', $queryParameters['hl'], 'Enable highlighting did not set the "hl" query parameter'); $this->assertSame(200, $queryParameters['hl.fragsize'], 'hl.fragsize was not set to the default value of 200'); } @@ -203,16 +228,19 @@ public function canEnableHighlighting() */ public function canDisableHighlighting() { - /** @var $query \ApacheSolrForTypo3\Solr\Domain\Search\Query\Query */ + /** @var $query \ApacheSolrForTypo3\Solr\Domain\Search\Query\SearchQuery */ $query = $this->getInitializedTestSearchQuery(); - $query->getHighlighting()->setIsEnabled(true); - - $queryParameters = $query->getQueryParameters(); + $highlighting = new Highlighting(); + $highlighting->setIsEnabled(true); + $query = $this->builder->startFrom($query)->useHighlighting($highlighting)->getQuery(); + $queryParameters = $this->getAllQueryParameters($query); $this->assertSame('true', $queryParameters['hl'], 'Enable highlighting did not set the "hl" query parameter'); - $query->getHighlighting()->setIsEnabled(false); - $queryParameters = $query->getQueryParameters(); + $highlighting->setIsEnabled(false); + $query = $this->builder->startFrom($query)->useHighlighting($highlighting)->getQuery(); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertNull($queryParameters['hl'], 'Could not disable highlighting'); } @@ -227,8 +255,12 @@ public function canSetHighlightingFieldList() $fakeConfiguration = new TypoScriptConfiguration($fakeConfigurationArray); $query = $this->getInitializedTestSearchQuery('test', $fakeConfiguration); - $query->getHighlighting()->setIsEnabled(true); - $queryParameters = $query->getQueryParameters(); + + $highlighting = Highlighting::fromTypoScriptConfiguration($fakeConfiguration); + $highlighting->setIsEnabled(true); + $query = $this->builder->startFrom($query)->useHighlighting($highlighting)->getQuery(); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertSame('true', $queryParameters['hl'], 'Enable highlighting did not set the "hl" query parameter'); $this->assertSame('title', $queryParameters['hl.fl'], 'Can set highlighting field list'); } @@ -242,11 +274,12 @@ public function canPassCustomWrapForHighlighting() $fakeConfigurationArray['plugin.']['tx_solr.']['search.']['results.']['resultsHighlighting'] = 1; $fakeConfigurationArray['plugin.']['tx_solr.']['search.']['results.']['resultsHighlighting.']['wrap'] = '[A]|[B]'; $fakeConfiguration = new TypoScriptConfiguration($fakeConfigurationArray); - $query = $this->getInitializedTestSearchQuery('test', $fakeConfiguration); - $query->getHighlighting()->setIsEnabled(true); - $queryParameters = $query->getQueryParameters(); + $highlighting = Highlighting::fromTypoScriptConfiguration($fakeConfiguration); + $highlighting->setIsEnabled(true); + $query = $this->builder->startFrom($query)->useHighlighting($highlighting)->getQuery(); + $queryParameters = $this->getAllQueryParameters($query); $this->assertSame('[A]', $queryParameters['hl.tag.pre'], 'Can set highlighting hl.tag.pre'); $this->assertSame('[B]', $queryParameters['hl.tag.post'], 'Can set highlighting hl.tag.post'); $this->assertSame('[A]', $queryParameters['hl.simple.pre'], 'Can set highlighting hl.tag.pre'); @@ -265,11 +298,13 @@ public function simplePreAndPostIsUsedWhenFastVectorHighlighterCouldNotBeUsed() $query = $this->getInitializedTestSearchQuery('test', $fakeConfiguration); + $highlighting = Highlighting::fromTypoScriptConfiguration($fakeConfiguration); + $highlighting->setIsEnabled(true); // fragSize 10 is to small for FastVectorHighlighter - $query->getHighlighting()->setIsEnabled(true); - $query->getHighlighting()->setFragmentSize(17); + $highlighting->setFragmentSize(17); + $query = $this->builder->startFrom($query)->useHighlighting($highlighting)->getQuery(); + $queryParameters = $this->getAllQueryParameters($query); - $queryParameters = $query->getQueryParameters(); $this->assertSame('[A]', $queryParameters['hl.simple.pre'], 'Can set highlighting field list'); $this->assertSame('[B]', $queryParameters['hl.simple.post'], 'Can set highlighting field list'); $this->assertEmpty($queryParameters['hl.tag.pre'], 'When the highlighting fragment size is to small hl.tag.pre should not be used because FastVectoreHighlighter will not be used'); @@ -285,9 +320,12 @@ public function canUseFastVectorHighlighting() $fakeConfiguration = new TypoScriptConfiguration($fakeConfigurationArray); $query = $this->getInitializedTestSearchQuery('test', $fakeConfiguration); - $query->getHighlighting()->setIsEnabled(true); - $query->getHighlighting()->setFragmentSize(200); - $queryParameters = $query->getQueryParameters(); + $highlighting = Highlighting::fromTypoScriptConfiguration($fakeConfiguration); + $highlighting->setIsEnabled(true); + // fragSize 10 is to small for FastVectorHighlighter + $highlighting->setFragmentSize(200); + $query = $this->builder->startFrom($query)->useHighlighting($highlighting)->getQuery(); + $queryParameters = $this->getAllQueryParameters($query); $this->assertSame('true', $queryParameters['hl'], 'Enable highlighting did not set the "hl" query parameter'); $this->assertSame('true', $queryParameters['hl.useFastVectorHighlighter'], 'Enable highlighting did not set the "hl.useFastVectorHighlighter" query parameter'); @@ -302,12 +340,15 @@ public function fastVectorHighlighterIsDisabledWhenFragSizeIsLessThen18() $fakeConfiguration = new TypoScriptConfiguration($fakeConfigurationArray); $query = $this->getInitializedTestSearchQuery('test', $fakeConfiguration); - $query->getHighlighting()->setIsEnabled(true); - $query->getHighlighting()->setFragmentSize(0); - $queryParameters = $query->getQueryParameters(); + $highlighting = Highlighting::fromTypoScriptConfiguration($fakeConfiguration); + $highlighting->setIsEnabled(true); + // fragSize 10 is to small for FastVectorHighlighter + $highlighting->setFragmentSize(0); + $query = $this->builder->startFrom($query)->useHighlighting($highlighting)->getQuery(); + $queryParameters = $this->getAllQueryParameters($query); $this->assertSame('true', $queryParameters['hl'], 'Enable highlighting did not set the "hl" query parameter'); - $this->assertNull($queryParameters['hl.useFastVectorHighlighter'], 'FastVectorHighlighter was disabled but still requested'); + $this->assertSame('false',$queryParameters['hl.useFastVectorHighlighter'], 'FastVectorHighlighter was disabled but still requested'); } /** @@ -316,29 +357,7 @@ public function fastVectorHighlighterIsDisabledWhenFragSizeIsLessThen18() public function canSetQueryString() { $query = $this->getInitializedTestSearchQuery('i like solr'); - $this->assertSame('i like solr', $query->getQueryStringContainer()->getQueryString(), 'Can not set and get query string'); - } - - /** - * @test - */ - public function queryStringCanBeOverwrittenWhenUseQueryStringWasSet() - { - $query = $this->getInitializedTestSearchQuery('i like solr'); - $query->getQueryStringContainer()->useRawQueryString(true); - $query->getQueryStringContainer()->setQueryString('i like SOLR!'); - $this->assertSame('i like SOLR!', $query->getQueryStringContainer()->getQueryString(), 'Can not set and get query string'); - } - - /** - * @test - */ - public function queryStringCanNotBeOverwrittenWhenUseQueryStringWasSetToFalse() - { - $query = $this->getInitializedTestSearchQuery('i like solr'); - $query->getQueryStringContainer()->useRawQueryString(false); - $query->getQueryStringContainer()->setQueryString('i like SOLR!'); - $this->assertSame('i like solr', $query->getQueryStringContainer()->getQueryString(), 'Can not set and get query string'); + $this->assertSame('i like solr', $query->getQuery(), 'Can not set and get query string'); } /** @@ -347,9 +366,9 @@ public function queryStringCanNotBeOverwrittenWhenUseQueryStringWasSetToFalse() public function canSetPage() { $query = $this->getInitializedTestSearchQuery('i like solr'); - $query->getPagination()->setPage(10); + $query->setStart(10); - $this->assertSame(10, $query->getPagination()->getPage(), 'Can not set and get page'); + $this->assertSame(10, $query->getStart(), 'Can not set and get page'); } /** @@ -358,14 +377,8 @@ public function canSetPage() public function noFiltersAreSetAfterInitialization() { $query = $this->getInitializedTestSearchQuery(); - $filters = $query->getFilters()->getValues(); - - - $this->assertCount( - 0, - $filters, - 'Query already contains filters after intialization.' - ); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertEmpty($queryParameters['fq'], 'Query already contains filters after intialization.'); } /** @@ -376,13 +389,8 @@ public function addsCorrectAccessFilterForAnonymousUser() $query = $this->getInitializedTestSearchQuery(); $queryBuilder = new QueryBuilder($this->configurationMock, $this->loggerMock); $queryBuilder->startFrom($query)->useUserAccessGroups([-1, 0]); - $filters = $query->getFilters()->getValues(); - - $this->assertContains( - '{!typo3access}-1,0', - $filters, - 'Access filter not found in [' . implode('], [', (array)$filters) . ']' - ); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertSame('{!typo3access}-1,0', $queryParameters['fq'], 'Accessfilter was not applied'); } /** @@ -393,13 +401,8 @@ public function grantsAccessToGroupZeroIfNoGroupsProvided() $query = $this->getInitializedTestSearchQuery(); $queryBuilder = new QueryBuilder($this->configurationMock, $this->loggerMock); $queryBuilder->startFrom($query)->useUserAccessGroups([]); - $filters = $query->getFilters()->getValues(); - - $this->assertContains( - '{!typo3access}0', - $filters, - 'Access filter not found in [' . implode('], [', (array)$filters) . ']' - ); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertSame('{!typo3access}0', $queryParameters['fq'], 'Changed accessfilter was not applied'); } /** @@ -408,16 +411,10 @@ public function grantsAccessToGroupZeroIfNoGroupsProvided() public function grantsAccessToGroupZeroIfZeroNotProvided() { $query = $this->getInitializedTestSearchQuery(); - $queryBuilder = new QueryBuilder($this->configurationMock, $this->loggerMock); $queryBuilder->startFrom($query)->useUserAccessGroups([5]); - $filters = $query->getFilters()->getValues(); - - $this->assertContains( - '{!typo3access}0,5', - $filters, - 'Access filter not found in [' . implode('], [', (array)$filters) . ']' - ); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertSame('{!typo3access}0,5', $queryParameters['fq'], 'Access filter was not applied as expected'); } /** @@ -426,16 +423,10 @@ public function grantsAccessToGroupZeroIfZeroNotProvided() public function filtersDuplicateAccessGroups() { $query = $this->getInitializedTestSearchQuery(); - $queryBuilder = new QueryBuilder($this->configurationMock, $this->loggerMock); $queryBuilder->startFrom($query)->useUserAccessGroups([1, 1]); - $filters = $query->getFilters()->getValues(); - - $this->assertContains( - '{!typo3access}0,1', - $filters, - 'Access filter not found in [' . implode('], [', (array)$filters) . ']' - ); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertSame('{!typo3access}0,1', $queryParameters['fq'], 'Access filter was not applied as expected'); } /** @@ -446,17 +437,9 @@ public function allowsOnlyOneAccessFilter() $query = $this->getInitializedTestSearchQuery(); $queryBuilder = new QueryBuilder($this->configurationMock, $this->loggerMock); $queryBuilder->startFrom($query)->useUserAccessGroups([1])->useUserAccessGroups([2]); + $queryParameters = $this->getAllQueryParameters($query); - $filters = $query->getFilters()->getValues(); - - $this->assertSame( - count($filters), - 1, - 'Too many filters in [' . implode('], [', (array)$filters) . ']' - ); - - $parameter = $query->getQueryParameters(); - $this->assertSame('{!typo3access}0,2', $parameter['fq'][0], 'Unexpected filter query'); + $this->assertSame('{!typo3access}0,2', $queryParameters['fq'], 'Unexpected filter query'); } // TODO if user is in group -2 (logged in), disallow access to group -1 @@ -485,9 +468,11 @@ public function groupingIsNotActiveAfterInitialization() public function settingGroupingTrueActivatesGrouping() { $query = $this->getInitializedTestSearchQuery(); - $query->getGrouping()->setIsEnabled(true); - $queryParameters = $query->getQueryParameters(); + $grouping = new Grouping(true); + $query = $this->builder->startFrom($query)->useGrouping($grouping)->getQuery(); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertArrayHasKey('group', $queryParameters); $this->assertEquals('true', $queryParameters['group']); @@ -504,12 +489,11 @@ public function settingGroupingTrueActivatesGrouping() * @test * @depends settingGroupingTrueActivatesGrouping */ - public function settingGroupingFalseDeactivatesGrouping(Query $query) + public function settingGroupingFalseDeactivatesGrouping(SearchQuery $query) { - $query->getGrouping()->setIsEnabled(false); - - $queryParameters = $query->getQueryParameters(); - + $grouping = new Grouping(false); + $query = $this->builder->startFrom($query)->useGrouping($grouping)->getQuery(); + $queryParameters = $this->getAllQueryParameters($query); foreach ($queryParameters as $queryParameter => $value) { $this->assertTrue( !GeneralUtility::isFirstPartOfStr($queryParameter, 'group'), @@ -545,12 +529,13 @@ public function canAddGroupField() public function canGetGroupSorting() { $query = $this->getInitializedTestSearchQuery('test'); - $this->assertSame([], $query->getGrouping()->getSortings(), 'By default getGroupSortings should return an empty array'); - - $query->getGrouping()->addSorting('price_f'); - $query->getGrouping()->addSorting('author_s'); - - $this->assertSame(['price_f', 'author_s'], $query->getGrouping()->getSortings(), 'Can not get groupSortings after adding'); + $this->assertNull($query->getGrouping()->getSort(), 'By default getGroupSortings should return an empty array'); + $grouping = new Grouping(true); + $grouping->addSorting('price_f'); + $grouping->addSorting('author_s'); + $query = $this->builder->startFrom($query)->useGrouping($grouping)->getQuery(); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertSame('price_f author_s', $queryParameters['group.sort'], 'Can not get groupSortings after adding'); } /** @@ -559,11 +544,14 @@ public function canGetGroupSorting() public function canSetNumberOfResultsByGroup() { $query = $this->getInitializedTestSearchQuery('group test'); - $initialValue = $query->getGrouping()->getResultsPerGroup(); - $this->assertSame(1, $initialValue); - - $query->getGrouping()->setResultsPerGroup(22); - $this->assertSame(22, $query->getGrouping()->getResultsPerGroup(), 'Can not set number of results per group'); + $grouping = new Grouping(true); + $grouping->addSorting('price_f'); + $grouping->addSorting('author_s'); + $this->assertSame(1, $grouping->getResultsPerGroup()); + $grouping->setResultsPerGroup(22); + $query = $this->builder->startFrom($query)->useGrouping($grouping)->getQuery(); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertSame(22, $queryParameters['group.limit'], 'Can not set number of results per group'); } /** @@ -587,11 +575,10 @@ public function canGetQueryFieldsAsStringWhenPassedFromConfiguration() $fakeConfigurationArray = []; $fakeConfigurationArray['plugin.']['tx_solr.']['search.']['query.']['queryFields'] = $input; $fakeConfiguration = new TypoScriptConfiguration($fakeConfigurationArray); - $query = $this->getInitializedTestSearchQuery('test', $fakeConfiguration); - $output = $query->getQueryFields()->toString(); + $queryParameters = $this->getAllQueryParameters($query); $expectedOutput = 'content^10.0 title^5.0'; - + $output = $queryParameters['qf']; $this->assertSame($output, $expectedOutput, 'Passed and retrieved query fields are not the same'); } @@ -604,9 +591,9 @@ public function canReturnEmptyStringAsQueryFieldStringWhenNothingWasPassed() $fakeConfiguration = new TypoScriptConfiguration($fakeConfigurationArray); $query = $this->getInitializedTestSearchQuery('test', $fakeConfiguration); - $output = $query->getQueryFields()->toString(); + $queryParameters = $this->getAllQueryParameters($query); $expectedOutput = ''; - + $output = $queryParameters['qf']; $this->assertSame($output, $expectedOutput, 'Unexpected output from getQueryFieldsAsString when no configuration was passed'); } @@ -616,15 +603,17 @@ public function canReturnEmptyStringAsQueryFieldStringWhenNothingWasPassed() public function canSetMinimumMatch() { $query = $this->getInitializedTestSearchQuery(); - $this->assertNull($query->getQueryParameter('mm')); + $queryParameters = $this->getAllQueryParameters($query); + + $this->assertNull($queryParameters['mm']); - // can we set a value? - $query->setMinimumMatch('2<-35%'); - $this->assertSame('2<-35%', $query->getQueryParameter('mm')); + $query = $this->builder->startFrom($query)->useMinimumMatch('2<-35%')->getQuery(); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertSame('2<-35%', $queryParameters['mm']); - // can we unset the value? - $query->setMinimumMatch(false); - $this->assertNull($query->getQueryParameter('mm')); + $query = $this->builder->startFrom($query)->removeMinimumMatch()->getQuery(); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertNull($queryParameters['mm']); } /** @@ -633,14 +622,19 @@ public function canSetMinimumMatch() public function canSetBoostFunction() { $query = $this->getInitializedTestSearchQuery(); - $this->assertNull($query->getQueryParameter('bf')); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertNull($queryParameters['bf']); $testBoostFunction = 'recip(ms(NOW,created),3.16e-11,1,1)'; - $query->setBoostFunction($testBoostFunction); - $this->assertSame($testBoostFunction, $query->getQueryParameter('bf'), 'bf queryParameter was not present after setting a boostFunction'); + $query = $this->builder->startFrom($query)->useBoostFunction($testBoostFunction)->getQuery(); + $queryParameters = $this->getAllQueryParameters($query); - $query->setBoostFunction(false); - $this->assertNull($query->getQueryParameter('bf'), 'bf parameter should be null after reset'); + $this->assertSame($testBoostFunction, $queryParameters['bf'], 'bf queryParameter was not present after setting a boostFunction'); + + $query = $this->builder->startFrom($query)->removeAllBoostFunctions()->getQuery(); + $queryParameters = $this->getAllQueryParameters($query); + + $this->assertNull($queryParameters['bf'], 'bf parameter should be null after reset'); } /** @@ -649,14 +643,15 @@ public function canSetBoostFunction() public function canSetBoostQuery() { $query = $this->getInitializedTestSearchQuery(); - $this->assertNull($query->getQueryParameter('bq')); - + $queryParameters = $this->getAllQueryParameters($query); + $this->assertNull($queryParameters['bq']); $testBoostQuery = '(type:tt_news)^10'; - $query->setBoostQuery($testBoostQuery); - $this->assertSame($testBoostQuery, $query->getQueryParameter('bq'), 'bq queryParameter was not present after setting a boostQuery'); - - $query->setBoostQuery(false); - $this->assertNull($query->getQueryParameter('bq'), 'bq parameter should be null after reset'); + $query = $this->builder->startFrom($query)->useBoostQueries($testBoostQuery)->getQuery(); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertSame($testBoostQuery, $queryParameters['bq'], 'bq queryParameter was not present after setting a boostQuery'); + $query = $this->builder->startFrom($query)->removeAllBoostQueries()->getQuery(); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertEmpty($queryParameters['bq'], 'bq parameter should be null after reset'); } /** @@ -668,13 +663,9 @@ public function canReturnFieldListWhenConfigurationWithReturnFieldsWasPassed() $fakeConfigurationArray = []; $fakeConfigurationArray['plugin.']['tx_solr.']['search.']['query.']['returnFields'] = $input; $fakeConfiguration = new TypoScriptConfiguration($fakeConfigurationArray); - $query = $this->getInitializedTestSearchQuery('test', $fakeConfiguration); - - $output = $query->getReturnFields()->getValues(); - $expectedOutput = ['abstract', 'price']; - - $this->assertSame($output, $expectedOutput, 'Did not parse returnsFields as expected'); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertSame('abstract,price', $queryParameters['fl'], 'Did not parse returnsFields as expected'); } /** @@ -684,12 +675,9 @@ public function canReturnDefaultFieldListWhenNoConfigurationWasPassed() { $fakeConfigurationArray = []; $fakeConfiguration = new TypoScriptConfiguration($fakeConfigurationArray); - $query = $this->getInitializedTestSearchQuery('test', $fakeConfiguration); - $output = $query->getReturnFields()->getValues(); - $expectedOutput = ['*', 'score']; - - $this->assertSame($output, $expectedOutput, 'Did not parse returnsFields as expected'); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertSame('*,score', $queryParameters['fl'], 'Did not parse returnsFields as expected'); } /** @@ -699,17 +687,12 @@ public function canAddReturnField() { $fakeConfigurationArray = []; $fakeConfiguration = new TypoScriptConfiguration($fakeConfigurationArray); - $query = $this->getInitializedTestSearchQuery('test', $fakeConfiguration); - - $expectedOutput = ['*', 'score']; - $this->assertSame($query->getReturnFields()->getValues(), $expectedOutput, 'Did not parse returnsFields as expected'); - - $query->getReturnFields()->add('title'); - $expectedOutput = ['score', 'title']; - - // why is the * removed from the fieldList - $this->assertSame($expectedOutput, $query->getReturnFields()->getValues(), 'Added return field was not in the list of valid fields'); + $returnFields = ReturnFields::fromString('url'); + $returnFields->add('title'); + $this->builder->startFrom($query)->useReturnFields($returnFields); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertSame('url,title', $queryParameters['fl'], 'Added return field was not in the list of valid fields'); } /** @@ -719,14 +702,13 @@ public function canRemoveReturnField() { $fakeConfigurationArray = []; $fakeConfiguration = new TypoScriptConfiguration($fakeConfigurationArray); - $initialReturnFieldList = ['title','content','url']; $query = $this->getInitializedTestSearchQuery('test', $fakeConfiguration); - $query->setReturnFields(ReturnFields::fromArray($initialReturnFieldList)); - $query->getReturnFields()->remove('content'); - - $expectedOutput = ['title', 'url']; - $this->assertSame($expectedOutput, $query->getReturnFields()->getValues(), 'content was not remove from the fieldList'); + $returnFields = ReturnFields::fromArray($initialReturnFieldList); + $returnFields->remove('content'); + $this->builder->startFrom($query)->useReturnFields($returnFields); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertSame('title,url', $queryParameters['fl'], 'content was not remove from the fieldList'); } /** @@ -734,11 +716,11 @@ public function canRemoveReturnField() */ public function canEnableFaceting() { - /** @var $query \ApacheSolrForTypo3\Solr\Domain\Search\Query\Query */ + /** @var $query \ApacheSolrForTypo3\Solr\Domain\Search\Query\SearchQuery */ $query = $this->getInitializedTestSearchQuery(); - $query->getFaceting()->setIsEnabled(true); - $queryParameters = $query->getQueryParameters(); - + $faceting = new Faceting(true); + $this->builder->startFrom($query)->useFaceting($faceting); + $queryParameters = $this->getAllQueryParameters($query); $this->assertSame('true', $queryParameters['facet'], 'Enable faceting did not set the "facet" query parameter'); } @@ -749,14 +731,20 @@ public function canDisableFaceting() { $query = $this->getInitializedTestSearchQuery(); - $query->getFaceting()->setIsEnabled(true); - $query->getFaceting()->addAdditionalParameter('f.title.facet.sort', 'lex'); + $faceting = new Faceting(true); + $faceting->addAdditionalParameter('f.title.facet.sort', 'lex'); + + $this->builder->startFrom($query)->useFaceting($faceting); + $queryParameters = $this->getAllQueryParameters($query); - $queryParameters = $query->getQueryParameters(); $this->assertSame('true', $queryParameters['facet'], 'Enable faceting did not set the "facet" query parameter'); $this->assertSame('lex', $queryParameters['f.title.facet.sort'], 'Facet sorting parameter should be lex'); - $query->getFaceting()->setIsEnabled(false); - $queryParameters = $query->getQueryParameters(); + + $faceting = new Faceting(false); + $faceting->addAdditionalParameter('f.title.facet.sort', 'lex'); + $this->builder->startFrom($query)->useFaceting($faceting); + + $queryParameters = $this->getAllQueryParameters($query); $this->assertNull($queryParameters['facet'], 'Facet argument should be null after reset'); $this->assertNull($queryParameters['f.title.facet.sort'], 'Facet sorting parameter should also be removed after reset'); } @@ -769,16 +757,18 @@ public function canAddFacetField() $fakeConfiguration = new TypoScriptConfiguration([]); $query = $this->getInitializedTestSearchQuery('test', $fakeConfiguration); - $facetFields = $query->getQueryParameter('facet.field'); - $this->assertNull($facetFields, 'facet.field query parameter was expected to be null after init.'); + $queryParameters = $query->getQueryParameters(); + $this->assertNull($queryParameters['facet.field'], 'facet.field query parameter was expected to be null after init.'); + $faceting = Faceting::fromTypoScriptConfiguration($fakeConfiguration); // after adding a few facet fields we should be able to retrieve them - $query->getFaceting()->setIsEnabled(true); - $query->getFaceting()->addField('color_s'); - $query->getFaceting()->addField('price_f'); + $faceting->setIsEnabled(true); + $faceting->addField('color_s'); + $faceting->addField('price_f'); - $facetFields = $query->getQueryParameter('facet.field'); - $this->assertSame(['color_s', 'price_f'], $facetFields, 'facet.field should not be empty after adding a few fields.'); + $this->builder->startFrom($query)->useFaceting($faceting); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertSame(['color_s', 'price_f'], $queryParameters['facet.field'], 'facet.field should not be empty after adding a few fields.'); } /** @@ -791,11 +781,14 @@ public function canSetFacetFields() $fakeFields = ['lastname_s', 'role_s']; - $query->getFaceting()->setIsEnabled(true); - $query->getFaceting()->setFields($fakeFields); - $retrievedFields = $query->getQueryParameter('facet.field'); + $faceting = Faceting::fromTypoScriptConfiguration($fakeConfiguration); + $faceting->setIsEnabled(true); + $faceting->setFields($fakeFields); + + $this->builder->startFrom($query)->useFaceting($faceting); + $queryParameters = $this->getAllQueryParameters($query); - $this->assertSame(['lastname_s', 'role_s'], $retrievedFields, 'Could not use setFacetFields to pass facet fields'); + $this->assertSame(['lastname_s', 'role_s'], $queryParameters['facet.field'], 'Could not use setFields to pass facet fields'); } /** @@ -810,8 +803,9 @@ public function canUseFacetMinCountFromConfiguration() $fakeConfiguration = new TypoScriptConfiguration($fakeConfigurationArray); $query = $this->getInitializedTestSearchQuery('test', $fakeConfiguration); - $query->getFaceting()->setIsEnabled(true); - $queryParameters = $query->getQueryParameters(); + $faceting = Faceting::fromTypoScriptConfiguration($fakeConfiguration); + $this->builder->startFrom($query)->useFaceting($faceting); + $queryParameters = $this->getAllQueryParameters($query); $this->assertSame(10, $queryParameters['facet.mincount'], 'Can not use facet.minimumCount from configuration'); } @@ -828,8 +822,9 @@ public function canUseFacetSortByFromConfiguration() $fakeConfiguration = new TypoScriptConfiguration($fakeConfigurationArray); $query = $this->getInitializedTestSearchQuery('test', $fakeConfiguration); - $query->getFaceting()->setIsEnabled(true); - $queryParameters = $query->getQueryParameters(); + $faceting = Faceting::fromTypoScriptConfiguration($fakeConfiguration); + $this->builder->startFrom($query)->useFaceting($faceting); + $queryParameters = $this->getAllQueryParameters($query); $this->assertSame('index', $queryParameters['facet.sort'], 'Can not use facet.sort from configuration'); } @@ -839,16 +834,21 @@ public function canUseFacetSortByFromConfiguration() */ public function canSetSpellChecking() { - /** @var $query \ApacheSolrForTypo3\Solr\Domain\Search\Query\Query */ + /** @var $query \ApacheSolrForTypo3\Solr\Domain\Search\Query\SearchQuery */ $query = $this->getInitializedTestSearchQuery(); - $query->getSpellchecking()->setIsEnabled(true); - $queryParameters = $query->getQueryParameters(); + + $spellchecking = Spellchecking::getEmpty(); + $spellchecking->setIsEnabled(true); + $this->builder->startFrom($query)->useSpellchecking($spellchecking); + $queryParameters = $this->getAllQueryParameters($query); $this->assertSame('true', $queryParameters['spellcheck'], 'Enable spellchecking did not set the "spellcheck" query parameter'); // can we unset it again? - $query->getSpellchecking()->setIsEnabled(false); - $queryParameters = $query->getQueryParameters(); + $spellchecking->setIsEnabled(false); + $this->builder->startFrom($query)->useSpellchecking($spellchecking); + + $queryParameters = $this->getAllQueryParameters($query); $this->assertNull($queryParameters['spellcheck'], 'Disable spellchecking did not unset the "spellcheck" query parameter'); $this->assertNull($queryParameters['spellcheck.maxCollationTries'], 'spellcheck.maxCollationTries was not unsetted'); } @@ -858,7 +858,7 @@ public function canSetSpellChecking() */ public function noSiteHashFilterIsSetWhenWildcardIsPassed() { - /** @var $query \ApacheSolrForTypo3\Solr\Domain\Search\Query\Query */ + /** @var $query \ApacheSolrForTypo3\Solr\Domain\Search\Query\SearchQuery */ $configurationMock = $this->getDumbMock(TypoScriptConfiguration::class); $configurationMock->expects($this->once())->method('getObjectByPathOrDefault')->willReturn(['allowedSites' => '*']); $this->siteHashServiceMock->expects($this->once())->method('getAllowedSitesForPageIdAndAllowedSitesConfiguration')->willReturn('*'); @@ -867,8 +867,9 @@ public function noSiteHashFilterIsSetWhenWildcardIsPassed() $query = $builder->buildSearchQuery(''); $query = $builder->startFrom($query)->useSiteHashFromTypoScript(4711)->getQuery(); - $filters = $query->getFilters()->getValues(); - $this->assertEmpty($filters, 'The filters should be empty when a wildcard sitehash was passed'); + $queryParameters = $this->getAllQueryParameters($query); + + $this->assertEmpty($queryParameters['fq'], 'The filters should be empty when a wildcard sitehash was passed'); } /** @@ -876,7 +877,7 @@ public function noSiteHashFilterIsSetWhenWildcardIsPassed() */ public function filterIsAddedWhenAllowedSiteIsPassed() { - /** @var $query \ApacheSolrForTypo3\Solr\Domain\Search\Query\Query */ + /** @var $query \ApacheSolrForTypo3\Solr\Domain\Search\Query\SearchQuery */ $configurationMock = $this->getDumbMock(TypoScriptConfiguration::class); $configurationMock->expects($this->once())->method('getObjectByPathOrDefault')->willReturn(['allowedSites' => 'site1.local']); @@ -887,12 +888,9 @@ public function filterIsAddedWhenAllowedSiteIsPassed() $query = $builder->buildSearchQuery(''); $query = $builder->startFrom($query)->useSiteHashFromTypoScript(4711)->getQuery(); + $queryParameters = $this->getAllQueryParameters($query); - $filters = $query->getFilters()->getValues(); - - $this->assertCount(1, $filters, 'We expected that one filter was added'); - $siteHashFilter = $filters['siteHash']; - $this->assertEquals('siteHash:"dsada43242342342"', $siteHashFilter, 'Unexpected siteHashFilter was added to the query'); + $this->assertEquals('siteHash:"dsada43242342342"', $queryParameters['fq'], 'Unexpected siteHashFilter was added to the query'); } /** @@ -911,8 +909,7 @@ public function canTestNumberOfSuggestionsToTryFromConfiguration() $builder = new QueryBuilder($fakeConfiguration, $this->loggerMock, $this->siteHashServiceMock); $builder->startFrom($query)->useSpellcheckingFromTypoScript(); - $queryParameters = $query->getQueryParameters(); - + $queryParameters = $this->getAllQueryParameters($query); $this->assertSame($input, $queryParameters['spellcheck.maxCollationTries'], 'Could not set spellcheck.maxCollationTries as expected'); } @@ -929,10 +926,8 @@ public function canUseConfiguredVariantsFieldWhenVariantsAreActive() $fakeConfiguration = new TypoScriptConfiguration($fakeConfigurationArray); $query = $this->getInitializedTestSearchQuery('test', $fakeConfiguration); - - $configuredField = $query->getFieldCollapsing()->getCollapseFieldName(); - $this->assertTrue($query->getFieldCollapsing()->getIsEnabled(), 'Collapsing was enabled but not indicated to be enabled'); - $this->assertSame('myField', $configuredField, 'Did not use the configured collapseField'); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertSame('{!collapse field=myField}', $queryParameters['fq'], 'Collapse filter query was not created'); } /** @@ -949,11 +944,9 @@ public function canUseConfiguredVariantsExpandAndRowCount() $fakeConfiguration = new TypoScriptConfiguration($fakeConfigurationArray); $query = $this->getInitializedTestSearchQuery('test', $fakeConfiguration); - - $arguments = $query->getQueryParameters(); - - $this->assertSame('true', $arguments['expand'], 'Expand argument of query was not set to true with configured expand'); - $this->assertSame(10, $arguments['expand.rows'], 'Expand.rows argument of query was not set to true with configured expand.rows'); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertSame('true', $queryParameters['expand'], 'Expand argument of query was not set to true with configured expand'); + $this->assertSame(10, $queryParameters['expand.rows'], 'Expand.rows argument of query was not set to true with configured expand.rows'); } /** @@ -967,11 +960,10 @@ public function expandRowsIsNotSetWhenExpandIsInactive() 'expand' => false, 'limit' => 10 ]; - $fakeConfiguration = new TypoScriptConfiguration($fakeConfigurationArray); $query = $this->getInitializedTestSearchQuery('test', $fakeConfiguration); - $arguments = $query->getQueryParameters(); - $this->assertNull($arguments['expand.rows'], 'Expand.rows should not be set when expand is set to false'); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertNull($queryParameters['expand.rows'], 'Expand.rows should not be set when expand is set to false'); } /** @@ -981,7 +973,8 @@ public function variantsAreDisabledWhenNothingWasConfigured() { $fakeConfiguration = new TypoScriptConfiguration([]); $query = $this->getInitializedTestSearchQuery('test', $fakeConfiguration); - $this->assertFalse($query->getFieldCollapsing()->getIsEnabled(), 'Collapsing was not disabled by default'); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertNull($queryParameters['fq'], 'No filter query should be generated when field collapsing is disbled'); } /** @@ -996,28 +989,6 @@ public function canConvertQueryToString() $this->assertSame('test', $queryToString, 'Could not convert query to string'); } - /** - * @test - */ - public function canSetCollapsing() - { - $fakeConfiguration = new TypoScriptConfiguration([]); - $query = $this->getInitializedTestSearchQuery('test', $fakeConfiguration); - $filters = $query->getFilters()->getValues(); - $this->assertNull($filters['collapsing'], 'No collapsing filter should be set without collpasing'); - - // can we enable collapsing - $query->getFieldCollapsing()->setIsEnabled(true); - - $filters = $query->getQueryParameters()['fq']; - $this->assertSame($filters[0], '{!collapse field=variantId}', 'No filter should be set without collpasing'); - - // can we disable it again - $query->getFieldCollapsing()->setIsEnabled(false); - $filters = $query->getQueryParameters()['fq']; - $this->assertNull($filters[0], 'No collapsing filter should be set after disables collpasing'); - } - /** * @test */ @@ -1027,28 +998,30 @@ public function canAddAndRemoveFilters() $query = $this->getInitializedTestSearchQuery('test', $fakeConfiguration); // can we add a filter? - $query->getFilters()->add('foo:bar'); - $filters = $query->getFilters()->getValues(); - $this->assertSame(['foo:bar'], $filters, 'Could not get filters from query object'); + $this->builder->startFrom($query)->useFilter('foo:bar'); + $parameters = $this->getAllQueryParameters($query); + $this->assertSame('foo:bar', $parameters['fq'], 'Could not get filters from query object'); // can we remove the filter after adding? - $query->getFilters()->removeByFieldName('foo'); - $filters = $query->getFilters()->getValues(); - $this->assertSame([], $filters, 'Could not remove filters from query object'); + $this->builder->startFrom($query)->removeFilterByFieldName('foo'); + $parameters = $this->getAllQueryParameters($query); + $this->assertNull($parameters['fq'], 'Could not remove filters from query object'); // can we add a new filter - $query->getFilters()->add('title:test'); - $filters = $query->getFilters()->getValues(); - $this->assertSame(['title:test'], array_values($filters), 'Could not get filters from query object'); + $this->builder->startFrom($query)->useFilter('title:test'); + $parameters = $this->getAllQueryParameters($query); + $this->assertSame('title:test', $parameters['fq'], 'Could not get filters from query object'); + $this->builder->startFrom($query)->removeFilterByFieldName('title'); // can we remove the filter by name? - $name = array_search('title:test', $filters); + $this->builder->startFrom($query)->useFilter('siteHash:xyz', 'siteHashFilter'); + $parameters = $this->getAllQueryParameters($query); + $this->assertSame('siteHash:xyz', $parameters['fq'], 'Could not get filters from query object'); + $this->builder->startFrom($query)->removeFilterByName('siteHashFilter'); - // @todo analyze this: why is the key different between php5.6 and php7 - $query->getFilters()->removeByName($name); - $filters = $query->getFilters()->getValues(); - $this->assertSame([], $filters, 'Could not remove filters from query object by filter key'); + $parameters = $this->getAllQueryParameters($query); + $this->assertNull($parameters['fq'], 'Could not remove filters from query object by filter key'); } /** @@ -1060,13 +1033,13 @@ public function canRemoveFilterByValue() $query = $this->getInitializedTestSearchQuery('test', $fakeConfiguration); // can we add a filter? - $query->getFilters()->add('foo:bar'); - $filters = $query->getFilters()->getValues(); - $this->assertSame(['foo:bar'], $filters, 'Could not get filters from query object'); + $this->builder->startFrom($query)->useFilter('foo:bar'); + $parameters = $this->getAllQueryParameters($query); + $this->assertSame('foo:bar', $parameters['fq'], 'Could not get filters from query object'); - $query->getFilters()->removeByValue('foo:bar'); - $filters = $query->getFilters()->getValues(); - $this->assertSame([], $filters, 'Filters are not empty after removing the last one'); + $this->builder->startFrom($query)->removeFilterByValue('foo:bar'); + $parameters = $this->getAllQueryParameters($query); + $this->assertNull($parameters['fq'], 'Filters are not empty after removing the last one'); } /** @@ -1075,17 +1048,17 @@ public function canRemoveFilterByValue() public function canSetAndUnSetQueryType() { $query = $this->getInitializedTestSearchQuery('test'); - $queryParameters = $query->getQueryParameters(); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertNull($queryParameters['qt'], 'The qt parameter was expected to be null'); - $query->setQueryType('dismax'); - $queryParameters = $query->getQueryParameters(); + $this->builder->startFrom($query)->useQueryType('dismax'); + $queryParameters = $this->getAllQueryParameters($query); $this->assertSame('dismax', $queryParameters['qt'], 'The qt parameter was expected to be dismax'); - $this->assertSame('dismax', $query->getQueryType(), 'getQueryType should return the qt queryParameter'); //passing false as parameter should reset the query type - $query->setQueryType(false); - $queryParameters = $query->getQueryParameters(); + $this->builder->startFrom($query)->removeQueryType(); + $queryParameters = $this->getAllQueryParameters($query); $this->assertNull($queryParameters['qt'], 'The qt parameter was expected to be null after reset'); } @@ -1099,16 +1072,16 @@ public function canSetOperator() $queryParameters = $query->getQueryParameters(); $this->assertNull($queryParameters['q.op'], 'The queryParameter q.op should be null because no operator was passed'); - $query->setOperator(Operator::getOr()); - $queryParameters = $query->getQueryParameters(); + $this->builder->startFrom($query)->useOperator(Operator::getOr()); + $queryParameters = $this->getAllQueryParameters($query); $this->assertEquals(Operator::OPERATOR_OR, $queryParameters['q.op'], 'The queryParameter q.op should be OR'); - $query->setOperator(Operator::getAnd()); - $queryParameters = $query->getQueryParameters(); + $this->builder->startFrom($query)->useOperator(Operator::getAnd()); + $queryParameters = $this->getAllQueryParameters($query); $this->assertEquals(Operator::OPERATOR_AND, $queryParameters['q.op'], 'The queryParameter q.op should be AND'); - $query->getOperator()->setIsEnabled(false); - $queryParameters = $query->getQueryParameters(); + $this->builder->startFrom($query)->removeOperator(); + $queryParameters = $this->getAllQueryParameters($query); $this->assertNull($queryParameters['q.op'], 'The queryParameter q.op should be null because operator was resetted'); } @@ -1119,15 +1092,20 @@ public function canSetAlternativeQuery() { // check initial value $query = $this->getInitializedTestSearchQuery('test'); - $this->assertNull($query->getAlternativeQuery(), 'We expected that alternative query is initially null'); + $queryParameters = $this->getAllQueryParameters($query); + + $this->assertNull($queryParameters['q.alt'], 'We expected that alternative query is initially null'); // can we set it? - $query->setAlternativeQuery('alt query'); - $this->assertEquals('alt query', $query->getAlternativeQuery(), 'Could not get passed alternative query'); + $this->builder->startFrom($query)->useAlternativeQuery('alt query'); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertEquals('alt query', $queryParameters['q.alt'], 'Could not get passed alternative query'); + // can we reset it? - $query->setAlternativeQuery(false); - $this->assertNull($query->getAlternativeQuery(), 'We expect alternative query is null after reset'); + $this->builder->startFrom($query)->removeAlternativeQuery(); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertNull($queryParameters['q.alt'], 'We expect alternative query is null after reset'); } /** @@ -1140,13 +1118,15 @@ public function canSetOmitHeaders() $queryParameters = $query->getQueryParameters(); $this->assertNull($queryParameters['omitHeader'], 'The queryParameter omitHeader should be null because it was not'); - $query->setOmitHeader(); - $queryParameters = $query->getQueryParameters(); + $this->builder->startFrom($query)->useOmitHeader(); + + $queryParameters = $this->getAllQueryParameters($query); $this->assertSame('true', $queryParameters['omitHeader'], 'The queryParameter omitHeader should be "true" because it was enabled'); - $query->setOmitHeader(false); - $queryParameters = $query->getQueryParameters(); - $this->assertNull($queryParameters['omitHeader'], 'The queryParameter omitHeader should be null because it was resetted'); + $this->builder->startFrom($query)->useOmitHeader(false); + + $queryParameters = $this->getAllQueryParameters($query); + $this->assertSame('false',$queryParameters['omitHeader'], 'The queryParameter omitHeader should be null because it was resetted'); } /** @@ -1156,15 +1136,18 @@ public function canSetReturnFields() { // check initial value $query = $this->getInitializedTestSearchQuery('test'); - $this->assertSame(['*', 'score'], $query->getReturnFields()->getValues(), 'FieldList initially contained unexpected values'); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertSame('*,score', $queryParameters['fl'], 'FieldList initially contained unexpected values'); // set from string - $query->setReturnFields(ReturnFields::fromString('content, title')); - $this->assertSame(['content', 'title'], $query->getReturnFields()->getValues(), 'Can not set fieldList from string'); + $this->builder->startFrom($query)->useReturnFields(ReturnFields::fromString('content, title')); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertSame('content,title', $queryParameters['fl'], 'Can not set fieldList from string'); // set from array - $query->setReturnFields(ReturnFields::fromArray(['content', 'title'])); - $this->assertSame(['content', 'title'], $query->getReturnFields()->getValues(), 'Can not set fieldList from array'); + $this->builder->startFrom($query)->useReturnFields(ReturnFields::fromArray(['content', 'title'])); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertSame('content,title', $queryParameters['fl'], 'Can not set fieldList from array'); } /** @@ -1174,23 +1157,23 @@ public function canSetSorting() { // check initial value $query = $this->getInitializedTestSearchQuery('test'); - $queryParameters = $query->getQueryParameters(); + $queryParameters = $this->getAllQueryParameters($query); $this->assertNull($queryParameters['sort'], 'Sorting should be null at the beginning'); // can set a field and direction combination - $query->setSorting('title desc'); - $queryParameters = $query->getQueryParameters(); + $this->builder->startFrom($query)->useSorting(Sorting::fromString('title desc')); + $queryParameters = $this->getAllQueryParameters($query); $this->assertSame('title desc', $queryParameters['sort'], 'Could not set sorting'); // can reset - $query->setSorting(false); - $queryParameters = $query->getQueryParameters(); + $this->builder->startFrom($query)->removeAllSortings(); + $queryParameters = $this->getAllQueryParameters($query); $this->assertNull($queryParameters['sort'], 'Sorting should be null after reset'); // when relevance is getting passed it is the same as we have no // sorting because this is a "virtual" value - $query->setSorting('relevance desc'); - $queryParameters = $query->getQueryParameters(); + $this->builder->startFrom($query)->useSorting(Sorting::fromString('relevance desc')); + $queryParameters = $this->getAllQueryParameters($query); $this->assertEquals('', $queryParameters['sort'], 'Sorting should be null after reset'); } @@ -1200,22 +1183,29 @@ public function canSetSorting() public function canSetQueryElevation() { $query = $this->getInitializedTestSearchQuery('test'); + $queryParameters = $this->getAllQueryParameters($query); - $this->assertNull($query->getQueryParameter('enableElevation')); - $this->assertNull($query->getQueryParameter('forceElevation')); - $this->assertNotContains('isElevated:[elevated]', $query->getReturnFields()->getValues()); + $this->assertNull($queryParameters['enableElevation']); + $this->assertNull($queryParameters['forceElevation']); + $this->assertNotContains('isElevated:[elevated]', $queryParameters['fl']); // do we get the expected default values, when calling setQueryElevantion with no arguments? - $query->getElevation()->setIsEnabled(true); - $this->assertSame('true', $query->getQueryParameter('enableElevation'), 'enabledElevation was not set after enabling elevation'); - $this->assertSame('true', $query->getQueryParameter('forceElevation'), 'forceElevation was not set after enabling elevation'); - $this->assertContains('isElevated:[elevated]', $query->getReturnFields()->getValues(), 'isElevated should be in the list of return fields'); + + $elevation = new Elevation(true); + $this->builder->startFrom($query)->useElevation($elevation); + $queryParameters = $this->getAllQueryParameters($query); + $this->assertSame('true', $queryParameters['enableElevation'], 'enabledElevation was not set after enabling elevation'); + $this->assertSame('true', $queryParameters['forceElevation'], 'forceElevation was not set after enabling elevation'); + $this->assertContains('isElevated:[elevated]', $queryParameters['fl'], 'isElevated should be in the list of return fields'); // can we reset the elevantion? - $query->getElevation()->setIsEnabled(false); - $this->assertNull($query->getQueryParameter('enableElevation')); - $this->assertNull($query->getQueryParameter('forceElevation')); - $this->assertNotContains('isElevated:[elevated]', $query->getReturnFields()->getValues()); + $elevation->setIsEnabled(false); + $this->builder->startFrom($query)->useElevation($elevation); + $queryParameters = $this->getAllQueryParameters($query); + + $this->assertNull($queryParameters['enableElevation']); + $this->assertNull($queryParameters['forceElevation']); + $this->assertSame('*,score',$queryParameters['fl']); } /** @@ -1224,14 +1214,27 @@ public function canSetQueryElevation() public function forceElevationIsFalseWhenForcingToFalse() { $query = $this->getInitializedTestSearchQuery('test'); - $this->assertNull($query->getQueryParameter('enableElevation')); - $this->assertNull($query->getQueryParameter('forceElevation')); + $queryParameters = $this->getAllQueryParameters($query); + + $this->assertNull($queryParameters['enableElevation']); + $this->assertNull($queryParameters['forceElevation']); - $query->getElevation()->setIsEnabled(true); - $query->getElevation()->setIsForced(false); + $elevation = new Elevation(); + $elevation->setIsEnabled(true); + $elevation->setIsForced(false); - $this->assertSame('true', $query->getQueryParameter('enableElevation'), 'enabledElevation was not set after enabling elevation'); - $this->assertSame('false', $query->getQueryParameter('forceElevation'), 'forceElevation was not false after forcing'); + $this->builder->startFrom($query)->useElevation($elevation); + $queryParameters = $this->getAllQueryParameters($query); + + $this->assertSame('true', $queryParameters['enableElevation'], 'enabledElevation was not set after enabling elevation'); + $this->assertSame('false', $queryParameters['forceElevation'], 'forceElevation was not false after forcing'); + + $elevation->setIsEnabled(false); + $this->builder->startFrom($query)->useElevation($elevation); + $queryParameters = $this->getAllQueryParameters($query); + + $this->assertNull($queryParameters['enableElevation']); + $this->assertNull($queryParameters['forceElevation']); } /** @@ -1239,22 +1242,27 @@ public function forceElevationIsFalseWhenForcingToFalse() */ public function canBuildExpectedQueryUrlFromCombinedQuery() { - $query = $this->getInitializedTestSearchQuery('hello world'); + $faceting = new Faceting(true); + $faceting->addField('content'); + $faceting->addField('type'); + $faceting->addField('color'); - $query->getFaceting()->setIsEnabled(true); - $query->getFaceting()->addField('content'); - $query->getFaceting()->addField('type'); - $query->getFilters()->add('color:red'); - $query->getReturnFields()->add('title'); - $query->getFaceting()->addField('color'); - $query->getFieldCollapsing()->setIsEnabled(true); - $query->getPagination()->setPage(3); + $returnFields = new ReturnFields(); + $returnFields->add('title'); + + $fieldCollapsing = new FieldCollapsing(true); $queryBuilder = new QueryBuilder($this->configurationMock, $this->loggerMock); - $queryBuilder->startFrom($query)->useUserAccessGroups([1,2,3]); + $query = $queryBuilder->newSearchQuery('hello world') + ->useFilter('color:red') + ->useUserAccessGroups([1,2,3]) + ->useFaceting($faceting) + ->useReturnFields($returnFields) + ->useFieldCollapsing($fieldCollapsing) + ->getQuery(); - $parameters = $query->getQueryParameters(); - $this->assertSame('score,title', $parameters['fl']); + $parameters = $this->getAllQueryParameters($query); + $this->assertSame('title', $parameters['fl']); $filterQueries = $parameters['fq']; $this->assertCount(3, $filterQueries, 'Unexpected amount of filter queries created'); @@ -1275,11 +1283,12 @@ public function canBuildExpectedQueryUrlFromCombinedQuery() public function canSetQueryFieldsFromString() { $query = $this->getInitializedTestSearchQuery('foo bar'); - $query->setQueryFields(QueryFields::fromString('content^100.0, title^10.0')); - $queryFields = $query->getQueryFields()->toString(); + $this->builder->startFrom($query)->useQueryFields(QueryFields::fromString('content^100.0, title^10.0')); + + $parameters = $this->getAllQueryParameters($query); // the , delimiter is removed - $this->assertSame('content^100.0 title^10.0', $queryFields, 'Can not set and get query fields'); + $this->assertSame('content^100.0 title^10.0', $parameters['qf'], 'Can not set and get query fields'); } /** @@ -1288,17 +1297,22 @@ public function canSetQueryFieldsFromString() public function canSetQueryFields() { $query = $this->getInitializedTestSearchQuery('foo bar'); - $this->assertSame('', $query->getQueryFields()->toString(), 'QueryFields are not empty by default'); + $parameters = $this->getAllQueryParameters($query); - $query->getQueryFields()->set('content', 10); - $query->getQueryFields()->set('title', 11); + $this->assertEmpty($parameters['qf'], 'QueryFields are not empty by default'); - $this->assertSame('content^10.0 title^11.0', $query->getQueryFields()->toString()); + $queryFields = new QueryFields([]); + $queryFields->set('content', 10); + $queryFields->set('title', 11); - // overwrite the boost of title - $query->getQueryFields()->set('title', 9); + $this->builder->startFrom($query)->useQueryFields($queryFields); + $parameters = $this->getAllQueryParameters($query); + $this->assertSame('content^10.0 title^11.0', $parameters['qf']); - $parameters = $query->getQueryParameters(); + // overwrite the boost of title + $queryFields->set('title', 9); + $this->builder->startFrom($query)->useQueryFields($queryFields); + $parameters = $this->getAllQueryParameters($query); $this->assertSame('content^10.0 title^9.0', $parameters['qf'], 'qf parameter not set in QueryParameters'); } @@ -1308,11 +1322,10 @@ public function canSetQueryFields() public function canSetPhraseFieldsFromString() { $query = $this->getInitializedTestSearchQuery('foo bar'); - $query->setPhraseFields(PhraseFields::fromString('content^100.0, title^10.0')); - $phraseFields = $query->getPhraseFields()->toString(); - + $this->builder->startFrom($query)->usePhraseFields(PhraseFields::fromString('content^100.0, title^10.0')); + $parameters = $this->getAllQueryParameters($query); // the , delimiter is removed - $this->assertSame('content^100.0 title^10.0', $phraseFields, 'Can not set and get phrase fields'); + $this->assertSame('content^100.0 title^10.0', $parameters['pf'], 'Can not set and get phrase fields'); } /** @@ -1321,16 +1334,24 @@ public function canSetPhraseFieldsFromString() public function canSetPhraseFields() { $query = $this->getInitializedTestSearchQuery('foo bar'); - $this->assertSame('', $query->getPhraseFields()->toString(), 'Phrase Fields must be empty by default'); + $parameters = $this->getAllQueryParameters($query); + + $this->assertEmpty($parameters['pf'], 'Phrase Fields must be empty by default'); - $query->getPhraseFields()->add('content', 10); - $query->getPhraseFields()->add('title', 11); + $phraseFields = new PhraseFields(true); + $phraseFields->add('content', 10); + $phraseFields->add('title', 11); - $this->assertSame('content^10.0 title^11.0', $query->getPhraseFields()->toString()); + $this->builder->startFrom($query)->usePhraseFields($phraseFields); + $parameters = $this->getAllQueryParameters($query); + $this->assertSame('content^10.0 title^11.0', $parameters['pf']); // overwrite the boost of title - $query->getPhraseFields()->add('title', 9); - $this->assertSame('content^10.0 title^9.0', $query->getPhraseFields()->toString()); + $phraseFields->add('title', 9); + $this->builder->startFrom($query)->usePhraseFields($phraseFields); + $parameters = $this->getAllQueryParameters($query); + + $this->assertSame('content^10.0 title^9.0', $parameters['pf']); } /** @@ -1339,9 +1360,12 @@ public function canSetPhraseFields() public function phraseFieldsAreNotSetInUrlQueryIfPhraseSearchIsDisabled() { $query = $this->getInitializedTestSearchQuery('foo bar'); - $query->getPhraseFields()->add('content', 10); - $query->getPhraseFields()->add('title', 11); - $parameters = $query->getQueryParameters(); + + $phraseFields = new PhraseFields(false); + $phraseFields->add('content', 10); + $phraseFields->add('title', 11); + $this->builder->startFrom($query)->usePhraseFields($phraseFields); + $parameters = $this->getAllQueryParameters($query); $this->assertNull($parameters['pf'], 'pf parameter must be empty(not set) if phrase search is disabled'); } @@ -1355,9 +1379,11 @@ public function phraseFieldsAreSetInUrlQueryIfPhraseSearchIsEnabled() $fakeConfiguration = new TypoScriptConfiguration($fakeConfigurationArray); $query = $this->getInitializedTestSearchQuery('foo bar', $fakeConfiguration); - $query->getPhraseFields()->add('content', 10); - $query->getPhraseFields()->add('title', 11); - $parameters = $query->getQueryParameters(); + $phraseFields = new PhraseFields(true); + $phraseFields->add('content', 10); + $phraseFields->add('title', 11); + $this->builder->startFrom($query)->usePhraseFields($phraseFields); + $parameters = $this->getAllQueryParameters($query); $this->assertSame('content^10.0 title^11.0', $parameters['pf'], 'pf parameters must be set if phrase search is enabled'); } @@ -1373,7 +1399,7 @@ public function canAddPhraseFieldsFromConfiguration() $fakeConfiguration = new TypoScriptConfiguration($fakeConfigurationArray); $query = $this->getInitializedTestSearchQuery('foo bar', $fakeConfiguration); - $parameters = $query->getQueryParameters(); + $parameters = $this->getAllQueryParameters($query); $this->assertSame('content^22.0 title^11.0', $parameters['pf'], 'pf parameters must be set if phrase search is enabled'); } @@ -1383,9 +1409,11 @@ public function canAddPhraseFieldsFromConfiguration() public function bigramPhraseFieldsAreNotSetInUrlQueryIfBigramPhraseSearchIsDisabled() { $query = $this->getInitializedTestSearchQuery('foo bar baz'); - $query->getBigramPhraseFields()->add('content', 10); - $query->getBigramPhraseFields()->add('title', 11); - $parameters = $query->getQueryParameters(); + $phraseFields = new BigramPhraseFields(false); + $phraseFields->add('content', 10); + $phraseFields->add('title', 11); + $this->builder->startFrom($query)->useBigramPhraseFields($phraseFields); + $parameters = $this->getAllQueryParameters($query); $this->assertNull($parameters['pf2'], 'pf2 parameter must be empty(not set) if phrase search is disabled'); } @@ -1399,9 +1427,11 @@ public function canAddBigramFieldsWhenBigramPhraseIsEnabled() $fakeConfiguration = new TypoScriptConfiguration($fakeConfigurationArray); $query = $this->getInitializedTestSearchQuery('foo bar', $fakeConfiguration); - $query->getBigramPhraseFields()->add('content', 10); - $query->getBigramPhraseFields()->add('title', 11); - $parameters = $query->getQueryParameters(); + $phraseFields = BigramPhraseFields::fromTypoScriptConfiguration($fakeConfiguration); + $phraseFields->add('content', 10); + $phraseFields->add('title', 11); + $this->builder->startFrom($query)->useBigramPhraseFields($phraseFields); + $parameters = $this->getAllQueryParameters($query); $this->assertSame('content^10.0 title^11.0', $parameters['pf2'], 'pf2 parameters must be set if bigram phrase search is enabled'); } @@ -1417,7 +1447,7 @@ public function canAddBigramFieldsFromConfiguration() $fakeConfiguration = new TypoScriptConfiguration($fakeConfigurationArray); $query = $this->getInitializedTestSearchQuery('foo bar', $fakeConfiguration); - $parameters = $query->getQueryParameters(); + $parameters = $this->getAllQueryParameters($query); $this->assertSame('content^12.0 title^14.0', $parameters['pf2'], 'pf2 parameters must be set if bigram phrase search is enabled'); } @@ -1427,9 +1457,11 @@ public function canAddBigramFieldsFromConfiguration() public function trigramPhraseFieldsAreNotSetInUrlQueryIfTrigramPhraseSearchIsDisabled() { $query = $this->getInitializedTestSearchQuery('foo bar baz foobar barbaz'); - $query->getTrigramPhraseFields()->add('content', 10); - $query->getTrigramPhraseFields()->add('title', 11); - $parameters = $query->getQueryParameters(); + $phraseFields = new TrigramPhraseFields(false); + $phraseFields->add('content', 10); + $phraseFields->add('title', 11); + $this->builder->startFrom($query)->useTrigramPhraseFields($phraseFields); + $parameters = $this->getAllQueryParameters($query); $this->assertNull($parameters['pf3'], 'pf3 parameter must be empty(not set) if phrase search is disabled'); } @@ -1442,10 +1474,11 @@ public function trigramPhraseFieldsAreSetInUrlQueryIfTrigramPhraseSearchIsEnable $fakeConfigurationArray['plugin.']['tx_solr.']['search.']['query.']['trigramPhrase'] = 1; $fakeConfiguration = new TypoScriptConfiguration($fakeConfigurationArray); $query = $this->getInitializedTestSearchQuery('foo bar', $fakeConfiguration); - - $query->getTrigramPhraseFields()->add('content', 10); - $query->getTrigramPhraseFields()->add('title', 11); - $parameters = $query->getQueryParameters(); + $trigram = TrigramPhraseFields::fromTypoScriptConfiguration($fakeConfiguration); + $trigram->add('content', 10); + $trigram->add('title', 11); + $this->builder->startFrom($query)->useTrigramPhraseFields($trigram); + $parameters = $this->getAllQueryParameters($query); $this->assertSame('content^10.0 title^11.0', $parameters['pf3'], 'pf3 parameters must be set if trigram phrase search is enabled'); } @@ -1457,11 +1490,9 @@ public function canAddTrigramFieldsFromConfiguration() $fakeConfigurationArray = []; $fakeConfigurationArray['plugin.']['tx_solr.']['search.']['query.']['trigramPhrase'] = 1; $fakeConfigurationArray['plugin.']['tx_solr.']['search.']['query.']['trigramPhrase.']['fields'] = 'content^12.0, title^14.0'; - $fakeConfiguration = new TypoScriptConfiguration($fakeConfigurationArray); $query = $this->getInitializedTestSearchQuery('foo bar', $fakeConfiguration); - - $parameters = $query->getQueryParameters(); + $parameters = $this->getAllQueryParameters($query); $this->assertSame('content^12.0 title^14.0', $parameters['pf3'], 'pf3 parameters must be set if trigram phrase search is enabled'); } @@ -1472,72 +1503,34 @@ public function setDebugMode() { $query = $this->getInitializedTestSearchQuery(); - $parameter = $query->getQueryParameters(); + $parameter = $this->getAllQueryParameters($query); $this->assertEmpty($parameter['debugQuery'], 'Debug query should be disabled by default'); $this->assertEmpty($parameter['echoParams'], 'Debug query should be disabled by default'); - $query->setDebugMode(); + $this->builder->startFrom($query)->useDebug(true); + $parameter = $this->getAllQueryParameters($query); - $parameter = $query->getQueryParameters(); $this->assertSame('true', $parameter['debugQuery'], 'Debug query should be disabled by default'); $this->assertSame('all', $parameter['echoParams'], 'Debug query should be disabled by default'); - $query->setDebugMode(false); - $parameter = $query->getQueryParameters(); + $this->builder->startFrom($query)->useDebug(false); + $parameter = $this->getAllQueryParameters($query); $this->assertEmpty($parameter['debugQuery'], 'Can not unset debug mode'); $this->assertEmpty($parameter['echoParams'], 'Can not unset debug mode'); } - /** - * @test - */ - public function canGetResultsPerPage() - { - $query = $this->getInitializedTestSearchQuery('foo bar'); - - $query->getPagination()->setResultsPerPage(12); - $this->assertSame(12, $query->getPagination()->getResultsPerPage()); - - $query->getPagination()->setResultsPerPage(-1); - $this->assertSame(0, $query->getPagination()->getResultsPerPage()); - - $query->getPagination()->setResultsPerPage(10); - } - - /** - * @test - */ - public function canGetRowsFromQuery() - { - $query = $this->getInitializedTestSearchQuery('foo bar'); - - $query->getPagination()->setResultsPerPage(12); - $this->assertSame(12, $query->getRows()); - - $query->getPagination()->setResultsPerPage(-1); - $this->assertSame(0, $query->getRows()); - - $query->getPagination()->setResultsPerPage(10); - - // setNumberOfGroups implicitly changes the results per page since the row argument is used for the number of groups - $query->getGrouping()->setIsEnabled(true); - $query->getGrouping()->setNumberOfGroups(5); - $this->assertSame(5, $query->getRows()); - $this->assertSame(10, $query->getPagination()->getResultsPerPage(), 'Paginated results per Page should be 10 as set before'); - } - - /** * @test */ public function addingQueriesToGroupingAddsToRightGroupingParameter() { $query = $this->getInitializedTestSearchQuery('group test'); - $query->getGrouping()->setIsEnabled(true); - $query->getGrouping()->addQuery('price:[* TO 500]'); - $query->getGrouping()->addQuery('someField:someValue'); - $parameters = $query->getQueryParameters(); + $grouping = new Grouping(true); + $grouping->addQuery('price:[* TO 500]'); + $grouping->addQuery('someField:someValue'); + $this->builder->startFrom($query)->useGrouping($grouping); + $parameters = $this->getAllQueryParameters($query); $this->assertSame(['price:[* TO 500]', 'someField:someValue'], $parameters['group.query'], 'Could not add group queries properly'); } @@ -1547,12 +1540,12 @@ public function addingQueriesToGroupingAddsToRightGroupingParameter() public function addingSortingsToGroupingAddsToRightGroupingParameter() { $query = $this->getInitializedTestSearchQuery('group test'); - $query->getGrouping()->setIsEnabled(true); - $query->getGrouping()->addSorting('price_f'); - $query->getGrouping()->addSorting('title desc'); - - $parameters = $query->getQueryParameters(); - $this->assertSame(['price_f', 'title desc'], $parameters['group.sort'], 'Could not add group sortings properly'); + $grouping = new Grouping(true); + $grouping->addSorting('price_f'); + $grouping->addSorting('title desc'); + $this->builder->startFrom($query)->useGrouping($grouping); + $parameters = $this->getAllQueryParameters($query); + $this->assertSame('price_f title desc', $parameters['group.sort'], 'Could not add group sortings properly'); } /** @@ -1561,11 +1554,11 @@ public function addingSortingsToGroupingAddsToRightGroupingParameter() public function addingFieldsToGroupingAddsToRightGroupingParameter() { $query = $this->getInitializedTestSearchQuery('group test'); - $query->getGrouping()->setIsEnabled(true); - $query->getGrouping()->addField('price_f'); - $query->getGrouping()->addField('category_s'); - - $parameters = $query->getQueryParameters(); + $grouping = new Grouping(true); + $grouping->addField('price_f'); + $grouping->addField('category_s'); + $this->builder->startFrom($query)->useGrouping($grouping); + $parameters = $this->getAllQueryParameters($query); $this->assertSame(['price_f', 'category_s'], $parameters['group.field'], 'Could not add group fields properly'); } @@ -1575,22 +1568,26 @@ public function addingFieldsToGroupingAddsToRightGroupingParameter() public function canDisablingGroupingRemoveTheGroupSorting() { $query = $this->getInitializedTestSearchQuery('foo bar'); - $query->getGrouping()->setIsEnabled(true); - $parameters = $query->getQueryParameters(); + $grouping = new Grouping(true); + $this->builder->startFrom($query)->useGrouping($grouping); + $parameters = $this->getAllQueryParameters($query); $this->assertSame($parameters['group'], 'true'); $this->assertSame($parameters['group.format'], 'grouped'); $this->assertSame($parameters['group.ngroups'], 'true'); - $query->getGrouping()->addSorting('title desc'); - $parameters = $query->getQueryParameters(); - $this->assertSame($parameters['group.sort'][0], 'title desc', 'Group sorting was not added'); + $grouping->addSorting('title desc'); + $this->builder->startFrom($query)->useGrouping($grouping); + $parameters = $this->getAllQueryParameters($query); + + $this->assertSame($parameters['group.sort'], 'title desc', 'Group sorting was not added'); $this->assertEmpty($parameters['group.field'], 'No field was passed, so it should not be set'); $this->assertEmpty($parameters['group.query'], 'No query was passed, so it should not be set'); - $query->getGrouping()->setIsEnabled(false); - $parameters = $query->getQueryParameters(); + $grouping->setIsEnabled(false); + $this->builder->startFrom($query)->useGrouping($grouping); + $parameters = $this->getAllQueryParameters($query); $this->assertEmpty($parameters['group.sort'], 'Grouping parameters should be removed'); $this->assertEmpty($parameters['group'], 'Grouping parameters should be removed'); @@ -1609,7 +1606,7 @@ public function canBuildSuggestQuery() ->getMock(); $suggestQuery = $this->builder->buildSuggestQuery('foo', [], 3232, ''); - $queryParameters = $suggestQuery->getQueryParameters(); + $queryParameters = $this->getAllQueryParameters($suggestQuery); $this->assertSame('foo', $queryParameters['facet.prefix'], 'Passed query string is not used as facet.prefix argument'); } @@ -1618,13 +1615,13 @@ public function canBuildSuggestQuery() */ public function alternativeQueryIsWildCardQueryForSuggestQuery() { - $this->builder = $this->getMockBuilder(QueryBuilder::class) ->setConstructorArgs([$this->configurationMock, $this->loggerMock]) ->setMethods(['useSiteHashFromTypoScript']) ->getMock(); $suggestQuery = $this->builder->buildSuggestQuery('bar', [], 3232, ''); - $this->assertSame('*:*', $suggestQuery->getAlternativeQuery(), 'Alterntive query is not set to wildcard query by default'); + $queryParameters = $this->getAllQueryParameters($suggestQuery); + $this->assertSame('*:*', $queryParameters['q.alt'], 'Alterntive query is not set to wildcard query by default'); } } \ No newline at end of file diff --git a/Tests/Unit/Domain/Search/Query/SuggestQueryTest.php b/Tests/Unit/Domain/Search/Query/SuggestQueryTest.php index 0ebc784d59..03d1ddbc5c 100644 --- a/Tests/Unit/Domain/Search/Query/SuggestQueryTest.php +++ b/Tests/Unit/Domain/Search/Query/SuggestQueryTest.php @@ -25,6 +25,7 @@ ***************************************************************/ use ApacheSolrForTypo3\Solr\Domain\Search\Query\Helper\EscapeService; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; use ApacheSolrForTypo3\Solr\Domain\Search\Query\SuggestQuery; use ApacheSolrForTypo3\Solr\Domain\Site\SiteHashService; use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; @@ -49,8 +50,10 @@ public function testSuggestQueryDoesNotUseFieldCollapsing() ]; $fakeConfiguration = new TypoScriptConfiguration($fakeConfigurationArray); - $suggestQuery = new SuggestQuery('typ', $fakeConfiguration); - $this->assertFalse($suggestQuery->getFieldCollapsing()->getIsEnabled(), 'Collapsing should never be active for a suggest query, even when active'); + $queryBuilder = new QueryBuilder($fakeConfiguration); + $suggestQuery = $queryBuilder->newSuggestQuery('type')->getQuery(); + + $this->assertNull($suggestQuery->getFilterQuery('fieldCollapsing'), 'Collapsing should never be active for a suggest query, even when active'); } /** @@ -60,8 +63,10 @@ public function testSuggestQueryUsesFilterList() { $fakeConfiguration = new TypoScriptConfiguration([]); $suggestQuery = new SuggestQuery('typ', $fakeConfiguration); - $suggestQuery->getFilters()->add('+type:pages'); - $queryParameters = $suggestQuery->getQueryParameters(); - $this->assertSame('+type:pages', $queryParameters['fq'][0], 'Filter was not added to the suggest query parameters'); + + $queryBuilder = new QueryBuilder($fakeConfiguration); + $queryBuilder->startFrom($suggestQuery)->useFilter('+type:pages'); + $queryParameters = $suggestQuery->getRequestBuilder()->build($suggestQuery)->getParams(); + $this->assertSame('+type:pages', $queryParameters['fq'], 'Filter was not added to the suggest query parameters'); } } diff --git a/Tests/Unit/Domain/Search/ResultSet/SearchResultSetTest.php b/Tests/Unit/Domain/Search/ResultSet/SearchResultSetTest.php index 3e2996a930..b708b76a7f 100644 --- a/Tests/Unit/Domain/Search/ResultSet/SearchResultSetTest.php +++ b/Tests/Unit/Domain/Search/ResultSet/SearchResultSetTest.php @@ -36,9 +36,7 @@ use ApacheSolrForTypo3\Solr\Search\SpellcheckingComponent; use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager; -use ApacheSolrForTypo3\Solr\System\Session\FrontendUserSession; use ApacheSolrForTypo3\Solr\Tests\Unit\UnitTest; -use TYPO3\CMS\Frontend\Plugin\AbstractPlugin; /** @@ -57,11 +55,6 @@ class SearchResultSetTest extends UnitTest */ protected $searchMock; - /** - * @var AbstractPlugin - */ - protected $pluginMock; - /** * @var SearchResultSetService */ @@ -101,16 +94,9 @@ public function setUp() $this->escapeServiceMock->expects($this->any())->method('escape')->will($this->returnArgument(0)); $this->searchResultSetService = $this->getMockBuilder(SearchResultSetService::class) - ->setMethods(['getRegisteredSearchComponents', 'getQueryInstance']) + ->setMethods(['getRegisteredSearchComponents']) ->setConstructorArgs([$this->configurationMock, $this->searchMock, $this->solrLogManagerMock]) ->getMock(); - - // @todo we should fake the result of getQueryInstance with a mock and move the tests that test Query partly into the QueryTest - $this->searchResultSetService->expects($this->any())->method('getQueryInstance')->will( - $this->returnCallback(function($queryString){ - return new Query($queryString, $this->configurationMock, $this->siteHashServiceMock, $this->escapeServiceMock, $this->solrLogManagerMock); - }) - ); } /** @@ -244,8 +230,7 @@ public function testAdditionalFiltersGetPassedToTheQuery() $resultSet = $this->searchResultSetService->search($fakeRequest); $this->assertSame($resultSet->getResponse(), $fakeResponse, 'Did not get the expected fakeResponse'); - - $this->assertSame(count($resultSet->getUsedQuery()->getFilters()->getValues()), 1, 'There should be one registered filter in the query'); + $this->assertSame(count($resultSet->getUsedQuery()->getFilterQueries()), 1, 'There should be one registered filter in the query'); } /** @@ -291,7 +276,7 @@ public function assertOneSearchWillBeTriggeredWithQueryAndShouldReturnFakeRespon $this->returnCallback( function(Query $query, $offset) use($expextedQueryString, $expectedOffset, $fakeResponse) { - $this->assertSame($expextedQueryString, $query->getQueryStringContainer()->getKeywords() , "Search was not triggered with an expected queryString"); + $this->assertSame($expextedQueryString, $query->getQuery() , "Search was not triggered with an expected queryString"); $this->assertSame($expectedOffset, $offset); return $fakeResponse; } diff --git a/Tests/Unit/Domain/Search/Statistics/StatisticsWriterProcessorTest.php b/Tests/Unit/Domain/Search/Statistics/StatisticsWriterProcessorTest.php index 20ca87c2b5..96c3e6b6a3 100644 --- a/Tests/Unit/Domain/Search/Statistics/StatisticsWriterProcessorTest.php +++ b/Tests/Unit/Domain/Search/Statistics/StatisticsWriterProcessorTest.php @@ -92,9 +92,7 @@ public function canWriteExpectedStatisticsData() $this->typoScriptConfigurationMock->expects($this->once())->method('getStatisticsAnonymizeIP')->will($this->returnValue(0)); $this->searchRequestMock->expects($this->once())->method('getContextTypoScriptConfiguration')->will($this->returnValue($this->typoScriptConfigurationMock)); - $queryStringsMock = $this->getDumbMock(QueryStringContainer::class); - $queryStringsMock->expects($this->once())->method('getKeywords')->willReturn('my search'); - $this->queryMock->expects($this->once())->method('getQueryStringContainer')->willReturn($queryStringsMock); + $this->queryMock->expects($this->once())->method('getQuery')->willReturn('my search'); $resultSetMock = $this->getDumbMock(SearchResultSet::class); $resultSetMock->expects($this->once())->method('getUsedQuery')->will($this->returnValue($this->queryMock)); diff --git a/Tests/Unit/Domain/Search/Suggest/SuggestServiceTest.php b/Tests/Unit/Domain/Search/Suggest/SuggestServiceTest.php index 2e5f045709..1521d0d559 100644 --- a/Tests/Unit/Domain/Search/Suggest/SuggestServiceTest.php +++ b/Tests/Unit/Domain/Search/Suggest/SuggestServiceTest.php @@ -97,7 +97,7 @@ public function setUp() */ protected function assertSuggestQueryWithQueryStringCreated($queryString) { - $this->suggestQueryMock->expects($this->any())->method('getQueryStringContainer')->willReturn(new QueryStringContainer($queryString)); + $this->suggestQueryMock->expects($this->any())->method('getQuery')->willReturn($queryString); } /** diff --git a/Tests/Unit/Query/Modifier/ElevationTest.php b/Tests/Unit/Query/Modifier/ElevationTest.php index ae1f5f5b81..4fd8e5eefd 100644 --- a/Tests/Unit/Query/Modifier/ElevationTest.php +++ b/Tests/Unit/Query/Modifier/ElevationTest.php @@ -24,18 +24,10 @@ * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; -use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Facets\FacetQueryBuilderRegistry; -use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Facets\FacetRegistry; -use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Facets\FacetUrlDecoderRegistry; -use ApacheSolrForTypo3\Solr\Domain\Search\SearchRequest; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; use ApacheSolrForTypo3\Solr\Query\Modifier\Elevation; -use ApacheSolrForTypo3\Solr\Query\Modifier\Faceting; -use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; use ApacheSolrForTypo3\Solr\Tests\Unit\UnitTest; -use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Extbase\Object\ObjectManager; /** * Tests the ApacheSolrForTypo3\Solr\Query\Modifier\Elevation class diff --git a/Tests/Unit/Query/Modifier/FacetingTest.php b/Tests/Unit/Query/Modifier/FacetingTest.php index 467bd4f006..72b07139d0 100644 --- a/Tests/Unit/Query/Modifier/FacetingTest.php +++ b/Tests/Unit/Query/Modifier/FacetingTest.php @@ -31,6 +31,7 @@ use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager; use ApacheSolrForTypo3\Solr\Tests\Unit\UnitTest; +use Solarium\QueryType\Select\RequestBuilder; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Object\ObjectManager; @@ -68,8 +69,11 @@ private function getQueryParametersFromExecutedFacetingModifier($fakeConfigurati $facetModifier->setSearchRequest($fakeSearchRequest); $facetModifier->modifyQuery($query); - $queryParameter = $query->getQueryParameters(); - return $queryParameter; + $requestBuilder = new RequestBuilder(); + + $request = $requestBuilder->build($query); + + return $request->getParams(); } /** diff --git a/Tests/Unit/Search/RelevanceComponentTest.php b/Tests/Unit/Search/RelevanceComponentTest.php index 1ab816d212..321b764cb6 100644 --- a/Tests/Unit/Search/RelevanceComponentTest.php +++ b/Tests/Unit/Search/RelevanceComponentTest.php @@ -24,10 +24,11 @@ * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ -use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; use ApacheSolrForTypo3\Solr\Domain\Search\Query\QueryBuilder; +use ApacheSolrForTypo3\Solr\Domain\Search\Query\Query; use ApacheSolrForTypo3\Solr\Search\RelevanceComponent; use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration; +use Solarium\QueryType\Select\RequestBuilder; /** * Testcase for RelevanceComponent @@ -36,6 +37,18 @@ */ class RelevanceComponentTest extends UnitTest { + + /** + * @param $query + * @return array + */ + protected function getQueryParameters($query) + { + $requestBuilder = new RequestBuilder(); + $request = $requestBuilder->build($query); + return $request->getParams(); + } + /** * @test */ @@ -54,13 +67,14 @@ public function canSetQuerySlop() $queryBuilder = new QueryBuilder($typoscriptConfiguration); $relevanceComponent = new RelevanceComponent($queryBuilder); - $query = new Query('test'); - $this->assertNull($query->getQueryParameter('qs')); + $query = new Query(); + $query->setQuery('test'); + $this->assertNull($this->getQueryParameters($query)['qs']); $relevanceComponent->setQuery($query); $relevanceComponent->initializeSearchComponent(); - $this->assertSame(2, $query->getQueryParameter('qs'), 'querySlop was not applied as qs parameter'); + $this->assertSame(2, $this->getQueryParameters($query)['qs'], 'querySlop was not applied as qs parameter'); } /** @@ -80,13 +94,14 @@ public function querySlopIsNotSetWhenPhraseIsDisabled() $queryBuilder = new QueryBuilder($typoscriptConfiguration); $relevanceComponent = new RelevanceComponent($queryBuilder); - $query = new Query('test'); - $this->assertNull($query->getQueryParameter('qs')); + $query = new Query(); + $query->setQuery('test'); + $this->assertNull($this->getQueryParameters($query)['qs']); $relevanceComponent->setQuery($query); $relevanceComponent->initializeSearchComponent(); - $this->assertNull($query->getQueryParameter('qs'), 'querySlop should still be null because phrase is disabled'); + $this->assertNull($this->getQueryParameters($query)['qs'], 'querySlop should still be null because phrase is disabled'); } /** @@ -107,13 +122,14 @@ public function canSetSlop() $queryBuilder = new QueryBuilder($typoscriptConfiguration); $relevanceComponent = new RelevanceComponent($queryBuilder); - $query = new Query('test'); - $this->assertNull($query->getQueryParameter('ps')); + $query = new Query(); + $query->setQuery('test'); + $this->assertNull($this->getQueryParameters($query)['ps']); $relevanceComponent->setQuery($query); $relevanceComponent->initializeSearchComponent(); - $this->assertSame(3, $query->getQueryParameter('ps'), 'slop was not applied as qs parameter'); + $this->assertSame(3, $this->getQueryParameters($query)['ps'], 'slop was not applied as qs parameter'); } /** @@ -133,13 +149,14 @@ public function slopIsNullWhenPhraseIsDisabled() $queryBuilder = new QueryBuilder($typoscriptConfiguration); $relevanceComponent = new RelevanceComponent($queryBuilder); - $query = new Query('test'); - $this->assertNull($query->getQueryParameter('ps')); + $query = new Query(); + $query->setQuery('test'); + $this->assertNull($this->getQueryParameters($query)['ps']); $relevanceComponent->setQuery($query); $relevanceComponent->initializeSearchComponent(); - $this->assertNull($query->getQueryParameter('ps'), 'PhraseSlop should be null, when phrase is disabled'); + $this->assertNull($this->getQueryParameters($query)['ps'], 'PhraseSlop should be null, when phrase is disabled'); } /** @@ -159,13 +176,15 @@ public function canSetBigramPhraseSlop() $queryBuilder = new QueryBuilder($typoscriptConfiguration); $relevanceComponent = new RelevanceComponent($queryBuilder); - $query = new Query('test'); - $this->assertNull($query->getQueryParameter('ps2')); + $query = new Query(); + $query->setQuery('test'); + + $this->assertNull($this->getQueryParameters($query)['ps2']); $relevanceComponent->setQuery($query); $relevanceComponent->initializeSearchComponent(); - $this->assertSame(4, $query->getQueryParameter('ps2'), 'slop was not applied as qs parameter'); + $this->assertSame(4, $this->getQueryParameters($query)['ps2'], 'slop was not applied as qs parameter'); } /** @@ -185,13 +204,14 @@ public function canNotSetBigramPhraseSlopWhenBigramPhraseIsDisabled() $queryBuilder = new QueryBuilder($typoscriptConfiguration); $relevanceComponent = new RelevanceComponent($queryBuilder); - $query = new Query('test'); - $this->assertNull($query->getQueryParameter('ps2')); + $query = new Query(); + $query->setQuery('test'); + $this->assertNull($this->getQueryParameters($query)['ps2']); $relevanceComponent->setQuery($query); $relevanceComponent->initializeSearchComponent(); - $this->assertNull($query->getQueryParameter('ps2'), 'ps2 parameter should be empty because bigramPhrases are disabled'); + $this->assertNull($this->getQueryParameters($query)['ps2'], 'ps2 parameter should be empty because bigramPhrases are disabled'); } /** @@ -211,13 +231,14 @@ public function canSetTrigramPhraseSlop() $queryBuilder = new QueryBuilder($typoscriptConfiguration); $relevanceComponent = new RelevanceComponent($queryBuilder); - $query = new Query('test'); - $this->assertNull($query->getQueryParameter('ps3')); + $query = new Query(); + $query->setQuery('test'); + $this->assertNull($this->getQueryParameters($query)['ps3']); $relevanceComponent->setQuery($query); $relevanceComponent->initializeSearchComponent(); - $this->assertSame(4, $query->getQueryParameter('ps3'), 'slop was not applied as qs parameter'); + $this->assertSame(4, $this->getQueryParameters($query)['ps3'], 'slop was not applied as qs parameter'); } /** @@ -237,13 +258,14 @@ public function canNotSetTrigramPhraseSlopWhenBigramPhraseIsDisabled() $queryBuilder = new QueryBuilder($typoscriptConfiguration); $relevanceComponent = new RelevanceComponent($queryBuilder); - $query = new Query('test'); - $this->assertNull($query->getQueryParameter('ps3')); + $query = new Query(); + $query->setQuery('test'); + $this->assertNull($this->getQueryParameters($query)['ps3']); $relevanceComponent->setQuery($query); $relevanceComponent->initializeSearchComponent(); - $this->assertNull($query->getQueryParameter('ps3'), 'ps2 parameter should be empty because bigramPhrases are disabled'); + $this->assertNull($this->getQueryParameters($query)['ps3'], 'ps3 parameter should be empty because bigramPhrases are disabled'); } @@ -261,13 +283,14 @@ public function canSetTieParameter() $queryBuilder = new QueryBuilder($typoscriptConfiguration); $relevanceComponent = new RelevanceComponent($queryBuilder); - $query = new Query('test'); - $this->assertNull($query->getQueryParameter('tie')); + $query = new Query(); + $query->setQuery('test'); + $this->assertNull($this->getQueryParameters($query)['tie']); $relevanceComponent->setQuery($query); $relevanceComponent->initializeSearchComponent(); - $this->assertSame('0.78', $query->getQueryParameter('tie'), 'tieParameter was not applied as tie parameter'); + $this->assertSame('0.78', $this->getQueryParameters($query)['tie'], 'tieParameter was not applied as tie parameter'); } /** @@ -281,8 +304,9 @@ public function canSetBoostQuery() ] ]; - $query = new Query('test'); - $this->assertNull($query->getQueryParameter('bq')); + $query = new Query(); + $query->setQuery('test'); + $this->assertNull($this->getQueryParameters($query)['bq']); $typoscriptConfiguration = $this->getTypoScriptConfigurationWithQueryConfiguration($searchConfiguration); $queryBuilder = new QueryBuilder($typoscriptConfiguration); @@ -292,7 +316,7 @@ public function canSetBoostQuery() $relevanceComponent->setQuery($query); $relevanceComponent->initializeSearchComponent(); - $this->assertSame('type:pages^100', $query->getQueryParameters()['bq'], 'Configured boostQuery was not applied'); + $this->assertSame('type:pages^100', $this->getQueryParameters($query)['bq'], 'Configured boostQuery was not applied'); } /** @@ -309,8 +333,9 @@ public function canSetBoostQueries() ] ]; - $query = new Query('test'); - $this->assertNull($query->getQueryParameter('bq')); + $query = new Query(); + $query->setQuery('test'); + $this->assertNull($this->getQueryParameters($query)['bq']); $typoscriptConfiguration = $this->getTypoScriptConfigurationWithQueryConfiguration($searchConfiguration); $queryBuilder = new QueryBuilder($typoscriptConfiguration); @@ -320,8 +345,8 @@ public function canSetBoostQueries() $relevanceComponent->setQuery($query); $relevanceComponent->initializeSearchComponent(); - $this->assertSame('type:pages^100', $query->getQueryParameters()['bq'][0], 'Configured boostQuery was not applied'); - $this->assertSame('type:tx_solr_file^400', $query->getQueryParameters()['bq'][1], 'Configured boostQuery was not applied'); + $this->assertSame('type:pages^100', $this->getQueryParameters($query)['bq'][0], 'Configured boostQuery was not applied'); + $this->assertSame('type:tx_solr_file^400', $this->getQueryParameters($query)['bq'][1], 'Configured boostQuery was not applied'); } /** @@ -335,8 +360,9 @@ public function canSetBoostFunction() ] ]; - $query = new Query('test'); - $this->assertNull($query->getQueryParameter('bf')); + $query = new Query(); + $query->setQuery('test'); + $this->assertNull($this->getQueryParameters($query)['bf']); $typoscriptConfiguration = $this->getTypoScriptConfigurationWithQueryConfiguration($searchConfiguration); $queryBuilder = new QueryBuilder($typoscriptConfiguration); @@ -346,7 +372,7 @@ public function canSetBoostFunction() $relevanceComponent->setQuery($query); $relevanceComponent->initializeSearchComponent(); - $this->assertSame('sum(clicks)^100', $query->getQueryParameters()['bf'], 'Configured boostFunction was not applied'); + $this->assertSame('sum(clicks)^100', $this->getQueryParameters($query)['bf'], 'Configured boostFunction was not applied'); } /** @@ -360,8 +386,9 @@ public function canSetMinimumMatch() ] ]; - $query = new Query('test'); - $this->assertNull($query->getQueryParameter('bf')); + $query = new Query(); + $query->setQuery('test'); + $this->assertNull($this->getQueryParameters($query)['mm']); $typoscriptConfiguration = $this->getTypoScriptConfigurationWithQueryConfiguration($searchConfiguration); $queryBuilder = new QueryBuilder($typoscriptConfiguration); @@ -371,7 +398,7 @@ public function canSetMinimumMatch() $relevanceComponent->setQuery($query); $relevanceComponent->initializeSearchComponent(); - $this->assertSame('<1', $query->getQueryParameters()['mm'], 'Configured minimumMatch was not applied'); + $this->assertSame('<1', $this->getQueryParameters($query)['mm'], 'Configured minimumMatch was not applied'); } /** diff --git a/Tests/Unit/Search/SortingComponentTest.php b/Tests/Unit/Search/SortingComponentTest.php index defc1d80a8..25755b6ddd 100644 --- a/Tests/Unit/Search/SortingComponentTest.php +++ b/Tests/Unit/Search/SortingComponentTest.php @@ -57,7 +57,8 @@ class SortingComponentTest extends UnitTest */ public function setUp() { - $this->query = new Query(''); + $this->query = new Query(); + $this->query->setQuery(''); $this->searchRequestMock = $this->getDumbMock(SearchRequest::class); $this->sortingComponent = new SortingComponent(); @@ -70,9 +71,9 @@ public function setUp() */ public function sortingFromUrlIsNotAppliedWhenSortingIsDisabled() { - $this->searchRequestMock->expects($this->once())->method('getArguments')->willReturn(['sort' => 'title asc']); + $this->searchRequestMock->expects($this->any())->method('getArguments')->willReturn(['sort' => 'title asc']); $this->sortingComponent->initializeSearchComponent(); - $this->assertNull($this->query->getQueryParameter('sort'), 'No sorting should be present in query'); + $this->assertSame([], $this->query->getSorts(), 'No sorting should be present in query'); } /** @@ -90,9 +91,9 @@ public function validSortingFromUrlIsApplied() ] ] ]); - $this->searchRequestMock->expects($this->once())->method('getArguments')->willReturn(['sort' => 'title asc']); + $this->searchRequestMock->expects($this->any())->method('getArguments')->willReturn(['sort' => 'title asc']); $this->sortingComponent->initializeSearchComponent(); - $this->assertSame('sortTitle asc', $this->query->getQueryParameter('sort'), 'Sorting was not applied in the query as expected'); + $this->assertSame(['sortTitle' => 'asc'], $this->query->getSorts(), 'Sorting was not applied in the query as expected'); } /** @@ -110,9 +111,9 @@ public function invalidSortingFromUrlIsNotApplied() ] ] ]); - $this->searchRequestMock->expects($this->once())->method('getArguments')->willReturn(['sort' => 'title INVALID']); + $this->searchRequestMock->expects($this->any())->method('getArguments')->willReturn(['sort' => 'title INVALID']); $this->sortingComponent->initializeSearchComponent(); - $this->assertNull($this->query->getQueryParameter('sort'), 'No sorting should be present in query'); + $this->assertSame([], $this->query->getSorts(), 'No sorting should be present in query'); } /** @@ -125,9 +126,9 @@ public function sortByIsApplied() 'sortBy' => 'price desc' ] ]); - $this->searchRequestMock->expects($this->once())->method('getArguments')->willReturn([]); + $this->searchRequestMock->expects($this->any())->method('getArguments')->willReturn([]); $this->sortingComponent->initializeSearchComponent(); - $this->assertSame('price desc', $this->query->getQueryParameter('sort'), 'No sorting should be present in query'); + $this->assertSame(['price' => 'desc'], $this->query->getSorts(), 'No sorting should be present in query'); } /** @@ -148,9 +149,9 @@ public function urlSortingHasPrioriy() ] ] ]); - $this->searchRequestMock->expects($this->once())->method('getArguments')->willReturn(['sort' => 'title asc']); + $this->searchRequestMock->expects($this->any())->method('getArguments')->willReturn(['sort' => 'title asc']); $this->sortingComponent->initializeSearchComponent(); - $this->assertSame('sortTitle asc', $this->query->getQueryParameter('sort'), 'No sorting should be present in query'); + $this->assertSame(['sortTitle' => 'asc'], $this->query->getSorts(), 'No sorting should be present in query'); } /** @@ -171,8 +172,8 @@ public function querySortingHasPriorityWhenSortingIsDisabled() ] ] ]); - $this->searchRequestMock->expects($this->once())->method('getArguments')->willReturn(['sort' => 'title asc']); + $this->searchRequestMock->expects($this->any())->method('getArguments')->willReturn(['sort' => 'title asc']); $this->sortingComponent->initializeSearchComponent(); - $this->assertSame('price desc', $this->query->getQueryParameter('sort'), 'No sorting should be present in query'); + $this->assertSame(['price' => 'desc'], $this->query->getSorts(), 'No sorting should be present in query'); } } \ No newline at end of file diff --git a/composer.json b/composer.json index fd0a45ae34..d7de5ab95c 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,8 @@ "typo3/cms-fluid": "^8.7.0 || ^9.3", "typo3/cms-reports": "^8.7.0 || ^9.3", "typo3/cms-scheduler": "^8.7.0 || ^9.3", - "typo3/cms-tstemplate": "^8.7.0 || ^9.3" + "typo3/cms-tstemplate": "^8.7.0 || ^9.3", + "solarium/solarium": "4.1.0-rc.1" }, "require-dev": { "phpunit/phpunit": "^6.0", @@ -60,6 +61,12 @@ "post-autoload-dump": [ "mkdir -p .Build/Web/typo3conf/ext/", "[ -L .Build/Web/typo3conf/ext/solr ] || ln -snvf ../../../../. .Build/Web/typo3conf/ext/solr" + ], + "extension-create-libs": [ + "@composer install -d Resources/Private/Php/ComposerLibraries" + ], + "extension-build": [ + "@extension-create-libs" ] }, "extra": { diff --git a/ext_localconf.php b/ext_localconf.php index 478a65f987..c69da1bd07 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -253,3 +253,11 @@ // add tsconfig \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPageTSConfig(''); + + +$isComposerMode = defined('TYPO3_COMPOSER_MODE') && TYPO3_COMPOSER_MODE; +if(!$isComposerMode) { + // we load the autoloader for our libraries + $dir = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath($_EXTKEY); + require $dir . '/Resources/Private/Php/ComposerLibraries/vendor/autoload.php'; +} diff --git a/ext_tables.php b/ext_tables.php index 5225275863..8fe29f887d 100644 --- a/ext_tables.php +++ b/ext_tables.php @@ -141,3 +141,10 @@ # ----- # ----- # ----- # ----- # ----- # ----- # ----- # ----- # ----- # $GLOBALS['TYPO3_CONF_VARS']['BE']['ContextMenu']['ItemProviders'][1487876780] = \ApacheSolrForTypo3\Solr\ContextMenu\ItemProviders\InitializeConnectionProvider::class; + +$isComposerMode = defined('TYPO3_COMPOSER_MODE') && TYPO3_COMPOSER_MODE; +if(!$isComposerMode) { + // we load the autoloader for our libraries + $dir = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath($_EXTKEY); + require $dir . '/Resources/Private/Php/ComposerLibraries/vendor/autoload.php'; +}