Skip to content

Commit be6ba1e

Browse files
committed
PHPLIB-285: Do not convert count's hint document to a string
1 parent 780fb6c commit be6ba1e

File tree

3 files changed

+64
-13
lines changed

3 files changed

+64
-13
lines changed

docs/includes/apiargs-MongoDBCollection-method-count-option.yaml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@ arg_name: option
66
name: hint
77
type: string|array|object
88
description: |
9-
The index to use. If you specify a document, it is interpreted as an index
10-
specification from which a name will be derived.
9+
The index to use. Specify either the index name as a string or the index key
10+
pattern as a document. If specified, then the query system will only consider
11+
plans using the hinted index.
12+
13+
.. versionchanged:: 1.2
14+
If a document is provided, it is passed to the command as-is. Previously,
15+
the library would convert the key pattern to an index name.
1116
interface: phpmethod
1217
operation: ~
1318
optional: true

src/Operation/Count.php

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ class Count implements Executable
5353
* This is not supported for server versions < 3.4 and will result in an
5454
* exception at execution time if used.
5555
*
56-
* * hint (string|document): The index to use. If a document, it will be
57-
* interpretted as an index specification and a name will be generated.
56+
* * hint (string|document): The index to use. Specify either the index
57+
* name as a string or the index key pattern as a document. If specified,
58+
* then the query system will only consider plans using the hinted index.
5859
*
5960
* * limit (integer): The maximum number of documents to count.
6061
*
@@ -87,14 +88,8 @@ public function __construct($databaseName, $collectionName, $filter = [], array
8788
throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object');
8889
}
8990

90-
if (isset($options['hint'])) {
91-
if (is_array($options['hint']) || is_object($options['hint'])) {
92-
$options['hint'] = \MongoDB\generate_index_name($options['hint']);
93-
}
94-
95-
if ( ! is_string($options['hint'])) {
96-
throw InvalidArgumentException::invalidType('"hint" option', $options['hint'], 'string or array or object');
97-
}
91+
if (isset($options['hint']) && ! is_string($options['hint']) && ! is_array($options['hint']) && ! is_object($options['hint'])) {
92+
throw InvalidArgumentException::invalidType('"hint" option', $options['hint'], 'string or array or object');
9893
}
9994

10095
if (isset($options['limit']) && ! is_integer($options['limit'])) {
@@ -177,7 +172,11 @@ private function createCommand()
177172
$cmd['collation'] = (object) $this->options['collation'];
178173
}
179174

180-
foreach (['hint', 'limit', 'maxTimeMS', 'skip'] as $option) {
175+
if (isset($this->options['hint'])) {
176+
$cmd['hint'] = is_array($this->options['hint']) ? (object) $this->options['hint'] : $this->options['hint'];
177+
}
178+
179+
foreach (['limit', 'maxTimeMS', 'skip'] as $option) {
181180
if (isset($this->options[$option])) {
182181
$cmd[$option] = $this->options[$option];
183182
}

tests/Operation/CountFunctionalTest.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
namespace MongoDB\Tests\Operation;
44

55
use MongoDB\Operation\Count;
6+
use MongoDB\Operation\CreateIndexes;
7+
use MongoDB\Operation\InsertMany;
68
use MongoDB\Tests\CommandObserver;
79
use stdClass;
810

@@ -26,4 +28,49 @@ function(stdClass $command) {
2628
}
2729
);
2830
}
31+
32+
public function testHintOption()
33+
{
34+
if (version_compare($this->getServerVersion(), '2.6.0', '<')) {
35+
$this->markTestSkipped('count command does not support "hint" option');
36+
}
37+
38+
$insertMany = new InsertMany($this->getDatabaseName(), $this->getCollectionName(), [
39+
['x' => 1],
40+
['x' => 2],
41+
['y' => 3],
42+
]);
43+
$insertMany->execute($this->getPrimaryServer());
44+
45+
$createIndexes = new CreateIndexes($this->getDatabaseName(), $this->getCollectionName(), [
46+
['key' => ['x' => 1], 'sparse' => true, 'name' => 'sparse_x'],
47+
['key' => ['y' => 1]],
48+
]);
49+
$createIndexes->execute($this->getPrimaryServer());
50+
51+
$hintsUsingSparseIndex = [
52+
['x' => 1],
53+
'sparse_x',
54+
];
55+
56+
/* Per SERVER-22041, the count command in server versions before 3.3.2
57+
* may ignore the hint option if its query predicate is empty. */
58+
$filter = ['_id' => ['$exists' => true]];
59+
60+
foreach ($hintsUsingSparseIndex as $hint) {
61+
$operation = new Count($this->getDatabaseName(), $this->getCollectionName(), $filter, ['hint' => $hint]);
62+
$this->assertEquals(2, $operation->execute($this->getPrimaryServer()));
63+
}
64+
65+
$hintsNotUsingSparseIndex = [
66+
['_id' => 1],
67+
['y' => 1],
68+
'y_1',
69+
];
70+
71+
foreach ($hintsNotUsingSparseIndex as $hint) {
72+
$operation = new Count($this->getDatabaseName(), $this->getCollectionName(), $filter, ['hint' => $hint]);
73+
$this->assertEquals(3, $operation->execute($this->getPrimaryServer()));
74+
}
75+
}
2976
}

0 commit comments

Comments
 (0)