Commit cb5cf0bb authored by Jeremy Mikola's avatar Jeremy Mikola

Merge pull request #68

parents ff9d54c2 fc71a5c4
...@@ -5,6 +5,7 @@ namespace MongoDB; ...@@ -5,6 +5,7 @@ namespace MongoDB;
use MongoDB\Driver\Command; use MongoDB\Driver\Command;
use MongoDB\Driver\Cursor; use MongoDB\Driver\Cursor;
use MongoDB\Driver\Manager; use MongoDB\Driver\Manager;
use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference; use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Server; use MongoDB\Driver\Server;
use MongoDB\Driver\WriteConcern; use MongoDB\Driver\WriteConcern;
...@@ -42,6 +43,7 @@ class Collection ...@@ -42,6 +43,7 @@ class Collection
private $collectionName; private $collectionName;
private $databaseName; private $databaseName;
private $manager; private $manager;
private $readConcern;
private $readPreference; private $readPreference;
private $writeConcern; private $writeConcern;
...@@ -53,6 +55,9 @@ class Collection ...@@ -53,6 +55,9 @@ class Collection
* *
* Supported options: * Supported options:
* *
* * readConcern (MongoDB\Driver\ReadConcern): The default read concern to
* use for collection operations. Defaults to the Manager's read concern.
*
* * readPreference (MongoDB\Driver\ReadPreference): The default read * * readPreference (MongoDB\Driver\ReadPreference): The default read
* preference to use for collection operations. Defaults to the Manager's * preference to use for collection operations. Defaults to the Manager's
* read preference. * read preference.
...@@ -77,6 +82,10 @@ class Collection ...@@ -77,6 +82,10 @@ class Collection
$this->databaseName = $parts[0]; $this->databaseName = $parts[0];
$this->collectionName = $parts[1]; $this->collectionName = $parts[1];
if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) {
throw new InvalidArgumentTypeException('"readConcern" option', $options['readConcern'], 'MongoDB\Driver\ReadConcern');
}
if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) { if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) {
throw new InvalidArgumentTypeException('"readPreference" option', $options['readPreference'], 'MongoDB\Driver\ReadPreference'); throw new InvalidArgumentTypeException('"readPreference" option', $options['readPreference'], 'MongoDB\Driver\ReadPreference');
} }
...@@ -86,6 +95,7 @@ class Collection ...@@ -86,6 +95,7 @@ class Collection
} }
$this->manager = $manager; $this->manager = $manager;
$this->readConcern = isset($options['readConcern']) ? $options['readConcern'] : $this->manager->getReadConcern();
$this->readPreference = isset($options['readPreference']) ? $options['readPreference'] : $this->manager->getReadPreference(); $this->readPreference = isset($options['readPreference']) ? $options['readPreference'] : $this->manager->getReadPreference();
$this->writeConcern = isset($options['writeConcern']) ? $options['writeConcern'] : $this->manager->getWriteConcern(); $this->writeConcern = isset($options['writeConcern']) ? $options['writeConcern'] : $this->manager->getWriteConcern();
} }
...@@ -102,6 +112,7 @@ class Collection ...@@ -102,6 +112,7 @@ class Collection
'collectionName' => $this->collectionName, 'collectionName' => $this->collectionName,
'databaseName' => $this->databaseName, 'databaseName' => $this->databaseName,
'manager' => $this->manager, 'manager' => $this->manager,
'readConcern' => $this->readConcern,
'readPreference' => $this->readPreference, 'readPreference' => $this->readPreference,
'writeConcern' => $this->writeConcern, 'writeConcern' => $this->writeConcern,
]; ];
...@@ -132,11 +143,20 @@ class Collection ...@@ -132,11 +143,20 @@ class Collection
*/ */
public function aggregate(array $pipeline, array $options = []) public function aggregate(array $pipeline, array $options = [])
{ {
$hasOutStage = \MongoDB\is_last_pipeline_operator_out($pipeline);
/* A "majority" read concern is not compatible with the $out stage, so
* avoid providing the Collection's read concern if it would conflict.
*/
if ( ! isset($options['readConcern']) && ! ($hasOutStage && $this->readConcern->getLevel() === ReadConcern::MAJORITY)) {
$options['readConcern'] = $this->readConcern;
}
if ( ! isset($options['readPreference'])) { if ( ! isset($options['readPreference'])) {
$options['readPreference'] = $this->readPreference; $options['readPreference'] = $this->readPreference;
} }
if (\MongoDB\is_last_pipeline_operator_out($pipeline)) { if ($hasOutStage) {
$options['readPreference'] = new ReadPreference(ReadPreference::RP_PRIMARY); $options['readPreference'] = new ReadPreference(ReadPreference::RP_PRIMARY);
} }
...@@ -176,6 +196,10 @@ class Collection ...@@ -176,6 +196,10 @@ class Collection
*/ */
public function count($filter = [], array $options = []) public function count($filter = [], array $options = [])
{ {
if ( ! isset($options['readConcern'])) {
$options['readConcern'] = $this->readConcern;
}
if ( ! isset($options['readPreference'])) { if ( ! isset($options['readPreference'])) {
$options['readPreference'] = $this->readPreference; $options['readPreference'] = $this->readPreference;
} }
...@@ -284,6 +308,10 @@ class Collection ...@@ -284,6 +308,10 @@ class Collection
*/ */
public function distinct($fieldName, $filter = [], array $options = []) public function distinct($fieldName, $filter = [], array $options = [])
{ {
if ( ! isset($options['readConcern'])) {
$options['readConcern'] = $this->readConcern;
}
if ( ! isset($options['readPreference'])) { if ( ! isset($options['readPreference'])) {
$options['readPreference'] = $this->readPreference; $options['readPreference'] = $this->readPreference;
} }
...@@ -352,6 +380,10 @@ class Collection ...@@ -352,6 +380,10 @@ class Collection
*/ */
public function find($filter = [], array $options = []) public function find($filter = [], array $options = [])
{ {
if ( ! isset($options['readConcern'])) {
$options['readConcern'] = $this->readConcern;
}
if ( ! isset($options['readPreference'])) { if ( ! isset($options['readPreference'])) {
$options['readPreference'] = $this->readPreference; $options['readPreference'] = $this->readPreference;
} }
...@@ -373,6 +405,10 @@ class Collection ...@@ -373,6 +405,10 @@ class Collection
*/ */
public function findOne($filter = [], array $options = []) public function findOne($filter = [], array $options = [])
{ {
if ( ! isset($options['readConcern'])) {
$options['readConcern'] = $this->readConcern;
}
if ( ! isset($options['readPreference'])) { if ( ! isset($options['readPreference'])) {
$options['readPreference'] = $this->readPreference; $options['readPreference'] = $this->readPreference;
} }
...@@ -621,6 +657,10 @@ class Collection ...@@ -621,6 +657,10 @@ class Collection
* *
* Supported options: * Supported options:
* *
* * readConcern (MongoDB\Driver\ReadConcern): The default read concern to
* use for collection operations. Defaults to this Collection's read
* concern.
*
* * readPreference (MongoDB\Driver\ReadPreference): The default read * * readPreference (MongoDB\Driver\ReadPreference): The default read
* preference to use for collection operations. Defaults to this * preference to use for collection operations. Defaults to this
* Collection's read preference. * Collection's read preference.
...@@ -634,6 +674,10 @@ class Collection ...@@ -634,6 +674,10 @@ class Collection
*/ */
public function withOptions(array $options = []) public function withOptions(array $options = [])
{ {
if ( ! isset($options['readConcern'])) {
$options['readConcern'] = $this->readConcern;
}
if ( ! isset($options['readPreference'])) { if ( ! isset($options['readPreference'])) {
$options['readPreference'] = $this->readPreference; $options['readPreference'] = $this->readPreference;
} }
......
...@@ -7,6 +7,7 @@ use MongoDB\Driver\Command; ...@@ -7,6 +7,7 @@ use MongoDB\Driver\Command;
use MongoDB\Driver\Cursor; use MongoDB\Driver\Cursor;
use MongoDB\Driver\Manager; use MongoDB\Driver\Manager;
use MongoDB\Driver\Query; use MongoDB\Driver\Query;
use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference; use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Server; use MongoDB\Driver\Server;
use MongoDB\Driver\WriteConcern; use MongoDB\Driver\WriteConcern;
...@@ -22,6 +23,7 @@ class Database ...@@ -22,6 +23,7 @@ class Database
{ {
private $databaseName; private $databaseName;
private $manager; private $manager;
private $readConcern;
private $readPreference; private $readPreference;
private $writeConcern; private $writeConcern;
...@@ -33,6 +35,10 @@ class Database ...@@ -33,6 +35,10 @@ class Database
* *
* Supported options: * Supported options:
* *
* * readConcern (MongoDB\Driver\ReadConcern): The default read concern to
* use for database operations and selected collections. Defaults to the
* Manager's read concern.
*
* * readPreference (MongoDB\Driver\ReadPreference): The default read * * readPreference (MongoDB\Driver\ReadPreference): The default read
* preference to use for database operations and selected collections. * preference to use for database operations and selected collections.
* Defaults to the Manager's read preference. * Defaults to the Manager's read preference.
...@@ -52,6 +58,10 @@ class Database ...@@ -52,6 +58,10 @@ class Database
throw new InvalidArgumentException('$databaseName is invalid: ' . $databaseName); throw new InvalidArgumentException('$databaseName is invalid: ' . $databaseName);
} }
if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) {
throw new InvalidArgumentTypeException('"readConcern" option', $options['readConcern'], 'MongoDB\Driver\ReadConcern');
}
if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) { if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) {
throw new InvalidArgumentTypeException('"readPreference" option', $options['readPreference'], 'MongoDB\Driver\ReadPreference'); throw new InvalidArgumentTypeException('"readPreference" option', $options['readPreference'], 'MongoDB\Driver\ReadPreference');
} }
...@@ -62,6 +72,7 @@ class Database ...@@ -62,6 +72,7 @@ class Database
$this->manager = $manager; $this->manager = $manager;
$this->databaseName = (string) $databaseName; $this->databaseName = (string) $databaseName;
$this->readConcern = isset($options['readConcern']) ? $options['readConcern'] : $this->manager->getReadConcern();
$this->readPreference = isset($options['readPreference']) ? $options['readPreference'] : $this->manager->getReadPreference(); $this->readPreference = isset($options['readPreference']) ? $options['readPreference'] : $this->manager->getReadPreference();
$this->writeConcern = isset($options['writeConcern']) ? $options['writeConcern'] : $this->manager->getWriteConcern(); $this->writeConcern = isset($options['writeConcern']) ? $options['writeConcern'] : $this->manager->getWriteConcern();
} }
...@@ -77,6 +88,7 @@ class Database ...@@ -77,6 +88,7 @@ class Database
return [ return [
'databaseName' => $this->databaseName, 'databaseName' => $this->databaseName,
'manager' => $this->manager, 'manager' => $this->manager,
'readConcern' => $this->readConcern,
'readPreference' => $this->readPreference, 'readPreference' => $this->readPreference,
'writeConcern' => $this->writeConcern, 'writeConcern' => $this->writeConcern,
]; ];
...@@ -191,6 +203,10 @@ class Database ...@@ -191,6 +203,10 @@ class Database
* *
* Supported options: * Supported options:
* *
* * readConcern (MongoDB\Driver\ReadConcern): The default read concern to
* use for collection operations. Defaults to the Database's read
* concern.
*
* * readPreference (MongoDB\Driver\ReadPreference): The default read * * readPreference (MongoDB\Driver\ReadPreference): The default read
* preference to use for collection operations. Defaults to the * preference to use for collection operations. Defaults to the
* Database's read preference. * Database's read preference.
...@@ -205,6 +221,10 @@ class Database ...@@ -205,6 +221,10 @@ class Database
*/ */
public function selectCollection($collectionName, array $options = []) public function selectCollection($collectionName, array $options = [])
{ {
if ( ! isset($options['readConcern'])) {
$options['readConcern'] = $this->readConcern;
}
if ( ! isset($options['readPreference'])) { if ( ! isset($options['readPreference'])) {
$options['readPreference'] = $this->readPreference; $options['readPreference'] = $this->readPreference;
} }
...@@ -221,6 +241,10 @@ class Database ...@@ -221,6 +241,10 @@ class Database
* *
* Supported options: * Supported options:
* *
* * readConcern (MongoDB\Driver\ReadConcern): The default read concern to
* use for database operations and selected collections. Defaults to this
* Database's read concern.
*
* * readPreference (MongoDB\Driver\ReadPreference): The default read * * readPreference (MongoDB\Driver\ReadPreference): The default read
* preference to use for database operations and selected collections. * preference to use for database operations and selected collections.
* Defaults to this Database's read preference. * Defaults to this Database's read preference.
...@@ -234,6 +258,10 @@ class Database ...@@ -234,6 +258,10 @@ class Database
*/ */
public function withOptions(array $options = []) public function withOptions(array $options = [])
{ {
if ( ! isset($options['readConcern'])) {
$options['readConcern'] = $this->readConcern;
}
if ( ! isset($options['readPreference'])) { if ( ! isset($options['readPreference'])) {
$options['readPreference'] = $this->readPreference; $options['readPreference'] = $this->readPreference;
} }
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
namespace MongoDB\Operation; namespace MongoDB\Operation;
use MongoDB\Driver\Command; use MongoDB\Driver\Command;
use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference; use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Server; use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\InvalidArgumentException;
...@@ -23,6 +24,7 @@ class Aggregate implements Executable ...@@ -23,6 +24,7 @@ class Aggregate implements Executable
{ {
private static $wireVersionForCursor = 2; private static $wireVersionForCursor = 2;
private static $wireVersionForDocumentLevelValidation = 4; private static $wireVersionForDocumentLevelValidation = 4;
private static $wireVersionForReadConcern = 4;
private $databaseName; private $databaseName;
private $collectionName; private $collectionName;
...@@ -50,6 +52,12 @@ class Aggregate implements Executable ...@@ -50,6 +52,12 @@ class Aggregate implements Executable
* * maxTimeMS (integer): The maximum amount of time to allow the query to * * maxTimeMS (integer): The maximum amount of time to allow the query to
* run. * run.
* *
* * readConcern (MongoDB\Driver\ReadConcern): Read concern. Note that a
* "majority" read concern is not compatible with the $out stage.
*
* For servers < 3.2, this option is ignored as read concern is not
* available.
*
* * readPreference (MongoDB\Driver\ReadPreference): Read preference. * * readPreference (MongoDB\Driver\ReadPreference): Read preference.
* *
* * useCursor (boolean): Indicates whether the command will request that * * useCursor (boolean): Indicates whether the command will request that
...@@ -108,6 +116,10 @@ class Aggregate implements Executable ...@@ -108,6 +116,10 @@ class Aggregate implements Executable
throw new InvalidArgumentTypeException('"maxTimeMS" option', $options['maxTimeMS'], 'integer'); throw new InvalidArgumentTypeException('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
} }
if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) {
throw new InvalidArgumentTypeException('"readConcern" option', $options['readConcern'], 'MongoDB\Driver\ReadConcern');
}
if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) { if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) {
throw new InvalidArgumentTypeException('"readPreference" option', $options['readPreference'], 'MongoDB\Driver\ReadPreference'); throw new InvalidArgumentTypeException('"readPreference" option', $options['readPreference'], 'MongoDB\Driver\ReadPreference');
} }
...@@ -183,6 +195,10 @@ class Aggregate implements Executable ...@@ -183,6 +195,10 @@ class Aggregate implements Executable
$cmd['maxTimeMS'] = $this->options['maxTimeMS']; $cmd['maxTimeMS'] = $this->options['maxTimeMS'];
} }
if (isset($this->options['readConcern']) && \MongoDB\server_supports_feature($server, self::$wireVersionForReadConcern)) {
$cmd['readConcern'] = \MongoDB\read_concern_as_document($this->options['readConcern']);
}
if ($this->options['useCursor']) { if ($this->options['useCursor']) {
$cmd['cursor'] = isset($this->options["batchSize"]) $cmd['cursor'] = isset($this->options["batchSize"])
? ['batchSize' => $this->options["batchSize"]] ? ['batchSize' => $this->options["batchSize"]]
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
namespace MongoDB\Operation; namespace MongoDB\Operation;
use MongoDB\Driver\Command; use MongoDB\Driver\Command;
use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference; use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Server; use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\InvalidArgumentException;
...@@ -18,6 +19,8 @@ use MongoDB\Exception\UnexpectedValueException; ...@@ -18,6 +19,8 @@ use MongoDB\Exception\UnexpectedValueException;
*/ */
class Count implements Executable class Count implements Executable
{ {
private static $wireVersionForReadConcern = 4;
private $databaseName; private $databaseName;
private $collectionName; private $collectionName;
private $filter; private $filter;
...@@ -36,6 +39,11 @@ class Count implements Executable ...@@ -36,6 +39,11 @@ class Count implements Executable
* * maxTimeMS (integer): The maximum amount of time to allow the query to * * maxTimeMS (integer): The maximum amount of time to allow the query to
* run. * run.
* *
* * readConcern (MongoDB\Driver\ReadConcern): Read concern.
*
* For servers < 3.2, this option is ignored as read concern is not
* available.
*
* * readPreference (MongoDB\Driver\ReadPreference): Read preference. * * readPreference (MongoDB\Driver\ReadPreference): Read preference.
* *
* * skip (integer): The number of documents to skip before returning the * * skip (integer): The number of documents to skip before returning the
...@@ -71,6 +79,10 @@ class Count implements Executable ...@@ -71,6 +79,10 @@ class Count implements Executable
throw new InvalidArgumentTypeException('"maxTimeMS" option', $options['maxTimeMS'], 'integer'); throw new InvalidArgumentTypeException('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
} }
if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) {
throw new InvalidArgumentTypeException('"readConcern" option', $options['readConcern'], 'MongoDB\Driver\ReadConcern');
}
if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) { if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) {
throw new InvalidArgumentTypeException('"readPreference" option', $options['readPreference'], 'MongoDB\Driver\ReadPreference'); throw new InvalidArgumentTypeException('"readPreference" option', $options['readPreference'], 'MongoDB\Driver\ReadPreference');
} }
...@@ -96,7 +108,7 @@ class Count implements Executable ...@@ -96,7 +108,7 @@ class Count implements Executable
{ {
$readPreference = isset($this->options['readPreference']) ? $this->options['readPreference'] : null; $readPreference = isset($this->options['readPreference']) ? $this->options['readPreference'] : null;
$cursor = $server->executeCommand($this->databaseName, $this->createCommand(), $readPreference); $cursor = $server->executeCommand($this->databaseName, $this->createCommand($server), $readPreference);
$result = current($cursor->toArray()); $result = current($cursor->toArray());
// Older server versions may return a float // Older server versions may return a float
...@@ -110,9 +122,10 @@ class Count implements Executable ...@@ -110,9 +122,10 @@ class Count implements Executable
/** /**
* Create the count command. * Create the count command.
* *
* @param Server $server
* @return Command * @return Command
*/ */
private function createCommand() private function createCommand(Server $server)
{ {
$cmd = ['count' => $this->collectionName]; $cmd = ['count' => $this->collectionName];
...@@ -126,6 +139,10 @@ class Count implements Executable ...@@ -126,6 +139,10 @@ class Count implements Executable
} }
} }
if (isset($this->options['readConcern']) && \MongoDB\server_supports_feature($server, self::$wireVersionForReadConcern)) {
$cmd['readConcern'] = \MongoDB\read_concern_as_document($this->options['readConcern']);
}
return new Command($cmd); return new Command($cmd);
} }
} }
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
namespace MongoDB\Operation; namespace MongoDB\Operation;
use MongoDB\Driver\Command; use MongoDB\Driver\Command;
use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference; use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Server; use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\InvalidArgumentException;
...@@ -18,6 +19,8 @@ use MongoDB\Exception\UnexpectedValueException; ...@@ -18,6 +19,8 @@ use MongoDB\Exception\UnexpectedValueException;
*/ */
class Distinct implements Executable class Distinct implements Executable
{ {
private static $wireVersionForReadConcern = 4;
private $databaseName; private $databaseName;
private $collectionName; private $collectionName;
private $fieldName; private $fieldName;
...@@ -32,6 +35,11 @@ class Distinct implements Executable ...@@ -32,6 +35,11 @@ class Distinct implements Executable
* * maxTimeMS (integer): The maximum amount of time to allow the query to * * maxTimeMS (integer): The maximum amount of time to allow the query to
* run. * run.
* *
* * readConcern (MongoDB\Driver\ReadConcern): Read concern.
*
* For servers < 3.2, this option is ignored as read concern is not
* available.
*
* * readPreference (MongoDB\Driver\ReadPreference): Read preference. * * readPreference (MongoDB\Driver\ReadPreference): Read preference.
* *
* @param string $databaseName Database name * @param string $databaseName Database name
...@@ -51,6 +59,10 @@ class Distinct implements Executable ...@@ -51,6 +59,10 @@ class Distinct implements Executable
throw new InvalidArgumentTypeException('"maxTimeMS" option', $options['maxTimeMS'], 'integer'); throw new InvalidArgumentTypeException('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
} }
if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) {
throw new InvalidArgumentTypeException('"readConcern" option', $options['readConcern'], 'MongoDB\Driver\ReadConcern');
}
if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) { if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) {
throw new InvalidArgumentTypeException('"readPreference" option', $options['readPreference'], 'MongoDB\Driver\ReadPreference'); throw new InvalidArgumentTypeException('"readPreference" option', $options['readPreference'], 'MongoDB\Driver\ReadPreference');
} }
...@@ -73,7 +85,7 @@ class Distinct implements Executable ...@@ -73,7 +85,7 @@ class Distinct implements Executable
{ {
$readPreference = isset($this->options['readPreference']) ? $this->options['readPreference'] : null; $readPreference = isset($this->options['readPreference']) ? $this->options['readPreference'] : null;
$cursor = $server->executeCommand($this->databaseName, $this->createCommand(), $readPreference); $cursor = $server->executeCommand($this->databaseName, $this->createCommand($server), $readPreference);
$result = current($cursor->toArray()); $result = current($cursor->toArray());
if ( ! isset($result->values) || ! is_array($result->values)) { if ( ! isset($result->values) || ! is_array($result->values)) {
...@@ -86,9 +98,10 @@ class Distinct implements Executable ...@@ -86,9 +98,10 @@ class Distinct implements Executable
/** /**
* Create the distinct command. * Create the distinct command.
* *
* @param Server $server
* @return Command * @return Command
*/ */
private function createCommand() private function createCommand(Server $server)
{ {
$cmd = [ $cmd = [
'distinct' => $this->collectionName, 'distinct' => $this->collectionName,
...@@ -103,6 +116,10 @@ class Distinct implements Executable ...@@ -103,6 +116,10 @@ class Distinct implements Executable
$cmd['maxTimeMS'] = $this->options['maxTimeMS']; $cmd['maxTimeMS'] = $this->options['maxTimeMS'];
} }
if (isset($this->options['readConcern']) && \MongoDB\server_supports_feature($server, self::$wireVersionForReadConcern)) {
$cmd['readConcern'] = \MongoDB\read_concern_as_document($this->options['readConcern']);
}
return new Command($cmd); return new Command($cmd);
} }
} }
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
namespace MongoDB\Operation; namespace MongoDB\Operation;
use MongoDB\Driver\Query; use MongoDB\Driver\Query;
use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference; use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Server; use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\InvalidArgumentException;
...@@ -65,6 +66,11 @@ class Find implements Executable ...@@ -65,6 +66,11 @@ class Find implements Executable
* * projection (document): Limits the fields to return for the matching * * projection (document): Limits the fields to return for the matching
* document. * document.
* *
* * readConcern (MongoDB\Driver\ReadConcern): Read concern.
*
* For servers < 3.2, this option is ignored as read concern is not
* available.
*
* * readPreference (MongoDB\Driver\ReadPreference): Read preference. * * readPreference (MongoDB\Driver\ReadPreference): Read preference.
* *
* * skip (integer): The number of documents to skip before returning. * * skip (integer): The number of documents to skip before returning.
...@@ -133,6 +139,10 @@ class Find implements Executable ...@@ -133,6 +139,10 @@ class Find implements Executable
throw new InvalidArgumentTypeException('"projection" option', $options['projection'], 'array or object'); throw new InvalidArgumentTypeException('"projection" option', $options['projection'], 'array or object');
} }
if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) {
throw new InvalidArgumentTypeException('"readConcern" option', $options['readConcern'], 'MongoDB\Driver\ReadConcern');
}
if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) { if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) {
throw new InvalidArgumentTypeException('"readPreference" option', $options['readPreference'], 'MongoDB\Driver\ReadPreference'); throw new InvalidArgumentTypeException('"readPreference" option', $options['readPreference'], 'MongoDB\Driver\ReadPreference');
} }
...@@ -188,7 +198,7 @@ class Find implements Executable ...@@ -188,7 +198,7 @@ class Find implements Executable
} }
} }
foreach (['batchSize', 'limit', 'skip', 'sort', 'noCursorTimeout', 'oplogReplay', 'projection'] as $option) { foreach (['batchSize', 'limit', 'skip', 'sort', 'noCursorTimeout', 'oplogReplay', 'projection', 'readConcern'] as $option) {
if (isset($this->options[$option])) { if (isset($this->options[$option])) {
$options[$option] = $this->options[$option]; $options[$option] = $this->options[$option];
} }
......
...@@ -36,6 +36,11 @@ class FindOne implements Executable ...@@ -36,6 +36,11 @@ class FindOne implements Executable
* * projection (document): Limits the fields to return for the matching * * projection (document): Limits the fields to return for the matching
* document. * document.
* *
* * readConcern (MongoDB\Driver\ReadConcern): Read concern.
*
* For servers < 3.2, this option is ignored as read concern is not
* available.
*
* * readPreference (MongoDB\Driver\ReadPreference): Read preference. * * readPreference (MongoDB\Driver\ReadPreference): Read preference.
* *
* * skip (integer): The number of documents to skip before returning. * * skip (integer): The number of documents to skip before returning.
......
...@@ -2,8 +2,10 @@ ...@@ -2,8 +2,10 @@
namespace MongoDB; namespace MongoDB;
use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\Server; use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentTypeException; use MongoDB\Exception\InvalidArgumentTypeException;
use stdClass;
/** /**
* Return whether the first key in the document starts with a "$" character. * Return whether the first key in the document starts with a "$" character.
...@@ -81,6 +83,25 @@ function generate_index_name($document) ...@@ -81,6 +83,25 @@ function generate_index_name($document)
return $name; return $name;
} }
/**
* Converts a ReadConcern instance to a stdClass for use in a BSON document.
*
* @internal
* @see https://jira.mongodb.org/browse/PHPC-498
* @param ReadConcern $readConcern Read concern
* @return stdClass
*/
function read_concern_as_document(ReadConcern $readConcern)
{
$document = [];
if ($readConcern->getLevel() !== null) {
$document['level'] = $readConcern->getLevel();
}
return (object) $document;
}
/** /**
* Return whether the server supports a particular feature. * Return whether the server supports a particular feature.
* *
......
...@@ -4,6 +4,7 @@ namespace MongoDB\Tests\Collection; ...@@ -4,6 +4,7 @@ namespace MongoDB\Tests\Collection;
use MongoDB\Collection; use MongoDB\Collection;
use MongoDB\Driver\BulkWrite; use MongoDB\Driver\BulkWrite;
use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference; use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\WriteConcern; use MongoDB\Driver\WriteConcern;
...@@ -114,6 +115,7 @@ class CollectionFunctionalTest extends FunctionalTestCase ...@@ -114,6 +115,7 @@ class CollectionFunctionalTest extends FunctionalTestCase
public function testWithOptionsInheritsReadPreferenceAndWriteConcern() public function testWithOptionsInheritsReadPreferenceAndWriteConcern()
{ {
$collectionOptions = [ $collectionOptions = [
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED), 'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED),
'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY),
]; ];
...@@ -122,6 +124,8 @@ class CollectionFunctionalTest extends FunctionalTestCase ...@@ -122,6 +124,8 @@ class CollectionFunctionalTest extends FunctionalTestCase
$clone = $collection->withOptions(); $clone = $collection->withOptions();
$debug = $clone->__debugInfo(); $debug = $clone->__debugInfo();
$this->assertInstanceOf('MongoDB\Driver\ReadConcern', $debug['readConcern']);
$this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel());
$this->assertInstanceOf('MongoDB\Driver\ReadPreference', $debug['readPreference']); $this->assertInstanceOf('MongoDB\Driver\ReadPreference', $debug['readPreference']);
$this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode());
$this->assertInstanceOf('MongoDB\Driver\WriteConcern', $debug['writeConcern']); $this->assertInstanceOf('MongoDB\Driver\WriteConcern', $debug['writeConcern']);
...@@ -131,6 +135,7 @@ class CollectionFunctionalTest extends FunctionalTestCase ...@@ -131,6 +135,7 @@ class CollectionFunctionalTest extends FunctionalTestCase
public function testWithOptionsPassesReadPreferenceAndWriteConcern() public function testWithOptionsPassesReadPreferenceAndWriteConcern()
{ {
$collectionOptions = [ $collectionOptions = [
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED), 'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED),
'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY),
]; ];
...@@ -138,6 +143,8 @@ class CollectionFunctionalTest extends FunctionalTestCase ...@@ -138,6 +143,8 @@ class CollectionFunctionalTest extends FunctionalTestCase
$clone = $this->collection->withOptions($collectionOptions); $clone = $this->collection->withOptions($collectionOptions);
$debug = $clone->__debugInfo(); $debug = $clone->__debugInfo();
$this->assertInstanceOf('MongoDB\Driver\ReadConcern', $debug['readConcern']);
$this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel());
$this->assertInstanceOf('MongoDB\Driver\ReadPreference', $debug['readPreference']); $this->assertInstanceOf('MongoDB\Driver\ReadPreference', $debug['readPreference']);
$this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode());
$this->assertInstanceOf('MongoDB\Driver\WriteConcern', $debug['writeConcern']); $this->assertInstanceOf('MongoDB\Driver\WriteConcern', $debug['writeConcern']);
......
...@@ -4,6 +4,7 @@ namespace MongoDB\Tests\Database; ...@@ -4,6 +4,7 @@ namespace MongoDB\Tests\Database;
use MongoDB\Database; use MongoDB\Database;
use MongoDB\Driver\BulkWrite; use MongoDB\Driver\BulkWrite;
use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference; use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\WriteConcern; use MongoDB\Driver\WriteConcern;
...@@ -100,9 +101,10 @@ class DatabaseFunctionalTest extends FunctionalTestCase ...@@ -100,9 +101,10 @@ class DatabaseFunctionalTest extends FunctionalTestCase
$this->assertCollectionCount($this->getNamespace(), 0); $this->assertCollectionCount($this->getNamespace(), 0);
} }
public function testSelectCollectionInheritsReadPreferenceAndWriteConcern() public function testSelectCollectionInheritsOptions()
{ {
$databaseOptions = [ $databaseOptions = [
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED), 'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED),
'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY),
]; ];
...@@ -111,15 +113,18 @@ class DatabaseFunctionalTest extends FunctionalTestCase ...@@ -111,15 +113,18 @@ class DatabaseFunctionalTest extends FunctionalTestCase
$collection = $database->selectCollection($this->getCollectionName()); $collection = $database->selectCollection($this->getCollectionName());
$debug = $collection->__debugInfo(); $debug = $collection->__debugInfo();
$this->assertInstanceOf('MongoDB\Driver\ReadConcern', $debug['readConcern']);
$this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel());
$this->assertInstanceOf('MongoDB\Driver\ReadPreference', $debug['readPreference']); $this->assertInstanceOf('MongoDB\Driver\ReadPreference', $debug['readPreference']);
$this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode());
$this->assertInstanceOf('MongoDB\Driver\WriteConcern', $debug['writeConcern']); $this->assertInstanceOf('MongoDB\Driver\WriteConcern', $debug['writeConcern']);
$this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW()); $this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW());
} }
public function testSelectCollectionPassesReadPreferenceAndWriteConcern() public function testSelectCollectionPassesOptions()
{ {
$collectionOptions = [ $collectionOptions = [
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED), 'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED),
'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY),
]; ];
...@@ -127,15 +132,18 @@ class DatabaseFunctionalTest extends FunctionalTestCase ...@@ -127,15 +132,18 @@ class DatabaseFunctionalTest extends FunctionalTestCase
$collection = $this->database->selectCollection($this->getCollectionName(), $collectionOptions); $collection = $this->database->selectCollection($this->getCollectionName(), $collectionOptions);
$debug = $collection->__debugInfo(); $debug = $collection->__debugInfo();
$this->assertInstanceOf('MongoDB\Driver\ReadConcern', $debug['readConcern']);
$this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel());
$this->assertInstanceOf('MongoDB\Driver\ReadPreference', $debug['readPreference']); $this->assertInstanceOf('MongoDB\Driver\ReadPreference', $debug['readPreference']);
$this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode());
$this->assertInstanceOf('MongoDB\Driver\WriteConcern', $debug['writeConcern']); $this->assertInstanceOf('MongoDB\Driver\WriteConcern', $debug['writeConcern']);
$this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW()); $this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW());
} }
public function testWithOptionsInheritsReadPreferenceAndWriteConcern() public function testWithOptionsInheritsOptions()
{ {
$databaseOptions = [ $databaseOptions = [
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED), 'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED),
'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY),
]; ];
...@@ -144,15 +152,18 @@ class DatabaseFunctionalTest extends FunctionalTestCase ...@@ -144,15 +152,18 @@ class DatabaseFunctionalTest extends FunctionalTestCase
$clone = $database->withOptions(); $clone = $database->withOptions();
$debug = $clone->__debugInfo(); $debug = $clone->__debugInfo();
$this->assertInstanceOf('MongoDB\Driver\ReadConcern', $debug['readConcern']);
$this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel());
$this->assertInstanceOf('MongoDB\Driver\ReadPreference', $debug['readPreference']); $this->assertInstanceOf('MongoDB\Driver\ReadPreference', $debug['readPreference']);
$this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode());
$this->assertInstanceOf('MongoDB\Driver\WriteConcern', $debug['writeConcern']); $this->assertInstanceOf('MongoDB\Driver\WriteConcern', $debug['writeConcern']);
$this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW()); $this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW());
} }
public function testWithOptionsPassesReadPreferenceAndWriteConcern() public function testWithOptionsPassesOptions()
{ {
$databaseOptions = [ $databaseOptions = [
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED), 'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED),
'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY),
]; ];
...@@ -160,6 +171,8 @@ class DatabaseFunctionalTest extends FunctionalTestCase ...@@ -160,6 +171,8 @@ class DatabaseFunctionalTest extends FunctionalTestCase
$clone = $this->database->withOptions($databaseOptions); $clone = $this->database->withOptions($databaseOptions);
$debug = $clone->__debugInfo(); $debug = $clone->__debugInfo();
$this->assertInstanceOf('MongoDB\Driver\ReadConcern', $debug['readConcern']);
$this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel());
$this->assertInstanceOf('MongoDB\Driver\ReadPreference', $debug['readPreference']); $this->assertInstanceOf('MongoDB\Driver\ReadPreference', $debug['readPreference']);
$this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode());
$this->assertInstanceOf('MongoDB\Driver\WriteConcern', $debug['writeConcern']); $this->assertInstanceOf('MongoDB\Driver\WriteConcern', $debug['writeConcern']);
......
<?php
namespace MongoDB\Tests;
use MongoDB\Driver\ReadConcern;
/**
* Unit tests for utility functions.
*/
class FunctionsTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider provideReadConcernsAndDocuments
*/
public function testReadConcernAsDocument(ReadConcern $readConcern, $expectedDocument)
{
$this->assertEquals($expectedDocument, \MongoDB\read_concern_as_document($readConcern));
}
public function provideReadConcernsAndDocuments()
{
return [
[ new ReadConcern, (object) [] ],
[ new ReadConcern(ReadConcern::LOCAL), (object) ['level' => ReadConcern::LOCAL] ],
[ new ReadConcern(ReadConcern::MAJORITY), (object) ['level' => ReadConcern::MAJORITY] ],
];
}
}
...@@ -53,6 +53,10 @@ class AggregateTest extends TestCase ...@@ -53,6 +53,10 @@ class AggregateTest extends TestCase
$options[][] = ['maxTimeMS' => $value]; $options[][] = ['maxTimeMS' => $value];
} }
foreach ($this->getInvalidReadConcernValues() as $value) {
$options[][] = ['readConcern' => $value];
}
foreach ($this->getInvalidReadPreferenceValues() as $value) { foreach ($this->getInvalidReadPreferenceValues() as $value) {
$options[][] = ['readPreference' => $value]; $options[][] = ['readPreference' => $value];
} }
......
...@@ -40,6 +40,10 @@ class CountTest extends TestCase ...@@ -40,6 +40,10 @@ class CountTest extends TestCase
$options[][] = ['maxTimeMS' => $value]; $options[][] = ['maxTimeMS' => $value];
} }
foreach ($this->getInvalidReadConcernValues() as $value) {
$options[][] = ['readConcern' => $value];
}
foreach ($this->getInvalidReadPreferenceValues() as $value) { foreach ($this->getInvalidReadPreferenceValues() as $value) {
$options[][] = ['readPreference' => $value]; $options[][] = ['readPreference' => $value];
} }
......
...@@ -32,6 +32,10 @@ class DistinctTest extends TestCase ...@@ -32,6 +32,10 @@ class DistinctTest extends TestCase
$options[][] = ['maxTimeMS' => $value]; $options[][] = ['maxTimeMS' => $value];
} }
foreach ($this->getInvalidReadConcernValues() as $value) {
$options[][] = ['readConcern' => $value];
}
foreach ($this->getInvalidReadPreferenceValues() as $value) { foreach ($this->getInvalidReadPreferenceValues() as $value) {
$options[][] = ['readPreference' => $value]; $options[][] = ['readPreference' => $value];
} }
......
...@@ -64,6 +64,10 @@ class FindTest extends TestCase ...@@ -64,6 +64,10 @@ class FindTest extends TestCase
$options[][] = ['projection' => $value]; $options[][] = ['projection' => $value];
} }
foreach ($this->getInvalidReadConcernValues() as $value) {
$options[][] = ['readConcern' => $value];
}
foreach ($this->getInvalidReadPreferenceValues() as $value) { foreach ($this->getInvalidReadPreferenceValues() as $value) {
$options[][] = ['readPreference' => $value]; $options[][] = ['readPreference' => $value];
} }
......
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
namespace MongoDB\Tests; namespace MongoDB\Tests;
use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\WriteConcern;
use ReflectionClass; use ReflectionClass;
use stdClass; use stdClass;
...@@ -74,6 +77,16 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase ...@@ -74,6 +77,16 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
return [3.14, 'foo', true, [], new stdClass]; return [3.14, 'foo', true, [], new stdClass];
} }
/**
* Return a list of invalid ReadPreference values.
*
* @return array
*/
protected function getInvalidReadConcernValues()
{
return [123, 3.14, 'foo', true, [], new stdClass, new ReadPreference(ReadPreference::RP_PRIMARY), new WriteConcern(1)];
}
/** /**
* Return a list of invalid ReadPreference values. * Return a list of invalid ReadPreference values.
* *
...@@ -81,7 +94,7 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase ...@@ -81,7 +94,7 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
*/ */
protected function getInvalidReadPreferenceValues() protected function getInvalidReadPreferenceValues()
{ {
return [123, 3.14, 'foo', true, [], new stdClass]; return [123, 3.14, 'foo', true, [], new stdClass, new ReadConcern, new WriteConcern(1)];
} }
/** /**
...@@ -101,7 +114,7 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase ...@@ -101,7 +114,7 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
*/ */
protected function getInvalidWriteConcernValues() protected function getInvalidWriteConcernValues()
{ {
return [123, 3.14, 'foo', true, [], new stdClass]; return [123, 3.14, 'foo', true, [], new stdClass, new ReadConcern, new ReadPreference(ReadPreference::RP_PRIMARY)];
} }
/** /**
......
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