Skip to content

PHPORM-209 Add query builder helper to set read preference #3244

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 2 commits into from
Jan 14, 2025
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
31 changes: 28 additions & 3 deletions src/Query/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
use MongoDB\Builder\Type\QueryInterface;
use MongoDB\Builder\Type\SearchOperatorInterface;
use MongoDB\Driver\Cursor;
use MongoDB\Driver\ReadPreference;
use Override;
use RuntimeException;
use stdClass;
Expand Down Expand Up @@ -102,7 +103,7 @@ class Builder extends BaseBuilder
/**
* The maximum amount of seconds to allow the query to run.
*
* @var int
* @var int|float
*/
public $timeout;

Expand All @@ -113,6 +114,8 @@ class Builder extends BaseBuilder
*/
public $hint;

private ReadPreference $readPreference;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get why other properties are public. Using private by default to not add the new property to the API for no reason.


/**
* Custom options to add to the query.
*
Expand Down Expand Up @@ -211,7 +214,7 @@ public function project($columns)
/**
* The maximum amount of seconds to allow the query to run.
*
* @param int $seconds
* @param int|float $seconds
*
* @return $this
*/
Expand Down Expand Up @@ -454,7 +457,7 @@ public function toMql(): array

// Apply order, offset, limit and projection
if ($this->timeout) {
$options['maxTimeMS'] = $this->timeout * 1000;
$options['maxTimeMS'] = (int) ($this->timeout * 1000);
}

if ($this->orders) {
Expand Down Expand Up @@ -1534,6 +1537,24 @@ public function options(array $options)
return $this;
}

/**
* Set the read preference for the query
*
* @see https://www.php.net/manual/en/class.mongodb-driver-readpreference.php
*
* @param string $mode
* @param array $tagSets
* @param array $options
*
* @return $this
*/
public function readPreference(string $mode, ?array $tagSets = null, ?array $options = null): static
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just want to confirm that you don't need the ability to clear a ReadPreference previously assigned to the builder.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the operations using the query builder cannot be unset.
For this config, it's still possible to overwrite with an other read preference.

{
$this->readPreference = new ReadPreference($mode, $tagSets, $options);

return $this;
}

/**
* Performs a full-text search of the field or fields in an Atlas collection.
* NOTE: $search is only available for MongoDB Atlas clusters, and is not available for self-managed deployments.
Expand Down Expand Up @@ -1642,6 +1663,10 @@ private function inheritConnectionOptions(array $options = []): array
}
}

if (! isset($options['readPreference']) && isset($this->readPreference)) {
$options['readPreference'] = $this->readPreference;
}

return $options;
}

Expand Down
26 changes: 26 additions & 0 deletions tests/Query/BuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Mockery as m;
use MongoDB\BSON\Regex;
use MongoDB\BSON\UTCDateTime;
use MongoDB\Driver\ReadPreference;
use MongoDB\Laravel\Connection;
use MongoDB\Laravel\Query\Builder;
use MongoDB\Laravel\Query\Grammar;
Expand Down Expand Up @@ -1416,6 +1417,31 @@ function (Builder $elemMatchQuery): void {
['find' => [['embedded._id' => 1], []]],
fn (Builder $builder) => $builder->where('embedded->id', 1),
];

yield 'options' => [
['find' => [[], ['comment' => 'hello']]],
fn (Builder $builder) => $builder->options(['comment' => 'hello']),
];

yield 'readPreference' => [
['find' => [[], ['readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED)]]],
fn (Builder $builder) => $builder->readPreference(ReadPreference::SECONDARY_PREFERRED),
];

yield 'readPreference advanced' => [
['find' => [[], ['readPreference' => new ReadPreference(ReadPreference::NEAREST, [['dc' => 'ny']], ['maxStalenessSeconds' => 120])]]],
fn (Builder $builder) => $builder->readPreference(ReadPreference::NEAREST, [['dc' => 'ny']], ['maxStalenessSeconds' => 120]),
];

yield 'hint' => [
['find' => [[], ['hint' => ['foo' => 1]]]],
fn (Builder $builder) => $builder->hint(['foo' => 1]),
];

yield 'timeout' => [
['find' => [[], ['maxTimeMS' => 2345]]],
fn (Builder $builder) => $builder->timeout(2.3456),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted that this tests integer truncation.

];
}

#[DataProvider('provideExceptions')]
Expand Down
Loading