559 lines
14 KiB
PHP
559 lines
14 KiB
PHP
|
<?php
|
||
|
|
||
|
namespace WapplerSystems\Meilisearch\Domain\Search\Query;
|
||
|
|
||
|
/***************************************************************
|
||
|
* Copyright notice
|
||
|
*
|
||
|
* (c) 2017 <timo.hund@dkd.de>
|
||
|
* 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 WapplerSystems\Meilisearch\Domain\Search\Query\ParameterBuilder\BigramPhraseFields;
|
||
|
use WapplerSystems\Meilisearch\Domain\Search\Query\ParameterBuilder\Elevation;
|
||
|
use WapplerSystems\Meilisearch\Domain\Search\Query\ParameterBuilder\Faceting;
|
||
|
use WapplerSystems\Meilisearch\Domain\Search\Query\ParameterBuilder\FieldCollapsing;
|
||
|
use WapplerSystems\Meilisearch\Domain\Search\Query\ParameterBuilder\Grouping;
|
||
|
use WapplerSystems\Meilisearch\Domain\Search\Query\ParameterBuilder\Highlighting;
|
||
|
use WapplerSystems\Meilisearch\Domain\Search\Query\ParameterBuilder\Operator;
|
||
|
use WapplerSystems\Meilisearch\Domain\Search\Query\ParameterBuilder\PhraseFields;
|
||
|
use WapplerSystems\Meilisearch\Domain\Search\Query\ParameterBuilder\QueryFields;
|
||
|
use WapplerSystems\Meilisearch\Domain\Search\Query\ParameterBuilder\ReturnFields;
|
||
|
use WapplerSystems\Meilisearch\Domain\Search\Query\ParameterBuilder\Slops;
|
||
|
use WapplerSystems\Meilisearch\Domain\Search\Query\ParameterBuilder\Sorting;
|
||
|
use WapplerSystems\Meilisearch\Domain\Search\Query\ParameterBuilder\Sortings;
|
||
|
use WapplerSystems\Meilisearch\Domain\Search\Query\ParameterBuilder\Spellchecking;
|
||
|
use WapplerSystems\Meilisearch\Domain\Search\Query\ParameterBuilder\TrigramPhraseFields;
|
||
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||
|
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||
|
|
||
|
/**
|
||
|
* The AbstractQueryBuilder contains all logic to initialize solr queries independent from TYPO3.
|
||
|
*/
|
||
|
abstract class AbstractQueryBuilder {
|
||
|
|
||
|
/**
|
||
|
* @var Query
|
||
|
*/
|
||
|
protected $queryToBuild = null;
|
||
|
|
||
|
/**
|
||
|
* @param Query $query
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function startFrom(Query $query)
|
||
|
{
|
||
|
$this->queryToBuild = $query;
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return Query
|
||
|
*/
|
||
|
public function getQuery(): Query
|
||
|
{
|
||
|
return $this->queryToBuild;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param bool $omitHeader
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function useOmitHeader($omitHeader = true)
|
||
|
{
|
||
|
$this->queryToBuild->setOmitHeader($omitHeader);
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Uses an array of filters and applies them to the query.
|
||
|
*
|
||
|
* @param array $filterArray
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function useFilterArray(array $filterArray)
|
||
|
{
|
||
|
foreach ($filterArray as $key => $additionalFilter) {
|
||
|
$this->useFilter($additionalFilter, $key);
|
||
|
}
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Applies the queryString that is used to search
|
||
|
*
|
||
|
* @param string $queryString
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function useQueryString($queryString)
|
||
|
{
|
||
|
$this->queryToBuild->setQuery($queryString);
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Applies the passed queryType to the query.
|
||
|
*
|
||
|
* @param string $queryType
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function useQueryType(string $queryType)
|
||
|
{
|
||
|
$this->queryToBuild->addParam('qt', $queryType);
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Remove the queryType (qt) from the query.
|
||
|
*
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function removeQueryType()
|
||
|
{
|
||
|
$this->queryToBuild->addParam('qt', null);
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Can be used to remove all sortings from the query.
|
||
|
*
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function removeAllSortings()
|
||
|
{
|
||
|
$this->queryToBuild->clearSorts();
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Applies the passed sorting to the query.
|
||
|
*
|
||
|
* @param Sorting $sorting
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function useSorting(Sorting $sorting)
|
||
|
{
|
||
|
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 Sortings $sortings
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function useSortings(Sortings $sortings)
|
||
|
{
|
||
|
foreach($sortings->getSortings() as $sorting) {
|
||
|
$this->useSorting($sorting);
|
||
|
}
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param int $resultsPerPage
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function useResultsPerPage($resultsPerPage)
|
||
|
{
|
||
|
$this->queryToBuild->setRows($resultsPerPage);
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param int $page
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function usePage($page)
|
||
|
{
|
||
|
$this->queryToBuild->setStart($page);
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param Operator $operator
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function useOperator(Operator $operator)
|
||
|
{
|
||
|
$this->queryToBuild->setQueryDefaultOperator( $operator->getOperator());
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Remove the default query operator.
|
||
|
*
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function removeOperator()
|
||
|
{
|
||
|
$this->queryToBuild->setQueryDefaultOperator('');
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param Slops $slops
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function useSlops(Slops $slops)
|
||
|
{
|
||
|
return $slops->build($this);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Uses the passed boostQuer(y|ies) for the query.
|
||
|
*
|
||
|
* @param string|array $boostQueries
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function useBoostQueries($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 $this
|
||
|
*/
|
||
|
public function removeAllBoostQueries()
|
||
|
{
|
||
|
$this->queryToBuild->getEDisMax()->clearBoostQueries();
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Uses the passed boostFunction for the query.
|
||
|
*
|
||
|
* @param string $boostFunction
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function useBoostFunction(string $boostFunction)
|
||
|
{
|
||
|
$this->queryToBuild->getEDisMax()->setBoostFunctions($boostFunction);
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Removes all previously configured boost functions.
|
||
|
*
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function removeAllBoostFunctions()
|
||
|
{
|
||
|
$this->queryToBuild->getEDisMax()->setBoostFunctions('');
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Uses the passed minimumMatch(mm) for the query.
|
||
|
*
|
||
|
* @param string $minimumMatch
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function useMinimumMatch(string $minimumMatch)
|
||
|
{
|
||
|
$this->queryToBuild->getEDisMax()->setMinimumMatch($minimumMatch);
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Remove any previous passed minimumMatch parameter.
|
||
|
*
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function removeMinimumMatch()
|
||
|
{
|
||
|
$this->queryToBuild->getEDisMax()->setMinimumMatch('');
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Applies the tie parameter to the query.
|
||
|
*
|
||
|
* @param mixed $tie
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function useTieParameter($tie)
|
||
|
{
|
||
|
$this->queryToBuild->getEDisMax()->setTie($tie);
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Applies custom QueryFields to the query.
|
||
|
*
|
||
|
* @param QueryFields $queryFields
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function useQueryFields(QueryFields $queryFields)
|
||
|
{
|
||
|
return $queryFields->build($this);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Applies custom ReturnFields to the query.
|
||
|
*
|
||
|
* @param ReturnFields $returnFields
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function useReturnFields(ReturnFields $returnFields)
|
||
|
{
|
||
|
return $returnFields->build($this);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Can be used to use a specific filter string in the solr query.
|
||
|
*
|
||
|
* @param string $filterString
|
||
|
* @param string $filterName
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function useFilter($filterString, $filterName = '')
|
||
|
{
|
||
|
$filterName = $filterName === '' ? $filterString : $filterName;
|
||
|
|
||
|
$nameWasPassedAndFilterIsAllreadySet = $filterName !== '' && $this->queryToBuild->getFilterQuery($filterName) !== null;
|
||
|
if($nameWasPassedAndFilterIsAllreadySet) {
|
||
|
return $this;
|
||
|
}
|
||
|
$this->queryToBuild->addFilterQuery(['key' => $filterName, 'query' => $filterString]);
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Removes a filter by the fieldName.
|
||
|
*
|
||
|
* @param string $fieldName
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function removeFilterByFieldName($fieldName)
|
||
|
{
|
||
|
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 $this
|
||
|
*/
|
||
|
public function removeFilterByName($name)
|
||
|
{
|
||
|
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 $this
|
||
|
*/
|
||
|
public function removeFilterByValue($value)
|
||
|
{
|
||
|
return $this->removeFilterByFunction(
|
||
|
function($key, $query) use ($value) {
|
||
|
$query = $query->getQuery();
|
||
|
return $query == $value;
|
||
|
}
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param \Closure $filterFunction
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function removeFilterByFunction($filterFunction)
|
||
|
{
|
||
|
$queries = $this->queryToBuild->getFilterQueries();
|
||
|
foreach($queries as $key => $query) {
|
||
|
$canBeRemoved = $filterFunction($key, $query);
|
||
|
if($canBeRemoved) {
|
||
|
unset($queries[$key]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$this->queryToBuild->setFilterQueries($queries);
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Passes the alternative query to the Query
|
||
|
* @param string $query
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function useAlternativeQuery(string $query)
|
||
|
{
|
||
|
$this->queryToBuild->getEDisMax()->setQueryAlternative($query);
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Remove the alternative query from the Query.
|
||
|
*
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function removeAlternativeQuery()
|
||
|
{
|
||
|
$this->queryToBuild->getEDisMax()->setQueryAlternative(null);
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Applies a custom Faceting configuration to the query.
|
||
|
*
|
||
|
* @param Faceting $faceting
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function useFaceting(Faceting $faceting)
|
||
|
{
|
||
|
return $faceting->build($this);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param FieldCollapsing $fieldCollapsing
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function useFieldCollapsing(FieldCollapsing $fieldCollapsing)
|
||
|
{
|
||
|
return $fieldCollapsing->build($this);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Applies a custom initialized grouping to the query.
|
||
|
*
|
||
|
* @param Grouping $grouping
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function useGrouping(Grouping $grouping)
|
||
|
{
|
||
|
return $grouping->build($this);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param Highlighting $highlighting
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function useHighlighting(Highlighting $highlighting)
|
||
|
{
|
||
|
return $highlighting->build($this);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param boolean $debugMode
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function useDebug($debugMode)
|
||
|
{
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param Elevation $elevation
|
||
|
* @return QueryBuilder
|
||
|
*/
|
||
|
public function useElevation(Elevation $elevation)
|
||
|
{
|
||
|
return $elevation->build($this);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param Spellchecking $spellchecking
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function useSpellchecking(Spellchecking $spellchecking)
|
||
|
{
|
||
|
return $spellchecking->build($this);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Applies a custom configured PhraseFields to the query.
|
||
|
*
|
||
|
* @param PhraseFields $phraseFields
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function usePhraseFields(PhraseFields $phraseFields)
|
||
|
{
|
||
|
return $phraseFields->build($this);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Applies a custom configured BigramPhraseFields to the query.
|
||
|
*
|
||
|
* @param BigramPhraseFields $bigramPhraseFields
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function useBigramPhraseFields(BigramPhraseFields $bigramPhraseFields)
|
||
|
{
|
||
|
return $bigramPhraseFields->build($this);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Applies a custom configured TrigramPhraseFields to the query.
|
||
|
*
|
||
|
* @param TrigramPhraseFields $trigramPhraseFields
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function useTrigramPhraseFields(TrigramPhraseFields $trigramPhraseFields)
|
||
|
{
|
||
|
return $trigramPhraseFields->build($this);
|
||
|
}
|
||
|
}
|