Commit 97a9df0c authored by Markus Kalkbrenner's avatar Markus Kalkbrenner Committed by GitHub

Support for ReRankQuery (#602)

* moved Component\BoostQuery to Component\DisMax\BoostQuery

* updated dev dependencies

* Every component that has a 'query' option is now able to bind parameters to a query string via its setQuery() function. Renamed option 'q' to 'query' in Solarium\Component\Facet\JsonQuery for consistency.

* Added Support for ReRankQuery, code cleanup

* fixed coding style

* fixed coding style

* added integration test and documentation for rerank

* use latest solr versions for testing

* check for reranking without relying on on the default ranking that might chege between different solr versions.
parent 419de8b7
......@@ -9,10 +9,10 @@ php:
- nightly
env:
- SYMFONY_VERSION=2.7.* SOLR_VERSION=6.6.2
- SYMFONY_VERSION=3.3.* SOLR_VERSION=6.6.2
- SYMFONY_VERSION=4.0.* SOLR_VERSION=6.6.2
- SYMFONY_VERSION=4.0.* SOLR_VERSION=7.2.1
- SYMFONY_VERSION=2.7.* SOLR_VERSION=6.6.4
- SYMFONY_VERSION=3.3.* SOLR_VERSION=6.6.4
- SYMFONY_VERSION=4.0.* SOLR_VERSION=6.6.4
- SYMFONY_VERSION=4.0.* SOLR_VERSION=7.3.1
cache:
directories:
......@@ -39,9 +39,9 @@ after_success:
matrix:
exclude:
- php: 7.0
env: SYMFONY_VERSION=4.0.* SOLR_VERSION=6.6.2
env: SYMFONY_VERSION=4.0.* SOLR_VERSION=6.6.4
- php: 7.0
env: SYMFONY_VERSION=4.0.* SOLR_VERSION=7.2.1
env: SYMFONY_VERSION=4.0.* SOLR_VERSION=7.3.1
allow_failures:
- php: nightly
......
......@@ -6,8 +6,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [4.1.0-rc.1]
### Added
- Every component that has a 'query' option is now able to bind parameters to a query string via its setQuery() function
- Tests for cursormark
- Support for ReRankQuery
### Changed
- Renamed option 'q' to 'query' in Solarium\Component\Facet\JsonQuery for consistency
### Deprecated
......
......@@ -18,9 +18,9 @@
"require-dev": {
"guzzlehttp/guzzle": "^3.8 || ^6.2",
"phpunit/phpunit": "^6.5",
"satooshi/php-coveralls": "^1.0",
"php-coveralls/php-coveralls": "^1.0",
"squizlabs/php_codesniffer": "^1.4",
"zendframework/zendframework1": "^1.12"
"zendframework/zendframework": "^3.0"
},
"suggest": {
"minimalcode/search": "Query builder compatible with Solarium, allows simplified solr-query handling"
......
......@@ -30,9 +30,6 @@ $client = new Solarium\Client($config);
$query = $client->createSelect();
$query->setQuery('electronics');
// set a handler that is configured with an elevator component in solrconfig.xml (or add it to your default handler)
$query->setHandler('elevate');
// get query elevation component
$elevate = $query->getQueryElevation();
......
Query Re-Ranking allows you to run a simple query (A) for matching documents and then re-rank the top N documents using the scores from a more complex query (B).
Since the more costly ranking from query B is only applied to the top N documents, it will have less impact on performance then just using the complex query B by itself. The trade off is that documents which score very low using the simple query A may not be considered during the re-ranking phase, even if they would score very highly using query B.
For more info see <https://lucene.apache.org/solr/guide/query-re-ranking.html>.
Options
-------
| Name | Type | Default value | Description |
|--------|---------|---------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| query | string | null | The query string for your complex ranking query. |
| docs | integer | 200 | The number of top N documents from the original query that should be re-ranked. This number will be treated as a minimum, and may be increased internally automatically in order to rank enough documents to satisfy the query (i.e., start+rows). |
| weight | float | 2.0 | A multiplicative factor that will be applied to the score from the reRankQuery for each of the top matching documents, before that score is added to the original score. |
||
Example
-------
```php
<?php
require(__DIR__.'/init.php');
htmlHeader();
// create a client instance
$client = new Solarium\Client($config);
// get a select query instance
$query = $client->createSelect();
$query->setQuery('electronics');
// get rerankquery component
$rerank = $query->getReRankQuery();
// boost documents that have a popularity of 10
$rerank->setQuery('popularity:10');
// set the "boost factor"
$rerank->setWeight(3);
// this executes the query and returns the result
$resultset = $client->select($query);
// display the total number of documents found by solr
echo 'NumFound: '.$resultset->getNumFound();
// show documents using the resultset iterator
foreach ($resultset as $document) {
echo '<hr/><table>';
// the documents are also iterable, to get all fields
foreach ($document as $field => $value) {
// this converts multivalue fields to a comma-separated string
if (is_array($value)) {
$value = implode(', ', $value);
}
echo '<tr><th>' . $field . '</th><td>' . $value . '</td></tr>';
}
}
htmlFooter();
```
......@@ -3,9 +3,9 @@
namespace Solarium\Component;
use Solarium\Component\RequestBuilder\ComponentRequestBuilderInterface;
use Solarium\Component\ResponseParser\ComponentParserInterface;
use Solarium\Core\Configurable;
use Solarium\Core\Query\AbstractQuery;
use Solarium\Core\Query\Helper;
/**
* Query component base class.
......@@ -32,11 +32,11 @@ abstract class AbstractComponent extends Configurable
abstract public function getRequestBuilder();
/**
* Get the response parser class for this query.
*
* @return ComponentParserInterface
* This component has no response parser...
*/
abstract public function getResponseParser();
public function getResponseParser()
{
}
/**
* Set parent query instance.
......@@ -61,4 +61,18 @@ abstract class AbstractComponent extends Configurable
{
return $this->queryInstance;
}
/**
* Returns a query helper.
*
* @return \Solarium\Core\Query\Helper
*/
public function getHelper()
{
if ($queryInstance = $this->getQueryInstance()) {
return $this->getQueryInstance()->getHelper();
} else {
return new Helper();
}
}
}
......@@ -77,6 +77,11 @@ interface ComponentAwareQueryInterface
*/
const COMPONENT_QUERYELEVATION = 'queryelevation';
/**
* Query component rerank query.
*/
const COMPONENT_RERANKQUERY = 'rerankquery';
/**
* Get all registered component types.
*
......
......@@ -14,16 +14,6 @@ trait SpellcheckTrait
*/
protected $collateParams = [];
/**
* Get query option.
*
* @return string|null
*/
public function getQuery()
{
return $this->getOption('query');
}
/**
* Set build option.
*
......
......@@ -7,16 +7,6 @@ namespace Solarium\Component\ComponentTraits;
*/
trait SuggesterTrait
{
/**
* Get query option.
*
* @return string|null
*/
public function getQuery()
{
return $this->getOption('query');
}
/**
* Set dictionary option.
*
......
......@@ -2,6 +2,7 @@
namespace Solarium\Component;
use Solarium\Component\DisMax\BoostQuery;
use Solarium\Component\RequestBuilder\DisMax as RequestBuilder;
use Solarium\Exception\InvalidArgumentException;
......@@ -48,13 +49,6 @@ class DisMax extends AbstractComponent
return new RequestBuilder();
}
/**
* This component has no response parser...
*/
public function getResponseParser()
{
}
/**
* Set QueryAlternative option.
*
......
<?php
namespace Solarium\Component;
namespace Solarium\Component\DisMax;
use Solarium\Component\QueryInterface;
use Solarium\Component\QueryTrait;
use Solarium\Core\Configurable;
use Solarium\Core\Query\Helper;
/**
* Filterquery.
*
* @see http://wiki.apache.org/solr/CommonQueryParameters#fq
* Boost query.
*/
class BoostQuery extends Configurable
class BoostQuery extends Configurable implements QueryInterface
{
/**
* Query.
*
* @var string
*/
protected $query;
use QueryTrait;
/**
* Get key value.
......@@ -42,35 +37,13 @@ class BoostQuery extends Configurable
}
/**
* Set the query string.
*
* This overwrites the current value
* Returns a query helper.
*
* @param string $query
* @param array $bind Bind values for placeholders in the query string
*
* @return self Provides fluent interface
*/
public function setQuery($query, $bind = null)
{
if (null !== $bind) {
$helper = new Helper();
$query = $helper->assemble($query, $bind);
}
$this->query = trim($query);
return $this;
}
/**
* Get the query string.
*
* @return string
* @return \Solarium\Core\Query\Helper
*/
public function getQuery()
public function getHelper()
{
return $this->query;
return new Helper();
}
/**
......
......@@ -53,13 +53,6 @@ class DistributedSearch extends AbstractComponent
return new RequestBuilder();
}
/**
* This component has no response parser...
*/
public function getResponseParser()
{
}
/**
* Add a shard.
*
......
......@@ -3,6 +3,8 @@
namespace Solarium\Component\Facet;
use Solarium\Component\FacetSetInterface;
use Solarium\Component\QueryInterface;
use Solarium\Component\QueryTrait;
use Solarium\Core\Query\Helper;
/**
......@@ -10,9 +12,12 @@ use Solarium\Core\Query\Helper;
*
* @see https://lucene.apache.org/solr/guide/7_3/json-facet-api.html
*/
class JsonQuery extends AbstractFacet implements JsonFacetInterface, FacetSetInterface
class JsonQuery extends AbstractFacet implements JsonFacetInterface, FacetSetInterface, QueryInterface
{
use JsonFacetTrait;
use JsonFacetTrait {
serialize as jsonFacetTraitSerialize;
}
use QueryTrait;
/**
* Default options.
......@@ -20,7 +25,7 @@ class JsonQuery extends AbstractFacet implements JsonFacetInterface, FacetSetInt
* @var array
*/
protected $options = [
'q' => '*:*',
'query' => '*:*',
];
/**
......@@ -34,32 +39,20 @@ class JsonQuery extends AbstractFacet implements JsonFacetInterface, FacetSetInt
}
/**
* Set the query string.
*
* This overwrites the current value
*
* @param string $query
* @param array $bind Bind values for placeholders in the query string
* Returns a query helper.
*
* @return self Provides fluent interface
* @return \Solarium\Core\Query\Helper
*/
public function setQuery($query, $bind = null)
public function getHelper()
{
if (null !== $bind) {
$helper = new Helper();
$query = $helper->assemble($query, $bind);
}
return $this->setOption('q', $query);
return new Helper();
}
/**
* Get the query string.
*
* @return string
*/
public function getQuery()
public function serialize()
{
return $this->getOption('q');
$options = $this->jsonFacetTraitSerialize();
$options['q'] = $options['query'];
unset($options['query']);
return $options;
}
}
......@@ -3,6 +3,8 @@
namespace Solarium\Component\Facet;
use Solarium\Component\FacetSetInterface;
use Solarium\Component\QueryInterface;
use Solarium\Component\QueryTrait;
use Solarium\Core\Query\Helper;
/**
......@@ -10,9 +12,10 @@ use Solarium\Core\Query\Helper;
*
* @see http://wiki.apache.org/solr/SimpleFacetParameters#facet.query_:_Arbitrary_Query_Faceting
*/
class Query extends AbstractFacet
class Query extends AbstractFacet implements QueryInterface
{
use ExcludeTagsTrait;
use QueryTrait;
/**
* Default options.
......@@ -34,32 +37,12 @@ class Query extends AbstractFacet
}
/**
* Set the query string.
* Returns a query helper.
*
* This overwrites the current value
*
* @param string $query
* @param array $bind Bind values for placeholders in the query string
*
* @return self Provides fluent interface
*/
public function setQuery($query, $bind = null)
{
if (null !== $bind) {
$helper = new Helper();
$query = $helper->assemble($query, $bind);
}
return $this->setOption('query', $query);
}
/**
* Get the query string.
*
* @return string
* @return \Solarium\Core\Query\Helper
*/
public function getQuery()
public function getHelper()
{
return $this->getOption('query');
return new Helper();
}
}
......@@ -4,6 +4,8 @@ namespace Solarium\Component\Highlighting;
use Solarium\Component\AbstractComponent;
use Solarium\Component\ComponentAwareQueryInterface;
use Solarium\Component\QueryInterface;
use Solarium\Component\QueryTrait;
use Solarium\Component\RequestBuilder\Highlighting as RequestBuilder;
use Solarium\Component\ResponseParser\Highlighting as ResponseParser;
use Solarium\Exception\InvalidArgumentException;
......@@ -13,8 +15,10 @@ use Solarium\Exception\InvalidArgumentException;
*
* @see http://wiki.apache.org/solr/HighlightingParameters
*/
class Highlighting extends AbstractComponent
class Highlighting extends AbstractComponent implements QueryInterface
{
use QueryTrait;
/**
* Value for fragmenter option gap.
*/
......@@ -724,30 +728,6 @@ class Highlighting extends AbstractComponent
return $this->getOption('regexmaxanalyzedchars');
}
/**
* Set highlight query option.
*
* Overrides the q parameter for highlighting
*
* @param string $query
*
* @return self Provides fluent interface
*/
public function setQuery($query)
{
return $this->setOption('query', $query);
}
/**
* Get query option.
*
* @return string|null
*/
public function getQuery()
{
return $this->getOption('query');
}
/**
* Set phraselimit option.
*
......
......@@ -47,13 +47,6 @@ class QueryElevation extends AbstractComponent
return new RequestBuilder();
}
/**
* This component has no response parser...
*/
public function getResponseParser()
{
}
/**
* Add a document transformer.
*
......
<?php
namespace Solarium\Component;
/**
* Query Trait.
*/
interface QueryInterface
{
/**
* Set the query string.
*
* This overwrites the current value of a query or 'q' parameter.
*
* @param string $query
* @param array $bind Bind values for placeholders in the query string
*
* @return self Provides fluent interface
*/
public function setQuery($query, $bind = null);
/**
* Get query option.
*
* @return string|null
*/
public function getQuery();
}
<?php
namespace Solarium\Component;
/**
* Query Trait.
*/
trait QueryTrait
{
/**
* Set the query string.
*
* This overwrites the current value of a query or 'q' parameter.
*
* @param string $query
* @param array $bind Bind values for placeholders in the query string
*
* @return self Provides fluent interface
*/
public function setQuery($query, $bind = null)
{
if (null !== $bind) {
$helper = $this->getHelper();
$query = $helper->assemble($query, $bind);
}
return $this->setOption('query', trim($query));
}
/**
* Get query option.
*
* @return string|null
*/
public function getQuery()
{
return $this->getOption('query');
}
}
<?php
namespace Solarium\Component\QueryTraits;
use Solarium\Component\ComponentAwareQueryInterface;
/**
* Trait query types supporting components.
*/
trait ReRankQueryTrait
{
/**
* Get a ReRankQuery component instance.
*
* This is a convenience method that maps presets to getComponent
*
* @return \Solarium\Component\ReRankQuery
*/
public function getReRankQuery()
{
return $this->getComponent(ComponentAwareQueryInterface::COMPONENT_RERANKQUERY, true);
}
}
<?php
namespace Solarium\Component;
use Solarium\Component\RequestBuilder\ReRankQuery as RequestBuilder;
/**
* Rerank query.
*
* @see https://lucene.apache.org/solr/guide/7_3/query-re-ranking.html#rerank-query-parser
*/
class ReRankQuery extends AbstractComponent implements QueryInterface
{
use QueryTrait;
/**
* Get component type.
*
* @return string
*/
public function getType()
{
return ComponentAwareQueryInterface::COMPONENT_RERANKQUERY;
}
/**
* Get a requestbuilder for this query.
*
* @return RequestBuilder
*/
public function getRequestBuilder()
{
return new RequestBuilder();
}
/**
* Get reRankDocs value.
*
* @return int
*/
public function getDocs()
{
return $this->getOption('docs');
}
/**
* Set reRankDocs value.
*
* @param int $value
*
* @return self Provides fluent interface
*/
public function setDocs(int $value)
{
return $this->setOption('docs', $value);
}
/**
* Get reRankWeight value.
*
* @return float
*/
public function getWeight()
{
return $this->getOption('weight');
}
/**
* Set reRankWeight value.
*
* @param float $value
*
* @return self Provides fluent interface
*/
public function setWeight(float $value)
{
return $this->setOption('weight', $value);
}
}
<?php
namespace Solarium\Component\RequestBuilder;
use Solarium\Component\ReRankQuery as ReRankQueryComponent;
use Solarium\Core\Client\Request;
/**
* Add select component spatial to the request.
*/
class ReRankQuery implements ComponentRequestBuilderInterface
{
/**
* Add request settings for ReRankQuery.
*
* @param ReRankQueryComponent $component
* @param Request $request
*
* @return Request
*/
public function buildComponent($component, $request)
{
$subRequest = new SubRequest();
$subRequest->addParam('reRankQuery', $component->getQuery());
$subRequest->addParam('reRankDocs', $component->getDocs());
$subRequest->addParam('reRankWeight', $component->getWeight());
$request->addParam('rq', $subRequest->getSubQuery());
return $request;
}
}
<?php
namespace Solarium\Component\RequestBuilder;
/**
* Interface for handling request params.
*/
interface RequestParamsInterface
{
/**
* Get a param value.
*
* @param string $key
*
* @return string|array
*/
public function getParam($key);
/**
* Get all params.
*
* @return array
*/
public function getParams();
/**
* Set request params.
*
* @param array $params
*
* @return self Provides fluent interface
*/
public function setParams($params);
/**
* Add a request param.
*
* If you add a request param that already exists the param will be converted into a multivalue param,
* unless you set the overwrite param to true.
*
* Empty params are not added to the request. If you want to empty a param disable it you should use
* remove param instead.
*
* @param string $key
* @param string|array $value
* @param bool $overwrite
*
* @return self Provides fluent interface
*/
public function addParam($key, $value, $overwrite = false);
/**
* Add multiple params to the request.
*
* @param array $params
* @param bool $overwrite
*
* @return self Provides fluent interface
*/
public function addParams($params, $overwrite = false);
/**
* Remove a param by key.
*
* @param string $key
*
* @return self Provides fluent interface
*/
public function removeParam($key);
/**
* Clear all request params.
*
* @return self Provides fluent interface
*/
public function clearParams();
/**
* Get the query string for this request.
*
* @return string
*/
public function getQueryString();
}
<?php
namespace Solarium\Component\RequestBuilder;
/**
* Trait for handling request params.
*/
trait RequestParamsTrait
{
/**
* Request params.
*
* Multivalue params are supported using a multidimensional array:
* 'fq' => array('cat:1','published:1')
*
* @var array
*/
protected $params = [];
/**
* Get a param value.
*
* @param string $key
*
* @return string|array
*/
public function getParam($key)
{
if (isset($this->params[$key])) {
return $this->params[$key];
}
}
/**
* Get all params.
*
* @return array
*/
public function getParams()
{
return $this->params;
}
/**
* Set request params.
*
* @param array $params
*
* @return self Provides fluent interface
*/
public function setParams($params)
{
$this->clearParams();
$this->addParams($params);
return $this;
}
/**
* Add a request param.
*
* If you add a request param that already exists the param will be converted into a multivalue param,
* unless you set the overwrite param to true.
*
* Empty params are not added to the request. If you want to empty a param disable it you should use
* remove param instead.
*
* @param string $key
* @param string|array|SubRequest $value
* @param bool $overwrite
*
* @return self Provides fluent interface
*/
public function addParam($key, $value, $overwrite = false)
{
if (null !== $value) {
if (!$overwrite && isset($this->params[$key])) {
if (!is_array($this->params[$key])) {
$this->params[$key] = [$this->params[$key]];
}
$this->params[$key][] = $value;
} else {
// not all solr handlers support 0/1 as boolean values...
if (true === $value) {
$value = 'true';
} elseif (false === $value) {
$value = 'false';
}
$this->params[$key] = $value;
}
}
return $this;
}
/**
* Add multiple params to the request.
*
* @param array $params
* @param bool $overwrite
*
* @return self Provides fluent interface
*/
public function addParams($params, $overwrite = false)
{
foreach ($params as $key => $value) {
$this->addParam($key, $value, $overwrite);
}
return $this;
}
/**
* Remove a param by key.
*
* @param string $key
*
* @return self Provides fluent interface
*/
public function removeParam($key)
{
if (isset($this->params[$key])) {
unset($this->params[$key]);
}
return $this;
}
/**
* Clear all request params.
*
* @return self Provides fluent interface
*/
public function clearParams()
{
$this->params = [];
return $this;
}
/**
* Get the query string for this request.
*
* @param string $separator
*
* @return string
*/
public function getQueryString(string $separator = '&')
{
$queryString = '';
if (count($this->params) > 0) {
$queryString = http_build_query($this->params, null, $separator);
$queryString = preg_replace(
'/%5B(?:[0-9]|[1-9][0-9]+)%5D=/',
'=',
$queryString
);
}
return $queryString;
}
}
<?php
namespace Solarium\Component\RequestBuilder;
/**
* Class for describing a sub request.
*/
class SubRequest implements RequestParamsInterface
{
use RequestParamsTrait;
/**
* Request params.
*
* Multivalue params are supported using a multidimensional array:
* 'fq' => array('cat:1','published:1')
*
* @var array
*/
protected $queryParser = 'rerank';
/**
* Get query parser.
*
* @return string
*/
public function getQueryParser()
{
return $this->queryParser;
}
/**
* Set query parser.
*
* @param string $value
*
* @return self Provides fluent interface
*/
public function setQueryParser(string $value)
{
$this->queryParser = $value;
return $this;
}
/**
* returns the complete sub request as string.
*
* @param string $separator
*
* @return string
*/
public function getSubQuery($separator = ' ')
{
$queryString = '';
foreach ($this->getParams() as $key => $value) {
$queryString .= $separator.$key.'='.$value;
}
if ($queryString) {
$queryString = '{!'.$this->getQueryParser().$queryString.'}';
}
return $queryString;
}
}
......@@ -31,13 +31,6 @@ class Spatial extends AbstractComponent
return new RequestBuilder();
}
/**
* This component has no response parser...
*/
public function getResponseParser()
{
}
/**
* @param string $sfield
*/
......
......@@ -11,9 +11,10 @@ use Solarium\Component\ResponseParser\Spellcheck as ResponseParser;
*
* @see http://wiki.apache.org/solr/SpellcheckComponent
*/
class Spellcheck extends AbstractComponent implements SpellcheckInterface
class Spellcheck extends AbstractComponent implements SpellcheckInterface, QueryInterface
{
use SpellcheckTrait;
use QueryTrait;
/**
* Get component type.
......@@ -44,21 +45,4 @@ class Spellcheck extends AbstractComponent implements SpellcheckInterface
{
return new ResponseParser();
}
/**
* Set spellcheck query option.
*
* @param string $query
* @param array $bind Bind values for placeholders in the query string
*
* @return self Provides fluent interface
*/
public function setQuery($query, $bind = null)
{
if (null !== $bind) {
$query = $this->getQueryInstance()->getHelper()->assemble($query, $bind);
}
return $this->setOption('query', trim($query));
}
}
......@@ -11,9 +11,10 @@ use Solarium\Component\ResponseParser\Suggester as ResponseParser;
*
* @see http://wiki.apache.org/solr/SpellcheckComponent
*/
class Suggester extends AbstractComponent implements SuggesterInterface
class Suggester extends AbstractComponent implements SuggesterInterface, QueryInterface
{
use SuggesterTrait;
use QueryTrait;
/**
* Get component type.
......@@ -44,21 +45,4 @@ class Suggester extends AbstractComponent implements SuggesterInterface
{
return new ResponseParser();
}
/**
* Set spellcheck query option.
*
* @param string $query
* @param array $bind Bind values for placeholders in the query string
*
* @return self Provides fluent interface
*/
public function setQuery($query, $bind = null)
{
if (null !== $bind) {
$query = $this->getQueryInstance()->getHelper()->assemble($query, $bind);
}
return $this->setOption('query', trim($query));
}
}
......@@ -810,8 +810,6 @@ class Client extends Configurable implements ClientInterface
* $result = $client->ping($query);
* </code>
*
* @see Solarium\QueryType\Ping
*
* @param QueryInterface|\Solarium\QueryType\Ping\Query $query
* @param Endpoint|string|null $endpoint
*
......@@ -836,9 +834,6 @@ class Client extends Configurable implements ClientInterface
* $result = $client->update($update);
* </code>
*
* @see Solarium\QueryType\Update
* @see Solarium\Result\Update
*
* @param QueryInterface|\Solarium\QueryType\Update\Query\Query $query
* @param Endpoint|string|null $endpoint
*
......@@ -862,9 +857,6 @@ class Client extends Configurable implements ClientInterface
* $result = $client->select($query);
* </code>
*
* @see Solarium\QueryType\Select
* @see Solarium\Result\Select
*
* @param QueryInterface|\Solarium\QueryType\Select\Query\Query $query
* @param Endpoint|string|null $endpoint
*
......@@ -888,9 +880,6 @@ class Client extends Configurable implements ClientInterface
* $result = $client->moreLikeThis($query);
* </code>
*
* @see Solarium\QueryType\MoreLikeThis
* @see Solarium\Result\MoreLikeThis
*
* @param QueryInterface|\Solarium\QueryType\MoreLikeThis\Query $query
* @param Endpoint|string|null $endpoint
*
......
......@@ -2,14 +2,18 @@
namespace Solarium\Core\Client;
use Solarium\Component\RequestBuilder\RequestParamsInterface;
use Solarium\Component\RequestBuilder\RequestParamsTrait;
use Solarium\Core\Configurable;
use Solarium\Exception\RuntimeException;
/**
* Class for describing a request.
*/
class Request extends Configurable
class Request extends Configurable implements RequestParamsInterface
{
use RequestParamsTrait;
/**
* Request GET method.
*/
......@@ -39,16 +43,6 @@ class Request extends Configurable
*/
protected $headers = [];
/**
* Request params.
*
* Multivalue params are supported using a multidimensional array:
* 'fq' => array('cat:1','published:1')
*
* @var array
*/
protected $params = [];
/**
* Raw POST data.
*
......@@ -121,128 +115,6 @@ class Request extends Configurable
return $this->getOption('method');
}
/**
* Get a param value.
*
* @param string $key
*
* @return string|array
*/
public function getParam($key)
{
if (isset($this->params[$key])) {
return $this->params[$key];
}
}
/**
* Get all params.
*
* @return array
*/
public function getParams()
{
return $this->params;
}
/**
* Set request params.
*
* @param array $params
*
* @return self Provides fluent interface
*/
public function setParams($params)
{
$this->clearParams();
$this->addParams($params);
return $this;
}
/**
* Add a request param.
*
* If you add a request param that already exists the param will be converted into a multivalue param,
* unless you set the overwrite param to true.
*
* Empty params are not added to the request. If you want to empty a param disable it you should use
* remove param instead.
*
* @param string $key
* @param string|array $value
* @param bool $overwrite
*
* @return self Provides fluent interface
*/
public function addParam($key, $value, $overwrite = false)
{
if (null !== $value) {
if (!$overwrite && isset($this->params[$key])) {
if (!is_array($this->params[$key])) {
$this->params[$key] = [$this->params[$key]];
}
$this->params[$key][] = $value;
} else {
// not all solr handlers support 0/1 as boolean values...
if (true === $value) {
$value = 'true';
} elseif (false === $value) {
$value = 'false';
}
$this->params[$key] = $value;
}
}
return $this;
}
/**
* Add multiple params to the request.
*
* @param array $params
* @param bool $overwrite
*
* @return self Provides fluent interface
*/
public function addParams($params, $overwrite = false)
{
foreach ($params as $key => $value) {
$this->addParam($key, $value, $overwrite);
}
return $this;
}
/**
* Remove a param by key.
*
* @param string $key
*
* @return self Provides fluent interface
*/
public function removeParam($key)
{
if (isset($this->params[$key])) {
unset($this->params[$key]);
}
return $this;
}
/**
* Clear all request params.
*
* @return self Provides fluent interface
*/
public function clearParams()
{
$this->params = [];
return $this;
}
/**
* Get raw POST data.
*
......@@ -377,26 +249,6 @@ class Request extends Configurable
return $this->getHandler().'?'.$this->getQueryString();
}
/**
* Get the query string for this request.
*
* @return string
*/
public function getQueryString()
{
$queryString = '';
if (count($this->params) > 0) {
$queryString = http_build_query($this->params, null, '&');
$queryString = preg_replace(
'/%5B(?:[0-9]|[1-9][0-9]+)%5D=/',
'=',
$queryString
);
}
return $queryString;
}
/**
* Set HTTP basic auth settings.
*
......
......@@ -2,41 +2,16 @@
namespace Solarium\QueryType\Analysis\Query;
use Solarium\Component\QueryInterface;
use Solarium\Component\QueryTrait;
use Solarium\Core\Query\AbstractQuery as BaseQuery;
/**
* Base class for Analysis queries.
*/
abstract class AbstractQuery extends BaseQuery
abstract class AbstractQuery extends BaseQuery implements QueryInterface
{
/**
* Set the query string.
*
* When present, the text that will be analyzed. The analysis will mimic the query-time analysis.
*
* @param string $query
* @param array $bind Optional bind values for placeholders in the query string
*
* @return self Provides fluent interface
*/
public function setQuery($query, $bind = null)
{
if (null !== $bind) {
$query = $this->getHelper()->assemble($query, $bind);
}
return $this->setOption('query', trim($query));
}
/**
* Get the query string.
*
* @return string
*/
public function getQuery()
{
return $this->getOption('query');
}
use QueryTrait;
/**
* Set the showmatch option.
......
......@@ -2,6 +2,8 @@
namespace Solarium\QueryType\Select\Query;
use Solarium\Component\QueryInterface;
use Solarium\Component\QueryTrait;
use Solarium\Core\Configurable;
use Solarium\Core\Query\Helper;
......@@ -10,8 +12,10 @@ use Solarium\Core\Query\Helper;
*
* @see http://wiki.apache.org/solr/CommonQueryParameters#fq
*/
class FilterQuery extends Configurable
class FilterQuery extends Configurable implements QueryInterface
{
use QueryTrait;
/**
* Tags for this filterquery.
*
......@@ -48,38 +52,6 @@ class FilterQuery extends Configurable
return $this->setOption('key', $value);
}
/**
* Set the query string.
*
* This overwrites the current value
*
* @param string $query
* @param array $bind Bind values for placeholders in the query string
*
* @return self Provides fluent interface
*/
public function setQuery($query, $bind = null)
{
if (null !== $bind) {
$helper = new Helper();
$query = $helper->assemble($query, $bind);
}
$this->query = trim($query);
return $this;
}
/**
* Get the query string.
*
* @return string
*/
public function getQuery()
{
return $this->query;
}
/**
* Add a tag.
*
......@@ -164,6 +136,16 @@ class FilterQuery extends Configurable
return $this->addTags($tags);
}
/**
* Returns a query helper.
*
* @return \Solarium\Core\Query\Helper
*/
public function getHelper()
{
return new Helper();
}
/**
* Initialize options.
*/
......
......@@ -4,6 +4,8 @@ namespace Solarium\QueryType\Select\Query;
use Solarium\Component\ComponentAwareQueryInterface;
use Solarium\Component\ComponentAwareQueryTrait;
use Solarium\Component\QueryInterface;
use Solarium\Component\QueryTrait;
use Solarium\Component\QueryTraits\DebugTrait;
use Solarium\Component\QueryTraits\DisMaxTrait;
use Solarium\Component\QueryTraits\DistributedSearchTrait;
......@@ -13,6 +15,7 @@ use Solarium\Component\QueryTraits\GroupingTrait;
use Solarium\Component\QueryTraits\HighlightingTrait;
use Solarium\Component\QueryTraits\MoreLikeThisTrait;
use Solarium\Component\QueryTraits\QueryElevationTrait;
use Solarium\Component\QueryTraits\ReRankQueryTrait;
use Solarium\Component\QueryTraits\SpatialTrait;
use Solarium\Component\QueryTraits\SpellcheckTrait;
use Solarium\Component\QueryTraits\StatsTrait;
......@@ -30,7 +33,7 @@ use Solarium\QueryType\Select\ResponseParser;
* lots of options and there are many Solarium subclasses for it.
* See the Solr documentation and the relevant Solarium classes for more info.
*/
class Query extends AbstractQuery implements ComponentAwareQueryInterface
class Query extends AbstractQuery implements ComponentAwareQueryInterface, QueryInterface
{
use ComponentAwareQueryTrait;
use MoreLikeThisTrait;
......@@ -46,6 +49,8 @@ class Query extends AbstractQuery implements ComponentAwareQueryInterface
use DistributedSearchTrait;
use StatsTrait;
use QueryElevationTrait;
use ReRankQueryTrait;
use QueryTrait;
/**
* Solr sort mode descending.
......@@ -127,6 +132,7 @@ class Query extends AbstractQuery implements ComponentAwareQueryInterface
ComponentAwareQueryInterface::COMPONENT_DISTRIBUTEDSEARCH => 'Solarium\Component\DistributedSearch',
ComponentAwareQueryInterface::COMPONENT_STATS => 'Solarium\Component\Stats\Stats',
ComponentAwareQueryInterface::COMPONENT_QUERYELEVATION => 'Solarium\Component\QueryElevation',
ComponentAwareQueryInterface::COMPONENT_RERANKQUERY => 'Solarium\Component\ReRankQuery',
];
parent::__construct($options);
......@@ -162,40 +168,6 @@ class Query extends AbstractQuery implements ComponentAwareQueryInterface
return new ResponseParser();
}
/**
* Set the query string.
*
* Overwrites the current value. You are responsible for the correct
* escaping of user input.
*
* @param string $query
* @param array $bind Bind values for placeholders in the query string
*
* @return self Provides fluent interface
*/
public function setQuery($query, $bind = null)
{
if (null !== $bind) {
$query = $this->getHelper()->assemble($query, $bind);
}
if (null !== $query) {
$query = trim($query);
}
return $this->setOption('query', $query);
}
/**
* Get the query string.
*
* @return string
*/
public function getQuery()
{
return $this->getOption('query');
}
/**
* Set default query operator.
*
......
......@@ -3,6 +3,8 @@
namespace Solarium\QueryType\Spellcheck;
use Solarium\Component\ComponentTraits\SpellcheckTrait;
use Solarium\Component\QueryInterface;
use Solarium\Component\QueryTrait;
use Solarium\Component\SpellcheckInterface;
use Solarium\Core\Client\Client;
use Solarium\Core\Query\AbstractQuery as BaseQuery;
......@@ -12,9 +14,10 @@ use Solarium\Core\Query\AbstractQuery as BaseQuery;
*
* Can be used for an autocomplete feature. See http://wiki.apache.org/solr/SpellcheckComponent for more info.
*/
class Query extends BaseQuery implements SpellcheckInterface
class Query extends BaseQuery implements SpellcheckInterface, QueryInterface
{
use SpellcheckTrait;
use QueryTrait;
/**
* Default options.
......@@ -58,18 +61,4 @@ class Query extends BaseQuery implements SpellcheckInterface
{
return new ResponseParser();
}
/**
* Set query option.
*
* Query to spellcheck
*
* @param string $query
*
* @return self Provides fluent interface
*/
public function setQuery($query)
{
return $this->setOption('query', $query);
}
}
......@@ -3,6 +3,8 @@
namespace Solarium\QueryType\Suggester;
use Solarium\Component\ComponentTraits\SuggesterTrait;
use Solarium\Component\QueryInterface;
use Solarium\Component\QueryTrait;
use Solarium\Component\SuggesterInterface;
use Solarium\Core\Client\Client;
use Solarium\Core\Query\AbstractQuery as BaseQuery;
......@@ -12,9 +14,10 @@ use Solarium\Core\Query\AbstractQuery as BaseQuery;
*
* Can be used for an autocomplete feature. See http://wiki.apache.org/solr/Suggester for more info.
*/
class Query extends BaseQuery implements SuggesterInterface
class Query extends BaseQuery implements SuggesterInterface, QueryInterface
{
use SuggesterTrait;
use QueryTrait;
/**
* Default options.
......@@ -60,18 +63,4 @@ class Query extends BaseQuery implements SuggesterInterface
{
return new ResponseParser();
}
/**
* Set query option.
*
* Query to spellcheck
*
* @param string $query
*
* @return self Provides fluent interface
*/
public function setQuery($query)
{
return $this->setOption('query', $query);
}
}
<?php
namespace Solarium\Tests\Component;
use PHPUnit\Framework\TestCase;
use Solarium\Component\ReRankQuery;
use Solarium\QueryType\Select\Query\Query;
class ReRankQueryTest extends TestCase
{
/**
* @var ReRankQuery
*/
protected $reRankQuery;
public function setUp()
{
$this->reRankQuery = new ReRankQuery();
}
public function testConfigMode()
{
$options = [
'query' => 'foo:bar',
'docs' => 50,
'weight' => '16.3161',
];
$this->reRankQuery->setOptions($options);
$this->assertEquals($options['query'], $this->reRankQuery->getQuery());
$this->assertEquals($options['docs'], $this->reRankQuery->getDocs());
$this->assertEquals($options['weight'], $this->reRankQuery->getWeight());
}
public function testGetType()
{
$this->assertEquals(
Query::COMPONENT_RERANKQUERY,
$this->reRankQuery->getType()
);
}
public function testGetResponseParser()
{
$this->assertNull($this->reRankQuery->getResponseParser());
}
public function testGetRequestBuilder()
{
$this->assertInstanceOf(
'Solarium\Component\RequestBuilder\ReRankQuery',
$this->reRankQuery->getRequestBuilder()
);
}
public function testSetAndGetQuery()
{
$this->reRankQuery->setQuery('category:1');
$this->assertSame('category:1', $this->reRankQuery->getQuery());
}
public function testSetAndGetQueryWithBind()
{
$this->reRankQuery->setQuery('id:%1%', [678]);
$this->assertSame('id:678', $this->reRankQuery->getQuery());
}
public function testSetAndGetDocs()
{
$value = 42;
$this->reRankQuery->setDocs($value);
$this->assertEquals($value, $this->reRankQuery->getDocs());
}
public function testSetAndGetWeight()
{
$value = '52.13';
$this->reRankQuery->setWeight($value);
$this->assertEquals($value, $this->reRankQuery->getWeight());
}
}
......@@ -73,13 +73,13 @@ class FacetSetTest extends TestCase
public function testBuildWithJsonFacets()
{
$this->component->addFacet(new JsonTerms(['key' => 'f1', 'field' => 'owner']));
$this->component->addFacet(new JsonQuery(['key' => 'f2', 'q' => 'category:23']));
$this->component->addFacet(new JsonQuery(['key' => 'f2', 'query' => 'category:23']));
$request = $this->builder->buildComponent($this->component, $this->request);
$this->assertNull($request->getRawData());
$this->assertEquals(
'?json.facet={"f1":{"field":"owner","type":"terms"},"f2":{"q":"category:23","type":"query"}}',
'?json.facet={"f1":{"field":"owner","type":"terms"},"f2":{"type":"query","q":"category:23"}}',
urldecode($request->getUri())
);
}
......@@ -155,7 +155,7 @@ class FacetSetTest extends TestCase
{
$this->component->addFacet(new FacetField(['key' => 'f1', 'field' => 'owner']));
$this->component->addFacet(new JsonTerms(['key' => 'f2', 'field' => 'customer']));
$this->component->addFacet(new JsonQuery(['key' => 'f3', 'q' => 'category:23']));
$this->component->addFacet(new JsonQuery(['key' => 'f3', 'query' => 'category:23']));
$this->component->addFacet(
new FacetMultiQuery(['key' => 'f4', 'query' => ['f5' => ['query' => 'category:40']]])
);
......@@ -164,7 +164,7 @@ class FacetSetTest extends TestCase
$this->assertNull($request->getRawData());
$this->assertEquals(
'?facet.field={!key=f1}owner&facet.query={!key=f5}category:40&facet=true&json.facet={"f2":{"field":"customer","type":"terms"},"f3":{"q":"category:23","type":"query"}}',
'?facet.field={!key=f1}owner&facet.query={!key=f5}category:40&facet=true&json.facet={"f2":{"field":"customer","type":"terms"},"f3":{"type":"query","q":"category:23"}}',
urldecode($request->getUri())
);
}
......@@ -193,7 +193,7 @@ class FacetSetTest extends TestCase
public function testBuildWithNestedJsonFacets()
{
$terms = new JsonTerms(['key' => 'f1', 'field' => 'owner']);
$query = new JsonQuery(['key' => 'f2', 'q' => 'category:23']);
$query = new JsonQuery(['key' => 'f2', 'query' => 'category:23']);
$query->addFacet(new JsonAggregation(['key' => 'f1', 'function' => 'avg(mul(price,popularity))']));
$query->addFacet(new JsonAggregation(['key' => 'f2', 'function' => 'unique(popularity)']));
$terms->addFacet($query);
......@@ -203,7 +203,7 @@ class FacetSetTest extends TestCase
$this->assertNull($request->getRawData());
$this->assertEquals(
'?json.facet={"f1":{"field":"owner","type":"terms","facet":{"f2":{"q":"category:23","type":"query","facet":{"f1":"avg(mul(price,popularity))","f2":"unique(popularity)"}}}}}',
'?json.facet={"f1":{"field":"owner","type":"terms","facet":{"f2":{"type":"query","facet":{"f1":"avg(mul(price,popularity))","f2":"unique(popularity)"},"q":"category:23"}}}}',
urldecode($request->getUri())
);
}
......
<?php
namespace Solarium\Tests\QueryType\Select\RequestBuilder\Component;
use PHPUnit\Framework\TestCase;
use Solarium\Component\RequestBuilder\ReRankQuery as RequestBuilder;
use Solarium\Component\ReRankQuery as Component;
use Solarium\Core\Client\Request;
class ReRankQueryTest extends TestCase
{
public function testBuildComponent()
{
$builder = new RequestBuilder();
$request = new Request();
$component = new Component();
$component->setQuery('foo:bar');
$component->setDocs(42);
$component->setWeight(48.2233);
$request = $builder->buildComponent($component, $request);
$this->assertEquals(
[
'rq' => '{!rerank reRankQuery=foo:bar reRankDocs=42 reRankWeight=48.2233}',
],
$request->getParams()
);
}
}
......@@ -352,6 +352,40 @@ abstract class AbstractTechproductsTest extends TestCase
], $terms);
}
public function testReRankQuery()
{
$select = $this->client->createSelect();
$select->setQuery('inStock:true');
$select->setRows(2);
$result = $this->client->select($select);
$this->assertSame(17, $result->getNumFound());
$this->assertSame(2, $result->count());
$ids = [];
/** @var \Solarium\QueryType\Select\Result\Document $document */
foreach ($result as $document) {
$ids[] = $document->id;
}
$reRankQuery = $select->getReRankQuery();
$reRankQuery->setQuery('popularity:10');
$result = $this->client->select($select);
$this->assertSame(17, $result->getNumFound());
$this->assertSame(2, $result->count());
$rerankedids = [];
/** @var \Solarium\QueryType\Select\Result\Document $document */
foreach ($result as $document) {
$rerankedids[] = $document->id;
}
$this->assertNotSame($ids, $rerankedids);
// These two ducuments have a popularity of 10 and should ranked highest.
$this->assertArraySubset([
'MA147LL/A',
'SOLR1000',
], $rerankedids);
}
public function testPrefetchIterator()
{
$select = $this->client->createSelect();
......
......@@ -670,6 +670,26 @@ abstract class AbstractQueryTest extends TestCase
);
}
public function testGetSpatial()
{
$spatial = $this->query->getSpatial();
$this->assertSame(
'Solarium\Component\Spatial',
get_class($spatial)
);
}
public function testGetReRankQuery()
{
$reRankQuery = $this->query->getReRankQuery();
$this->assertSame(
'Solarium\Component\ReRankQuery',
get_class($reRankQuery)
);
}
public function testAddTag()
{
$this->query->addTag('testtag');
......@@ -721,14 +741,4 @@ abstract class AbstractQueryTest extends TestCase
$this->query->setSplitOnWhitespace(false);
$this->assertFalse($this->query->getSplitOnWhitespace());
}
public function testGetSpatial()
{
$spatial = $this->query->getSpatial();
$this->assertSame(
'Solarium\Component\Spatial',
get_class($spatial)
);
}
}
<?php
namespace Solarium\Tests\QueryType\Select\Query\Component;
namespace Solarium\Tests\QueryType\Select\Query\Component\DisMax;
use PHPUnit\Framework\TestCase;
use Solarium\Component\BoostQuery;
use Solarium\Component\DisMax\BoostQuery;
class BoostQueryTest extends TestCase
{
......
......@@ -3,7 +3,7 @@
namespace Solarium\Tests\QueryType\Select\Query\Component;
use PHPUnit\Framework\TestCase;
use Solarium\Component\BoostQuery;
use Solarium\Component\DisMax\BoostQuery;
use Solarium\Component\DisMax;
use Solarium\Exception\InvalidArgumentException;
use Solarium\QueryType\Select\Query\Query;
......
......@@ -22,13 +22,13 @@ class JsonQueryTest extends TestCase
{
$options = [
'key' => 'myKey',
'q' => 'category:1',
'query' => 'category:1',
];
$this->facet->setOptions($options);
$this->assertSame($options['key'], $this->facet->getKey());
$this->assertSame($options['q'], $this->facet->getQuery());
$this->assertSame($options['query'], $this->facet->getQuery());
}
public function testGetType()
......
......@@ -499,7 +499,7 @@ class FacetSetTest extends TestCase
$this->assertInstanceOf(JsonQuery::class, $result);
$this->assertSame(1, $result->getOption('optionA'));
$this->assertSame(2, $result->getOption('optionB'));
$this->assertSame('*:*', $result->getOption('q'));
$this->assertSame('*:*', $result->getOption('query'));
if ($add) {
$this->assertInstanceOf(JsonQuery::class, $facetSet->getFacet('key'));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment