From 6bdb4cd1621292e5f2757ce94efe82a81327672d Mon Sep 17 00:00:00 2001 From: thomascorthals Date: Wed, 27 Nov 2024 15:49:34 +0100 Subject: [PATCH] Add cancellable and execution limit params (#1136) Co-authored-by: Markus Kalkbrenner --- CHANGELOG.md | 10 +- .../building-a-select-query.md | 40 +-- .../result-of-a-select-query.md | 8 + src/Core/Query/AbstractQuery.php | 28 +-- src/Core/Query/AbstractRequestBuilder.php | 2 - src/QueryType/Select/Query/Query.php | 234 ++++++++++++++++-- src/QueryType/Select/RequestBuilder.php | 10 +- src/QueryType/Select/Result/Result.php | 52 ++++ tests/Core/Query/QueryTest.php | 32 ++- tests/Core/Query/RequestBuilderTest.php | 28 --- .../AbstractTechproductsTestCase.php | 37 +++ .../Select/Query/AbstractQueryTestCase.php | 112 ++++++++- tests/QueryType/Select/RequestBuilderTest.php | 48 ++++ .../Select/Result/AbstractResultTestCase.php | 68 +++++ 14 files changed, 593 insertions(+), 116 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 837a2c77a..40b7e3a80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added - PHP 8.4 support -- Solarium\Core\Query\AbstractQuery::setCpuAllowed() +- Solarium\QueryType\Select\Query\Quey::setCanCancel() +- Solarium\QueryType\Select\Query\Quey::setQueryUuid() +- Solarium\QueryType\Select\Query\Quey::setPartialResults() +- Solarium\QueryType\Select\Query\Quey::setCpuAllowed() +- Solarium\QueryType\Select\Query\Quey::setMemAllowed() +- Solarium\QueryType\Select\Query\Quey::setSegmentTerminateEarly() +- Solarium\QueryType\Select\Query\Quey::setMultiThreaded() ### Fixed - JSON update requests correctly handle `Stringable` object set as field value @@ -18,6 +24,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed - Support for config objects, you have to convert them to an array before passing to a constructor or `setOptions()` +### Deprecated +- Solarium\Core\Query\AbstractQuery::setTimeAllowed(), moved to Solarium\QueryType\Select\Query\Query ## [6.3.5] ### Added diff --git a/docs/queries/select-query/building-a-select-query/building-a-select-query.md b/docs/queries/select-query/building-a-select-query/building-a-select-query.md index 877500f0e..42cae699a 100644 --- a/docs/queries/select-query/building-a-select-query/building-a-select-query.md +++ b/docs/queries/select-query/building-a-select-query/building-a-select-query.md @@ -5,22 +5,30 @@ Options The options below can be set as query option values, but also by using the set/get methods. See the API docs for all available methods. -| Name | Type | Default value | Description | -|----------------------|------------------|-------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| handler | string | select | Name of the Solr request handler to use, without leading or trailing slashes | -| resultclass | string | Solarium\_Result\_Select | Classname for result. If you set a custom classname make sure the class is readily available (or through autoloading) | -| documentclass | string | Solarium\_Document\_ReadWrite | Classname for documents in the resultset. If you set a custom classname make sure the class is readily available (or through autoloading) | -| query | string | \*:\* | Query to execute | -| start | int | 0 | Start position (offset) in the complete Solr query resultset, to paginate big resultsets. | -| rows | integer | 10 | Number of rows to fetch, starting from the 'start' (offset) position. It's a limit, you might get less. | -| fields | string | - ,score | Comma separated list of fields to fetch from Solr. There are two special values: '\*' meaning 'all fields' and 'score' to also fetch the Solr document score value. | -| sort | array | empty array | Array with sort field as key and sort order as values. Multiple entries possible, they are used in the order of the array. Example: array('price' => 'asc') | -| querydefaultoperator | string | null | With a null value the default of your Solr config will be used. If you want to override this supply 'AND' or 'OR' as the value. | -| querydefaultfield | string | null | With a null value the default of your Solr config will be used. If you want to override this supply a field name as the value. | -| responsewriter | string | json | You can set this to 'phps' for improved response parsing performance, at the cost of a (possible) security risk. Only use 'phps' for trusted Solr instances. | -| tag | array of strings | null | You can supply one or multiple tags for the main query string, to allow for exclusion of the main query in facets | -| cursormark | string | null | Set to '\*' and make sure sort contains a uniqueKey field to enable cursor functionality when passing the query to the PrefetchIterator plugin. Available since Solr 4.7. | -| splitonwhitespace | bool | null | Specifies whether the query parser splits the query text on whitespace before it's sent to be analyzed. Available for 'lucene' and 'edismax' query parsers since Solr 6.5. | +| Name | Type | Default value | Description | +|-----------------------|------------------|-----------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| handler | string | select | Name of the Solr request handler to use, without leading or trailing slashes | +| resultclass | string | Solarium\\QueryType\\Select\\Result\\Document | Classname for result. If you set a custom classname make sure the class is readily available (or through autoloading) | +| documentclass | string | Solarium\\QueryType\\Select\\Result\\Result | Classname for documents in the resultset. If you set a custom classname make sure the class is readily available (or through autoloading) | +| query | string | \*:\* | Query to execute | +| start | int | 0 | Start position (offset) in the complete Solr query resultset, to paginate big resultsets. | +| rows | integer | 10 | Number of rows to fetch, starting from the 'start' (offset) position. It's a limit, you might get less. | +| cancancel | bool | null | Is this a cancellable query? | +| queryuuid | string | null | Custom UUID to identify a cancellable query with | +| fields | string | \*,score | Comma separated list of fields to fetch from Solr. There are two special values: '\*' meaning 'all fields' and 'score' to also fetch the Solr document score value. | +| sort | array | empty array | Array with sort field as key and sort order as values. Multiple entries possible, they are used in the order of the array. Example: array('price' => 'asc') | +| querydefaultoperator | string | null | With a null value the default of your Solr config will be used. If you want to override this supply 'AND' or 'OR' as the value. | +| querydefaultfield | string | null | With a null value the default of your Solr config will be used. If you want to override this supply a field name as the value. | +| responsewriter | string | json | You can set this to 'phps' for improved response parsing performance, at the cost of a (possible) security risk. Only use 'phps' for trusted Solr instances. | +| tag | array of strings | null | You can supply one or multiple tags for the main query string, to allow for exclusion of the main query in facets | +| partialresults | bool | null | If set to false, reaching a query execution limit will generate an exception instead of returning partial results | +| timeallowed | int | null | Amount of time, in milliseconds, allowed for a search to complete | +| cpuallowed | int | null | Amount of CPU time, in milliseconds, allowed for a search to complete | +| memallowed | float | null | Amount of memory, in MiB, allowed for a search thread to allocate during query execution | +| segmentterminateearly | bool | null | If set to true, the search may be terminated early within a segment | +| multithreaded | bool | null | Controls if Solr may use more than one thread to satisfy the request | +| cursormark | string | null | Set to '\*' and make sure sort contains a uniqueKey field to enable cursor functionality when passing the query to the PrefetchIterator plugin. Available since Solr 4.7. | +| splitonwhitespace | bool | null | Specifies whether the query parser splits the query text on whitespace before it's sent to be analyzed. Available for 'lucene' and 'edismax' query parsers since Solr 6.5. | || Examples diff --git a/docs/queries/select-query/result-of-a-select-query/result-of-a-select-query.md b/docs/queries/select-query/result-of-a-select-query/result-of-a-select-query.md index f379fd363..001278032 100644 --- a/docs/queries/select-query/result-of-a-select-query/result-of-a-select-query.md +++ b/docs/queries/select-query/result-of-a-select-query/result-of-a-select-query.md @@ -11,6 +11,14 @@ Solr status code. This is not the HTTP status code! The normal value for success Solr index query time. This doesn't include things like the HTTP response time. Only available if your Solr server sends headers (`omitHeader=false`). +### PartialResults + +Solr sets this flag if partial results are returned because an execution limit is reached. Only available if your Solr server sends headers (`omitHeader=false`). + +### SegmentTerminatedEarly + +Solr sets this flag if early segment termination happens. Only available if your Solr server sends headers (`omitHeader=false`). + ### NumFound Total number of documents that matched the query. This is not necessarily the same as the number of document in the resultset, depending on you query settings! diff --git a/src/Core/Query/AbstractQuery.php b/src/Core/Query/AbstractQuery.php index 8a7b40cdb..97658eda6 100644 --- a/src/Core/Query/AbstractQuery.php +++ b/src/Core/Query/AbstractQuery.php @@ -98,6 +98,8 @@ public function getResultClass(): ?string * @param int $value * * @return self Provides fluent interface + * + * @deprecated Will be removed in Solarium 7. This parameter is only relevant for Select queries. */ public function setTimeAllowed(int $value): self { @@ -110,36 +112,14 @@ public function setTimeAllowed(int $value): self * Get timeAllowed option. * * @return int|null + * + * @deprecated Will be removed in Solarium 7. This parameter is only relevant for Select queries. */ public function getTimeAllowed(): ?int { return $this->getOption('timeallowed'); } - /** - * Set cpuAllowed option. - * - * @param int $value - * - * @return self Provides fluent interface - */ - public function setCpuAllowed(int $value): self - { - $this->setOption('cpuallowed', $value); - - return $this; - } - - /** - * Get cpuAllowed option. - * - * @return int|null - */ - public function getCpuAllowed(): ?int - { - return $this->getOption('cpuallowed'); - } - /** * Set omitHeader option. * diff --git a/src/Core/Query/AbstractRequestBuilder.php b/src/Core/Query/AbstractRequestBuilder.php index ff7369d8b..54cd018ee 100644 --- a/src/Core/Query/AbstractRequestBuilder.php +++ b/src/Core/Query/AbstractRequestBuilder.php @@ -38,8 +38,6 @@ public function build(AbstractQuery $query): Request $request->setHandler($query->getHandler()); $request->addParam('distrib', $query->getDistrib()); $request->addParam('omitHeader', $query->getOmitHeader()); - $request->addParam('timeAllowed', $query->getTimeAllowed()); - $request->addParam('cpuAllowed', $query->getCpuAllowed()); $request->addParam('NOW', $query->getNow()); $request->addParam('TZ', $query->getTimeZone()); $request->addParam('ie', $query->getInputEncoding()); diff --git a/src/QueryType/Select/Query/Query.php b/src/QueryType/Select/Query/Query.php index 72443e442..bdab0887b 100644 --- a/src/QueryType/Select/Query/Query.php +++ b/src/QueryType/Select/Query/Query.php @@ -205,6 +205,34 @@ public function getResponseParser(): ?ResponseParserInterface return new ResponseParser(); } + /** + * Set a custom document class. + * + * This class should implement the document interface + * + * @param string $value classname + * + * @return self Provides fluent interface + */ + public function setDocumentClass(string $value): self + { + $this->setOption('documentclass', $value); + + return $this; + } + + /** + * Get the current documentclass option. + * + * The value is a classname, not an instance + * + * @return string|null + */ + public function getDocumentClass(): ?string + { + return $this->getOption('documentclass'); + } + /** * Set default query operator. * @@ -280,55 +308,75 @@ public function getStart(): ?int } /** - * Set a custom document class. - * - * This class should implement the document interface + * Set the number of rows to fetch. * - * @param string $value classname + * @param int $rows * * @return self Provides fluent interface */ - public function setDocumentClass(string $value): self + public function setRows(int $rows): self { - $this->setOption('documentclass', $value); + $this->setOption('rows', $rows); return $this; } /** - * Get the current documentclass option. + * Get the number of rows. * - * The value is a classname, not an instance + * @return int|null + */ + public function getRows(): ?int + { + return $this->getOption('rows'); + } + + /** + * Set the canCancel option. * - * @return string|null + * @param bool $canCancel + * + * @return self Provides fluent interface */ - public function getDocumentClass(): ?string + public function setCanCancel(bool $canCancel): self { - return $this->getOption('documentclass'); + $this->setOption('cancancel', $canCancel); + + return $this; } /** - * Set the number of rows to fetch. + * Get the canCancel option. * - * @param int $rows + * @return bool|null + */ + public function getCanCancel(): ?bool + { + return $this->getOption('cancancel'); + } + + /** + * Set the queryUUID option. + * + * @param string $queryUuid * * @return self Provides fluent interface */ - public function setRows(int $rows): self + public function setQueryUuid(string $queryUuid): self { - $this->setOption('rows', $rows); + $this->setOption('queryuuid', $queryUuid); return $this; } /** - * Get the number of rows. + * Get the queryUUID option. * - * @return int|null + * @return string|null */ - public function getRows(): ?int + public function getQueryUuid(): ?string { - return $this->getOption('rows'); + return $this->getOption('queryuuid'); } /** @@ -779,6 +827,154 @@ public function setTags(array $tags): self return $this; } + /** + * Set the partialResults option. + * + * @param bool $partialResults + * + * @return self Provides fluent interface + */ + public function setPartialResults(bool $partialResults): self + { + $this->setOption('partialresults', $partialResults); + + return $this; + } + + /** + * Get the partalResults option. + * + * @return bool|null + */ + public function getPartialResults(): ?bool + { + return $this->getOption('partialresults'); + } + + /** + * Set timeAllowed option. + * + * @param int $value + * + * @return self Provides fluent interface + * + * @not-deprecated + */ + public function setTimeAllowed(int $value): self + { + $this->setOption('timeallowed', $value); + + return $this; + } + + /** + * Get timeAllowed option. + * + * @return int|null + * + * @not-deprecated + */ + public function getTimeAllowed(): ?int + { + return $this->getOption('timeallowed'); + } + + /** + * Set cpuAllowed option. + * + * @param int $value + * + * @return self Provides fluent interface + */ + public function setCpuAllowed(int $value): self + { + $this->setOption('cpuallowed', $value); + + return $this; + } + + /** + * Get cpuAllowed option. + * + * @return int|null + */ + public function getCpuAllowed(): ?int + { + return $this->getOption('cpuallowed'); + } + + /** + * Set memAllowed option. + * + * @param float $value + * + * @return self Provides fluent interface + */ + public function setMemAllowed(float $value): self + { + $this->setOption('memallowed', $value); + + return $this; + } + + /** + * Get memAllowed option. + * + * @return float|null + */ + public function getMemAllowed(): ?float + { + return $this->getOption('memallowed'); + } + + /** + * Set the segmentTerminateEarly option. + * + * @param bool $segmentTerminateEarly + * + * @return self Provides fluent interface + */ + public function setSegmentTerminateEarly(bool $segmentTerminateEarly): self + { + $this->setOption('segmentterminateearly', $segmentTerminateEarly); + + return $this; + } + + /** + * Get the segmentTerminateEarly option. + * + * @return bool|null + */ + public function getSegmentTerminateEarly(): ?bool + { + return $this->getOption('segmentterminateearly'); + } + + /** + * Set the multiThreaded option. + * + * @param bool $multiThreaded + * + * @return self Provides fluent interface + */ + public function setMultiThreaded(bool $multiThreaded): self + { + $this->setOption('multithreaded', $multiThreaded); + + return $this; + } + + /** + * Get the multiThreaded option. + * + * @return bool|null + */ + public function getMultiThreaded(): ?bool + { + return $this->getOption('multithreaded'); + } + /** * Set the cursor mark to fetch. * diff --git a/src/QueryType/Select/RequestBuilder.php b/src/QueryType/Select/RequestBuilder.php index d3d6a5a1d..a10c5a696 100644 --- a/src/QueryType/Select/RequestBuilder.php +++ b/src/QueryType/Select/RequestBuilder.php @@ -23,7 +23,7 @@ class RequestBuilder extends BaseRequestBuilder /** * Build request for a select query. * - * @param QueryInterface|SelectQuery $query + * @param QueryInterface&SelectQuery $query * * @return Request */ @@ -41,9 +41,17 @@ public function build(AbstractQuery $query): Request ); $request->addParam('start', $query->getStart()); $request->addParam('rows', $query->getRows()); + $request->addParam('canCancel', $query->getCanCancel()); + $request->addParam('queryUUID', $query->getQueryUUID()); $request->addParam('fl', implode(',', $query->getFields())); $request->addParam('q.op', $query->getQueryDefaultOperator()); $request->addParam('df', $query->getQueryDefaultField()); + $request->addParam('partialResults', $query->getPartialResults()); + $request->addParam('timeAllowed', $query->getTimeAllowed()); + $request->addParam('cpuAllowed', $query->getCpuAllowed()); + $request->addParam('memAllowed', $query->getMemAllowed()); + $request->addParam('segmentTerminateEarly', $query->getSegmentTerminateEarly()); + $request->addParam('multiThreaded', $query->getMultiThreaded()); $request->addParam('cursorMark', $query->getCursorMark()); $request->addParam('sow', $query->getSplitOnWhitespace()); diff --git a/src/QueryType/Select/Result/Result.php b/src/QueryType/Select/Result/Result.php index c74349c6a..b01cd88f1 100644 --- a/src/QueryType/Select/Result/Result.php +++ b/src/QueryType/Select/Result/Result.php @@ -84,6 +84,58 @@ class Result extends BaseResult implements \IteratorAggregate, \Countable */ protected $components; + /** + * Return the value of the partialResults header if present in the response header. + * + * @return bool|null + */ + public function getPartialResults(): ?bool + { + $this->parseResponse(); + + return $this->responseHeader['partialResults'] ?? null; + } + + /** + * Was a query execution limit reached for this search? + * + * This method can only return a correct result if the query had omitHeader=false. + * + * @see https://solr.apache.org/guide/solr/latest/query-guide/common-query-parameters.html#partialresults-parameter + * + * @return bool + */ + public function isPartialResults(): bool + { + return (bool) $this->getPartialResults(); + } + + /** + * Return the value of the segmentTerminatedEarly header if present in the response header. + * + * @return bool|null + */ + public function getSegmentTerminatedEarly(): ?bool + { + $this->parseResponse(); + + return $this->responseHeader['segmentTerminatedEarly'] ?? null; + } + + /** + * Did early segment termination happen for this search? + * + * This method can only return a correct result if the query had omitHeader=false. + * + * @see https://solr.apache.org/guide/solr/latest/query-guide/common-query-parameters.html#segmentterminateearly-parameter + * + * @return bool + */ + public function isSegmentTerminatedEarly(): bool + { + return (bool) $this->getSegmentTerminatedEarly(); + } + /** * get Solr numFound. * diff --git a/tests/Core/Query/QueryTest.php b/tests/Core/Query/QueryTest.php index d0c00ec3a..06ae34bf1 100644 --- a/tests/Core/Query/QueryTest.php +++ b/tests/Core/Query/QueryTest.php @@ -23,6 +23,19 @@ public function testSetAndGetResultClass() $this->assertSame('myResultClass', $query->getResultClass()); } + public function testGetDefaultOmitHeader() + { + $query = new TestQuery(); + $this->assertNull($query->getOmitHeader()); + } + + public function testSetAndGetOmitHeader() + { + $query = new TestQuery(); + $query->setOmitHeader(false); + $this->assertFalse($query->getOmitHeader()); + } + public function testGetHelper() { $query = new TestQuery(); @@ -76,12 +89,18 @@ public function testSetAndGetResponseWriter() $this->assertSame('phps', $query->getResponseWriter()); } + /** + * @deprecated Will be removed in Solarium 7. This parameter is only relevant for Select queries. + */ public function testGetDefaultTimeAllowed() { $query = new TestQuery(); $this->assertNull($query->getTimeAllowed()); } + /** + * @deprecated Will be removed in Solarium 7. This parameter is only relevant for Select queries. + */ public function testSetAndGetTimeAllowed() { $query = new TestQuery(); @@ -89,19 +108,6 @@ public function testSetAndGetTimeAllowed() $this->assertSame(1200, $query->getTimeAllowed()); } - public function testGetDefaultCpuAllowed() - { - $query = new TestQuery(); - $this->assertNull($query->getCpuAllowed()); - } - - public function testSetAndGetCpuAllowed() - { - $query = new TestQuery(); - $query->setCpuAllowed(500); - $this->assertSame(500, $query->getCpuAllowed()); - } - public function testSetAndGetNow() { $query = new TestQuery(); diff --git a/tests/Core/Query/RequestBuilderTest.php b/tests/Core/Query/RequestBuilderTest.php index d132d2d66..9f83fb8f1 100644 --- a/tests/Core/Query/RequestBuilderTest.php +++ b/tests/Core/Query/RequestBuilderTest.php @@ -92,34 +92,6 @@ public function testBuildWithHeader() ); } - public function testBuildWithTimeAllowed() - { - $query = new SelectQuery(); - $query->addParam('p1', 'v1'); - $query->addParam('p2', 'v2'); - $query->setTimeAllowed(1400); - $request = $this->builder->build($query); - - $this->assertSame( - 'select?omitHeader=true&timeAllowed=1400&p1=v1&p2=v2&wt=json&json.nl=flat', - urldecode($request->getUri()) - ); - } - - public function testBuildWithCpuAllowed() - { - $query = new SelectQuery(); - $query->addParam('p1', 'v1'); - $query->addParam('p2', 'v2'); - $query->setCpuAllowed(600); - $request = $this->builder->build($query); - - $this->assertSame( - 'select?omitHeader=true&cpuAllowed=600&p1=v1&p2=v2&wt=json&json.nl=flat', - urldecode($request->getUri()) - ); - } - public function testBuildWithNow() { $query = new SelectQuery(); diff --git a/tests/Integration/AbstractTechproductsTestCase.php b/tests/Integration/AbstractTechproductsTestCase.php index b2f5e9afb..64e0d0e05 100644 --- a/tests/Integration/AbstractTechproductsTestCase.php +++ b/tests/Integration/AbstractTechproductsTestCase.php @@ -1594,6 +1594,43 @@ public function testTermVectorComponent(string $responseWriter) } } + /** + * @dataProvider responseWriterProvider + * + * @group skip_for_solr_cloud + */ + public function testPartialResults(string $responseWriter) + { + // create an expensive query + $select = self::$client->createSelect(); + $select->setResponseWriter($responseWriter); + $select->setOmitHeader(false); + $select->createFilterQuery('feature')->setCache(false)->setQuery( + 'features:*power* cat:*electronics*' + ); + $select->createFilterQuery('region')->setCache(false)->setQuery( + $select->getHelper()->geofilt('store', 45.15, -93.85, 50) + ); + $select->getFacetSet()->createFacetField('cat')->setField('cat'); + $select->addField('termfreq(cat,\'electronics\')'); + $select->addField('totaltermfreq(cat,\'electronics\')'); + $select->addSort( + 'div(termfreq(features,\'power\'),totaltermfreq(features,\'power\'))', + $select::SORT_ASC + ); + $select->addSort('id', $select::SORT_DESC); + + $result = self::$client->select($select); + $this->assertFalse($result->isPartialResults()); + + // give the query 1 millisecond of time & CPU time to complete + $select->setTimeAllowed(1); + $select->setCpuAllowed(1); + + $result = self::$client->select($select); + $this->assertTrue($result->isPartialResults()); + } + /** * @dataProvider crossRequestFormatResponseWriterProvider */ diff --git a/tests/QueryType/Select/Query/AbstractQueryTestCase.php b/tests/QueryType/Select/Query/AbstractQueryTestCase.php index 7942750de..a26a9344d 100644 --- a/tests/QueryType/Select/Query/AbstractQueryTestCase.php +++ b/tests/QueryType/Select/Query/AbstractQueryTestCase.php @@ -39,10 +39,16 @@ public function testGetRequestBuilder() ); } - public function testSetAndGetStart() + public function testSetAndGetResultClass() { - $this->query->setStart(234); - $this->assertSame(234, $this->query->getStart()); + $this->query->setResultClass('MyResult'); + $this->assertSame('MyResult', $this->query->getResultClass()); + } + + public function testSetAndGetDocumentClass() + { + $this->query->setDocumentClass('MyDocument'); + $this->assertSame('MyDocument', $this->query->getDocumentClass()); } public function testSetAndGetQueryWithTrim() @@ -73,16 +79,10 @@ public function testSetAndGetQueryDefaultField() $this->assertSame($value, $this->query->getQueryDefaultField()); } - public function testSetAndGetResultClass() - { - $this->query->setResultClass('MyResult'); - $this->assertSame('MyResult', $this->query->getResultClass()); - } - - public function testSetAndGetDocumentClass() + public function testSetAndGetStart() { - $this->query->setDocumentClass('MyDocument'); - $this->assertSame('MyDocument', $this->query->getDocumentClass()); + $this->query->setStart(234); + $this->assertSame(234, $this->query->getStart()); } public function testSetAndGetRows() @@ -91,6 +91,28 @@ public function testSetAndGetRows() $this->assertSame(100, $this->query->getRows()); } + public function testGetDefaultCanCancel() + { + $this->assertNull($this->query->getCanCancel()); + } + + public function testSetAndGetCanCancel() + { + $this->query->setCanCancel(true); + $this->assertTrue($this->query->getCanCancel()); + } + + public function testGetDefaultQueryUuid() + { + $this->assertNull($this->query->getQueryUuid()); + } + + public function testSetAndGetQueryUuid() + { + $this->query->setQueryUuid('foobar'); + $this->assertSame('foobar', $this->query->getQueryUuid()); + } + public function testAddField() { $expectedFields = $this->query->getFields(); @@ -750,6 +772,72 @@ public function testSetTags() $this->assertSame(['t3', 't4'], $this->query->getTags()); } + public function testGetDefaultPartialResults() + { + $this->assertNull($this->query->getPartialResults()); + } + + public function testSetAndGetPartialResults() + { + $this->query->setPartialResults(true); + $this->assertTrue($this->query->getPartialResults()); + } + + public function testGetDefaultTimeAllowed() + { + $this->assertNull($this->query->getTimeAllowed()); + } + + public function testSetAndGetTimeAllowed() + { + $this->query->setTimeAllowed(1200); + $this->assertSame(1200, $this->query->getTimeAllowed()); + } + + public function testGetDefaultCpuAllowed() + { + $this->assertNull($this->query->getCpuAllowed()); + } + + public function testSetAndGetCpuAllowed() + { + $this->query->setCpuAllowed(500); + $this->assertSame(500, $this->query->getCpuAllowed()); + } + + public function testGetDefaultMemAllowed() + { + $this->assertNull($this->query->getMemAllowed()); + } + + public function testSetAndGetMemAllowed() + { + $this->query->setMemAllowed(2.5); + $this->assertSame(2.5, $this->query->getMemAllowed()); + } + + public function testGetDefaultSegmentTerminateEarly() + { + $this->assertNull($this->query->getSegmentTerminateEarly()); + } + + public function testSetAndGetSegmentTerminateEarly() + { + $this->query->setSegmentTerminateEarly(true); + $this->assertTrue($this->query->getSegmentTerminateEarly()); + } + + public function testGetDefaultMultiThreaded() + { + $this->assertNull($this->query->getMultiThreaded()); + } + + public function testSetAndGetMultiThreaded() + { + $this->query->setMultiThreaded(true); + $this->assertTrue($this->query->getMultiThreaded()); + } + public function testSetCursorMark() { $this->query->setCursorMark('*'); diff --git a/tests/QueryType/Select/RequestBuilderTest.php b/tests/QueryType/Select/RequestBuilderTest.php index 517f82834..e9dd62c79 100644 --- a/tests/QueryType/Select/RequestBuilderTest.php +++ b/tests/QueryType/Select/RequestBuilderTest.php @@ -151,6 +151,18 @@ public function testWithSuggesterComponent() ); } + public function testWithCancellableQuery() + { + $this->query->setCanCancel(true); + $this->query->setQueryUuid('foobar'); + $request = $this->builder->build($this->query); + + $this->assertSame( + 'select?omitHeader=true&wt=json&json.nl=flat&q=*:*&start=0&rows=10&canCancel=true&queryUUID=foobar&fl=*,score', + urldecode($request->getUri()) + ); + } + public function testWithTags() { $this->query->setTags(['t1', 't2']); @@ -160,6 +172,42 @@ public function testWithTags() $this->assertSame('{!tag=t1,t2}cat:1', $request->getParam('q')); } + public function testWithExecutionLimits() + { + $this->query->setPartialResults(true); + $this->query->setTimeAllowed(1000); + $this->query->setCpuAllowed(500); + $this->query->setMemAllowed(2.5); + $request = $this->builder->build($this->query); + + $this->assertSame( + 'select?omitHeader=true&wt=json&json.nl=flat&q=*:*&start=0&rows=10&fl=*,score&partialResults=true&timeAllowed=1000&cpuAllowed=500&memAllowed=2.5', + urldecode($request->getUri()) + ); + } + + public function testWithSegmentTerminateEarly() + { + $this->query->setSegmentTerminateEarly(true); + $request = $this->builder->build($this->query); + + $this->assertSame( + 'select?omitHeader=true&wt=json&json.nl=flat&q=*:*&start=0&rows=10&fl=*,score&segmentTerminateEarly=true', + urldecode($request->getUri()) + ); + } + + public function testWithMultiThreaded() + { + $this->query->setMultiThreaded(true); + $request = $this->builder->build($this->query); + + $this->assertSame( + 'select?omitHeader=true&wt=json&json.nl=flat&q=*:*&start=0&rows=10&fl=*,score&multiThreaded=true', + urldecode($request->getUri()) + ); + } + public function testWithCursorMark() { $this->query->setCursorMark('*'); diff --git a/tests/QueryType/Select/Result/AbstractResultTestCase.php b/tests/QueryType/Select/Result/AbstractResultTestCase.php index a0981486f..b8025396a 100644 --- a/tests/QueryType/Select/Result/AbstractResultTestCase.php +++ b/tests/QueryType/Select/Result/AbstractResultTestCase.php @@ -222,6 +222,54 @@ public function testGetQueryTime() $this->result->getQueryTime() ); } + + public function testNoPartialResults() + { + $this->assertNull( + $this->result->getPartialResults() + ); + + $this->assertFalse( + $this->result->isPartialResults() + ); + } + + public function testGetAndIsPartialResults() + { + $result = new SelectPartialResultsDummy(0, 5, 0, null, [], []); + + $this->assertTrue( + $result->getPartialResults() + ); + + $this->assertTrue( + $result->isPartialResults() + ); + } + + public function testNoSegmentTerminatedEarly() + { + $this->assertNull( + $this->result->getSegmentTerminatedEarly() + ); + + $this->assertFalse( + $this->result->isSegmentTerminatedEarly() + ); + } + + public function testGetSegmentTerminatedEarly() + { + $result = new SelectSegmentTerminatedEarlyDummy(0, 5, 0, null, [], []); + + $this->assertTrue( + $result->getSegmentTerminatedEarly() + ); + + $this->assertTrue( + $result->isSegmentTerminatedEarly() + ); + } } class SelectDummy extends Result @@ -237,3 +285,23 @@ public function __construct($status, $queryTime, $numfound, $maxscore, $docs, $c $this->responseHeader = ['status' => $status, 'QTime' => $queryTime]; } } + +class SelectPartialResultsDummy extends SelectDummy +{ + public function __construct($status, $queryTime, $numfound, $maxscore, $docs, $components) + { + parent::__construct($status, $queryTime, $numfound, $maxscore, $docs, $components); + + $this->responseHeader['partialResults'] = true; + } +} + +class SelectSegmentTerminatedEarlyDummy extends SelectDummy +{ + public function __construct($status, $queryTime, $numfound, $maxscore, $docs, $components) + { + parent::__construct($status, $queryTime, $numfound, $maxscore, $docs, $components); + + $this->responseHeader['segmentTerminatedEarly'] = true; + } +}