Commit d960f5f9 authored by Jeremy Mikola's avatar Jeremy Mikola

PHPLIB-286: Support hint option for Find and FindOne

parent 5731a023
......@@ -80,6 +80,19 @@ interface: phpmethod
operation: ~
optional: true
---
arg_name: option
name: hint
type: string|array|object
description: |
The index to use. Specify either the index name as a string or the index key
pattern as a document. If specified, then the query system will only consider
plans using the hinted index.
.. versionadded:: 1.2
interface: phpmethod
operation: ~
optional: true
---
source:
file: apiargs-common-option.yaml
ref: maxTimeMS
......
......@@ -18,6 +18,10 @@ source:
file: apiargs-MongoDBCollection-method-find-option.yaml
ref: comment
---
source:
file: apiargs-MongoDBCollection-method-find-option.yaml
ref: hint
---
source:
file: apiargs-common-option.yaml
ref: maxTimeMS
......
......@@ -70,6 +70,10 @@ class Find implements Executable
* NON_TAILABLE, TAILABLE, or TAILABLE_AWAIT. The default is
* NON_TAILABLE.
*
* * hint (string|document): The index to use. Specify either the index
* name as a string or the index key pattern as a document. If specified,
* then the query system will only consider plans using the hinted index.
*
* * limit (integer): The maximum number of documents to return.
*
* * maxTimeMS (integer): The maximum amount of time to allow the query to
......@@ -146,6 +150,10 @@ class Find implements Executable
}
}
if (isset($options['hint']) && ! is_string($options['hint']) && ! is_array($options['hint']) && ! is_object($options['hint'])) {
throw InvalidArgumentException::invalidType('"hint" option', $options['hint'], 'string or array or object');
}
if (isset($options['limit']) && ! is_integer($options['limit'])) {
throw InvalidArgumentException::invalidType('"limit" option', $options['limit'], 'integer');
}
......@@ -249,7 +257,7 @@ class Find implements Executable
}
}
foreach (['allowPartialResults', 'batchSize', 'comment', 'limit', 'maxTimeMS', 'noCursorTimeout', 'oplogReplay', 'projection', 'readConcern', 'skip', 'sort'] as $option) {
foreach (['allowPartialResults', 'batchSize', 'comment', 'hint', 'limit', 'maxTimeMS', 'noCursorTimeout', 'oplogReplay', 'projection', 'readConcern', 'skip', 'sort'] as $option) {
if (isset($this->options[$option])) {
$options[$option] = $this->options[$option];
}
......
......@@ -48,6 +48,10 @@ class FindOne implements Executable
* * comment (string): Attaches a comment to the query. If "$comment" also
* exists in the modifiers document, this option will take precedence.
*
* * hint (string|document): The index to use. Specify either the index
* name as a string or the index key pattern as a document. If specified,
* then the query system will only consider plans using the hinted index.
*
* * maxTimeMS (integer): The maximum amount of time to allow the query to
* run. If "$maxTimeMS" also exists in the modifiers document, this
* option will take precedence.
......
......@@ -3,6 +3,7 @@
namespace MongoDB\Tests\Operation;
use MongoDB\Driver\BulkWrite;
use MongoDB\Operation\CreateIndexes;
use MongoDB\Operation\Find;
use MongoDB\Tests\CommandObserver;
use stdClass;
......@@ -28,6 +29,57 @@ class FindFunctionalTest extends FunctionalTestCase
);
}
public function testHintOption()
{
$bulkWrite = new BulkWrite;
$bulkWrite->insert(['_id' => 1, 'x' => 1]);
$bulkWrite->insert(['_id' => 2, 'x' => 2]);
$bulkWrite->insert(['_id' => 3, 'y' => 3]);
$this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite);
$createIndexes = new CreateIndexes($this->getDatabaseName(), $this->getCollectionName(), [
['key' => ['x' => 1], 'sparse' => true, 'name' => 'sparse_x'],
['key' => ['y' => 1]],
]);
$createIndexes->execute($this->getPrimaryServer());
$hintsUsingSparseIndex = [
['x' => 1],
'sparse_x',
];
foreach ($hintsUsingSparseIndex as $hint) {
$operation = new Find($this->getDatabaseName(), $this->getCollectionName(), [], ['hint' => $hint]);
$cursor = $operation->execute($this->getPrimaryServer());
$expectedDocuments = [
(object) ['_id' => 1, 'x' => 1],
(object) ['_id' => 2, 'x' => 2],
];
$this->assertEquals($expectedDocuments, $cursor->toArray());
}
$hintsNotUsingSparseIndex = [
['_id' => 1],
['y' => 1],
'y_1',
];
foreach ($hintsNotUsingSparseIndex as $hint) {
$operation = new Find($this->getDatabaseName(), $this->getCollectionName(), [], ['hint' => $hint]);
$cursor = $operation->execute($this->getPrimaryServer());
$expectedDocuments = [
(object) ['_id' => 1, 'x' => 1],
(object) ['_id' => 2, 'x' => 2],
(object) ['_id' => 3, 'y' => 3],
];
$this->assertEquals($expectedDocuments, $cursor->toArray());
}
}
/**
* @dataProvider provideTypeMapOptionsAndExpectedDocuments
*/
......
......@@ -48,6 +48,10 @@ class FindTest extends TestCase
$options[][] = ['cursorType' => $value];
}
foreach ($this->getInvalidHintValues() as $value) {
$options[][] = ['hint' => $value];
}
foreach ($this->getInvalidIntegerValues() as $value) {
$options[][] = ['limit' => $value];
}
......@@ -91,6 +95,11 @@ class FindTest extends TestCase
return $options;
}
private function getInvalidHintValues()
{
return [123, 3.14, true];
}
/**
* @expectedException MongoDB\Exception\InvalidArgumentException
* @dataProvider provideInvalidConstructorCursorTypeOptions
......
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