Skip to content

PHPLIB-1582: Support hint option for distinct command #1525

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion src/Operation/Distinct.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
use function is_array;
use function is_integer;
use function is_object;
use function is_string;
use function MongoDB\create_field_path_type_map;
use function MongoDB\is_document;

Expand All @@ -53,7 +54,13 @@ class Distinct implements Executable, Explainable
*
* * comment (mixed): BSON value to attach as a comment to this command.
*
* This is not supported for servers versions < 4.4.
* This is not supported for server versions < 4.4.
*
* * 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.
*
* This is not supported for server versions < 7.1.
*
* * maxTimeMS (integer): The maximum amount of time to allow the query to
* run.
Expand Down Expand Up @@ -83,6 +90,10 @@ public function __construct(private string $databaseName, private string $collec
throw InvalidArgumentException::expectedDocumentType('"collation" option', $this->options['collation']);
}

if (isset($this->options['hint']) && ! is_string($this->options['hint']) && ! is_document($this->options['hint'])) {
throw InvalidArgumentException::invalidType('"hint" option', $this->options['hint'], 'string or array or object');
}

if (isset($this->options['maxTimeMS']) && ! is_integer($this->options['maxTimeMS'])) {
throw InvalidArgumentException::invalidType('"maxTimeMS" option', $this->options['maxTimeMS'], 'integer');
}
Expand Down Expand Up @@ -175,6 +186,11 @@ private function createCommandDocument(): array
$cmd['collation'] = (object) $this->options['collation'];
}

if (isset($this->options['hint'])) {
/** @psalm-var string|object */
$cmd['hint'] = is_array($this->options['hint']) ? (object) $this->options['hint'] : $this->options['hint'];
}

foreach (['comment', 'maxTimeMS'] as $option) {
if (isset($this->options[$option])) {
$cmd[$option] = $this->options[$option];
Expand Down
44 changes: 44 additions & 0 deletions tests/Operation/DistinctFunctionalTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
namespace MongoDB\Tests\Operation;

use MongoDB\Driver\BulkWrite;
use MongoDB\Operation\CreateIndexes;
use MongoDB\Operation\Distinct;
use MongoDB\Operation\InsertMany;
use MongoDB\Tests\CommandObserver;
use PHPUnit\Framework\Attributes\DataProvider;
use stdClass;

use function is_scalar;
use function json_encode;
use function sort;
use function usort;

use const JSON_THROW_ON_ERROR;
Expand Down Expand Up @@ -56,6 +59,47 @@ function (array $event): void {
);
}

public function testHintOption(): void
{
$this->skipIfServerVersion('<', '7.1.0', 'hint is not supported');

$insertMany = new InsertMany($this->getDatabaseName(), $this->getCollectionName(), [
['x' => 1],
['x' => 2, 'y' => 2],
['y' => 3],
]);
$insertMany->execute($this->getPrimaryServer());

$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 Distinct($this->getDatabaseName(), $this->getCollectionName(), 'y', [], ['hint' => $hint]);
$this->assertSame([2], $operation->execute($this->getPrimaryServer()));
}

$hintsNotUsingSparseIndex = [
['_id' => 1],
['y' => 1],
'y_1',
];

foreach ($hintsNotUsingSparseIndex as $hint) {
$operation = new Distinct($this->getDatabaseName(), $this->getCollectionName(), 'x', [], ['hint' => $hint]);
$values = $operation->execute($this->getPrimaryServer());
sort($values);
$this->assertSame([1, 2], $values);
}
}

public function testSessionOption(): void
{
(new CommandObserver())->observe(
Expand Down
3 changes: 3 additions & 0 deletions tests/Operation/DistinctTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public static function provideInvalidConstructorOptions()
{
return self::createOptionDataProvider([
'collation' => self::getInvalidDocumentValues(),
'hint' => self::getInvalidHintValues(),
'maxTimeMS' => self::getInvalidIntegerValues(),
'readConcern' => self::getInvalidReadConcernValues(),
'readPreference' => self::getInvalidReadPreferenceValues(),
Expand All @@ -42,6 +43,7 @@ public function testExplainableCommandDocument(): void
{
$options = [
'collation' => ['locale' => 'fr'],
'hint' => '_id_',
'maxTimeMS' => 100,
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
'comment' => 'explain me',
Expand All @@ -56,6 +58,7 @@ public function testExplainableCommandDocument(): void
'key' => 'f',
'query' => (object) ['x' => 1],
'collation' => (object) ['locale' => 'fr'],
'hint' => '_id_',
'comment' => 'explain me',
'maxTimeMS' => 100,
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
Expand Down