Commit 28e0574f authored by Jeremy Mikola's avatar Jeremy Mikola

Merge pull request #73

parents 2c6a7768 2f3a455e
......@@ -15,6 +15,7 @@ class Client
{
private $manager;
private $uri;
private $typeMap;
/**
* Constructs a new Client instance.
......@@ -23,15 +24,29 @@ class Client
* cluster of servers. It serves as a gateway for accessing individual
* databases and collections.
*
* Supported driver-specific options:
*
* * typeMap (array): Default type map for cursors and BSON documents.
*
* Other options are documented in MongoDB\Driver\Manager::__construct().
*
* @see http://docs.mongodb.org/manual/reference/connection-string/
* @see http://php.net/manual/en/mongodb-driver-manager.construct.php
* @see http://php.net/manual/en/mongodb.persistence.php#mongodb.persistence.typemaps
* @param string $uri MongoDB connection string
* @param array $options Additional connection string options
* @param array $uriOptions Additional connection string options
* @param array $driverOptions Driver-specific options
* @throws InvalidArgumentException
*/
public function __construct($uri = 'mongodb://localhost:27017', array $options = [], array $driverOptions = [])
public function __construct($uri = 'mongodb://localhost:27017', array $uriOptions = [], array $driverOptions = [])
{
$this->manager = new Manager($uri, $options, $driverOptions);
if (isset($driverOptions['typeMap']) && ! is_array($driverOptions['typeMap'])) {
throw new InvalidArgumentTypeException('"typeMap" driver option', $driverOptions['typeMap'], 'array');
}
$this->manager = new Manager($uri, $uriOptions, $driverOptions);
$this->uri = (string) $uri;
$this->typeMap = isset($driverOptions['typeMap']) ? $driverOptions['typeMap'] : null;
}
/**
......@@ -45,6 +60,7 @@ class Client
return [
'manager' => $this->manager,
'uri' => $this->uri,
'typeMap' => $this->typeMap,
];
}
......@@ -89,16 +105,7 @@ class Client
/**
* Select a collection.
*
* Supported options:
*
* * readPreference (MongoDB\Driver\ReadPreference): The default read
* preference to use for collection operations. Defaults to the Client's
* read preference.
*
* * writeConcern (MongoDB\Driver\WriteConcern): The default write concern
* to use for collection operations. Defaults to the Client's write
* concern.
*
* @see Collection::__construct() for supported options
* @param string $databaseName Name of the database containing the collection
* @param string $collectionName Name of the collection to select
* @param array $options Collection constructor options
......@@ -106,28 +113,23 @@ class Client
*/
public function selectCollection($databaseName, $collectionName, array $options = [])
{
$options += ['typeMap' => $this->typeMap];
return new Collection($this->manager, $databaseName . '.' . $collectionName, $options);
}
/**
* Select a database.
*
* Supported options:
*
* * readPreference (MongoDB\Driver\ReadPreference): The default read
* preference to use for database operations and selected collections.
* Defaults to the Client's read preference.
*
* * writeConcern (MongoDB\Driver\WriteConcern): The default write concern
* to use for database operations and selected collections. Defaults to
* the Client's write concern.
*
* @see Database::__construct() for supported options
* @param string $databaseName Name of the database to select
* @param array $options Database constructor options
* @return Database
*/
public function selectDatabase($databaseName, array $options = [])
{
$options += ['typeMap' => $this->typeMap];
return new Database($this->manager, $databaseName, $options);
}
}
......@@ -45,6 +45,7 @@ class Collection
private $manager;
private $readConcern;
private $readPreference;
private $typeMap;
private $writeConcern;
/**
......@@ -62,6 +63,8 @@ class Collection
* preference to use for collection operations. Defaults to the Manager's
* read preference.
*
* * typeMap (array): Default type map for cursors and BSON documents.
*
* * writeConcern (MongoDB\Driver\WriteConcern): The default write concern
* to use for collection operations. Defaults to the Manager's write
* concern.
......@@ -90,6 +93,10 @@ class Collection
throw new InvalidArgumentTypeException('"readPreference" option', $options['readPreference'], 'MongoDB\Driver\ReadPreference');
}
if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
throw new InvalidArgumentTypeException('"typeMap" option', $options['typeMap'], 'array');
}
if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
throw new InvalidArgumentTypeException('"writeConcern" option', $options['writeConcern'], 'MongoDB\Driver\WriteConcern');
}
......@@ -97,6 +104,7 @@ class Collection
$this->manager = $manager;
$this->readConcern = isset($options['readConcern']) ? $options['readConcern'] : $this->manager->getReadConcern();
$this->readPreference = isset($options['readPreference']) ? $options['readPreference'] : $this->manager->getReadPreference();
$this->typeMap = isset($options['typeMap']) ? $options['typeMap'] : null;
$this->writeConcern = isset($options['writeConcern']) ? $options['writeConcern'] : $this->manager->getWriteConcern();
}
......@@ -114,6 +122,7 @@ class Collection
'manager' => $this->manager,
'readConcern' => $this->readConcern,
'readPreference' => $this->readPreference,
'typeMap' => $this->typeMap,
'writeConcern' => $this->writeConcern,
];
}
......@@ -136,6 +145,10 @@ class Collection
* returned; otherwise, an ArrayIterator is returned, which wraps the
* "result" array from the command response document.
*
* Note: BSON deserialization of inline aggregation results (i.e. not using
* a command cursor) does not yet support a custom type map
* (depends on: https://jira.mongodb.org/browse/PHPC-314).
*
* @see Aggregate::__construct() for supported options
* @param array $pipeline List of pipeline operations
* @param array $options Command options
......@@ -160,6 +173,10 @@ class Collection
$options['readPreference'] = new ReadPreference(ReadPreference::RP_PRIMARY);
}
if ( ! isset($options['typeMap'])) {
$options['typeMap'] = $this->typeMap;
}
$operation = new Aggregate($this->databaseName, $this->collectionName, $pipeline, $options);
$server = $this->manager->selectServer($options['readPreference']);
......@@ -388,6 +405,10 @@ class Collection
$options['readPreference'] = $this->readPreference;
}
if ( ! isset($options['typeMap'])) {
$options['typeMap'] = $this->typeMap;
}
$operation = new Find($this->databaseName, $this->collectionName, $filter, $options);
$server = $this->manager->selectServer($options['readPreference']);
......@@ -413,6 +434,10 @@ class Collection
$options['readPreference'] = $this->readPreference;
}
if ( ! isset($options['typeMap'])) {
$options['typeMap'] = $this->typeMap;
}
$operation = new FindOne($this->databaseName, $this->collectionName, $filter, $options);
$server = $this->manager->selectServer($options['readPreference']);
......@@ -424,6 +449,9 @@ class Collection
*
* The document to return may be null.
*
* Note: BSON deserialization of the returned document does not yet support
* a custom type map (depends on: https://jira.mongodb.org/browse/PHPC-314).
*
* @see FindOneAndDelete::__construct() for supported options
* @see http://docs.mongodb.org/manual/reference/command/findAndModify/
* @param array|object $filter Query by which to filter documents
......@@ -451,6 +479,9 @@ class Collection
* returned. Specify FindOneAndReplace::RETURN_DOCUMENT_AFTER for the
* "returnDocument" option to return the updated document.
*
* Note: BSON deserialization of the returned document does not yet support
* a custom type map (depends on: https://jira.mongodb.org/browse/PHPC-314).
*
* @see FindOneAndReplace::__construct() for supported options
* @see http://docs.mongodb.org/manual/reference/command/findAndModify/
* @param array|object $filter Query by which to filter documents
......@@ -479,6 +510,9 @@ class Collection
* returned. Specify FindOneAndUpdate::RETURN_DOCUMENT_AFTER for the
* "returnDocument" option to return the updated document.
*
* Note: BSON deserialization of the returned document does not yet support
* a custom type map (depends on: https://jira.mongodb.org/browse/PHPC-314).
*
* @see FindOneAndReplace::__construct() for supported options
* @see http://docs.mongodb.org/manual/reference/command/findAndModify/
* @param array|object $filter Query by which to filter documents
......@@ -614,7 +648,7 @@ class Collection
* @see UpdateMany::__construct() for supported options
* @see http://docs.mongodb.org/manual/reference/command/update/
* @param array|object $filter Query by which to filter documents
* @param array|object $replacement Update to apply to the matched documents
* @param array|object $update Update to apply to the matched documents
* @param array $options Command options
* @return UpdateResult
*/
......@@ -636,7 +670,7 @@ class Collection
* @see ReplaceOne::__construct() for supported options
* @see http://docs.mongodb.org/manual/reference/command/update/
* @param array|object $filter Query by which to filter documents
* @param array|object $replacement Update to apply to the matched document
* @param array|object $update Update to apply to the matched document
* @param array $options Command options
* @return UpdateResult
*/
......@@ -655,36 +689,18 @@ class Collection
/**
* Get a clone of this collection with different 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
* preference to use for collection operations. Defaults to this
* Collection's read preference.
*
* * writeConcern (MongoDB\Driver\WriteConcern): The default write concern
* to use for collection operations. Defaults to this Collection's write
* concern.
*
* @see Collection::__construct() for supported options
* @param array $options Collection constructor options
* @return Collection
*/
public function withOptions(array $options = [])
{
if ( ! isset($options['readConcern'])) {
$options['readConcern'] = $this->readConcern;
}
if ( ! isset($options['readPreference'])) {
$options['readPreference'] = $this->readPreference;
}
if ( ! isset($options['writeConcern'])) {
$options['writeConcern'] = $this->writeConcern;
}
$options += [
'readConcern' => $this->readConcern,
'readPreference' => $this->readPreference,
'typeMap' => $this->typeMap,
'writeConcern' => $this->writeConcern,
];
return new Collection($this->manager, $this->databaseName . '.' . $this->collectionName, $options);
}
......
......@@ -3,7 +3,6 @@
namespace MongoDB;
use MongoDB\Collection;
use MongoDB\Driver\Command;
use MongoDB\Driver\Cursor;
use MongoDB\Driver\Manager;
use MongoDB\Driver\Query;
......@@ -15,6 +14,7 @@ use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\InvalidArgumentTypeException;
use MongoDB\Model\CollectionInfoIterator;
use MongoDB\Operation\CreateCollection;
use MongoDB\Operation\DatabaseCommand;
use MongoDB\Operation\DropCollection;
use MongoDB\Operation\DropDatabase;
use MongoDB\Operation\ListCollections;
......@@ -25,6 +25,7 @@ class Database
private $manager;
private $readConcern;
private $readPreference;
private $typeMap;
private $writeConcern;
/**
......@@ -43,6 +44,8 @@ class Database
* preference to use for database operations and selected collections.
* Defaults to the Manager's read preference.
*
* * typeMap (array): Default type map for cursors and BSON documents.
*
* * writeConcern (MongoDB\Driver\WriteConcern): The default write concern
* to use for database operations and selected collections. Defaults to
* the Manager's write concern.
......@@ -66,6 +69,10 @@ class Database
throw new InvalidArgumentTypeException('"readPreference" option', $options['readPreference'], 'MongoDB\Driver\ReadPreference');
}
if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
throw new InvalidArgumentTypeException('"typeMap" option', $options['typeMap'], 'array');
}
if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
throw new InvalidArgumentTypeException('"writeConcern" option', $options['writeConcern'], 'MongoDB\Driver\WriteConcern');
}
......@@ -74,6 +81,7 @@ class Database
$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->typeMap = isset($options['typeMap']) ? $options['typeMap'] : null;
$this->writeConcern = isset($options['writeConcern']) ? $options['writeConcern'] : $this->manager->getWriteConcern();
}
......@@ -90,6 +98,7 @@ class Database
'manager' => $this->manager,
'readConcern' => $this->readConcern,
'readPreference' => $this->readPreference,
'typeMap' => $this->typeMap,
'writeConcern' => $this->writeConcern,
];
}
......@@ -107,27 +116,22 @@ class Database
/**
* Execute a command on this database.
*
* @see DatabaseCommand::__construct() for supported options
* @param array|object $command Command document
* @param ReadPreference|null $readPreference Read preference
* @param array $options Options for command execution
* @return Cursor
* @throws InvalidArgumentException
*/
public function command($command, ReadPreference $readPreference = null)
public function command($command, array $options = [])
{
if ( ! is_array($command) && ! is_object($command)) {
throw new InvalidArgumentTypeException('$command', $command, 'array or object');
}
if ( ! $command instanceof Command) {
$command = new Command($command);
}
if ( ! isset($readPreference)) {
$readPreference = $this->readPreference;
if ( ! isset($options['readPreference'])) {
$options['readPreference'] = $this->readPreference;
}
$server = $this->manager->selectServer($readPreference);
$operation = new DatabaseCommand($this->databaseName, $command, $options);
$server = $this->manager->selectServer($options['readPreference']);
return $server->executeCommand($this->databaseName, $command);
return $operation->execute($server);
}
/**
......@@ -201,37 +205,19 @@ class Database
/**
* Select a collection within this database.
*
* 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
* preference to use for collection operations. Defaults to the
* Database's read preference.
*
* * writeConcern (MongoDB\Driver\WriteConcern): The default write concern
* to use for collection operations. Defaults to the Database's write
* concern.
*
* @see Collection::__construct() for supported options
* @param string $collectionName Name of the collection to select
* @param array $options Collection constructor options
* @return Collection
*/
public function selectCollection($collectionName, array $options = [])
{
if ( ! isset($options['readConcern'])) {
$options['readConcern'] = $this->readConcern;
}
if ( ! isset($options['readPreference'])) {
$options['readPreference'] = $this->readPreference;
}
if ( ! isset($options['writeConcern'])) {
$options['writeConcern'] = $this->writeConcern;
}
$options += [
'readConcern' => $this->readConcern,
'readPreference' => $this->readPreference,
'typeMap' => $this->typeMap,
'writeConcern' => $this->writeConcern,
];
return new Collection($this->manager, $this->databaseName . '.' . $collectionName, $options);
}
......@@ -239,36 +225,18 @@ class Database
/**
* Get a clone of this database with different 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
* preference to use for database operations and selected collections.
* Defaults to this Database's read preference.
*
* * writeConcern (MongoDB\Driver\WriteConcern): The default write concern
* to use for database operations and selected collections. Defaults to
* this Database's write concern.
*
* @see Database::__construct() for supported options
* @param array $options Database constructor options
* @return Database
*/
public function withOptions(array $options = [])
{
if ( ! isset($options['readConcern'])) {
$options['readConcern'] = $this->readConcern;
}
if ( ! isset($options['readPreference'])) {
$options['readPreference'] = $this->readPreference;
}
if ( ! isset($options['writeConcern'])) {
$options['writeConcern'] = $this->writeConcern;
}
$options += [
'readConcern' => $this->readConcern,
'readPreference' => $this->readPreference,
'typeMap' => $this->typeMap,
'writeConcern' => $this->writeConcern,
];
return new Database($this->manager, $this->databaseName, $options);
}
......
......@@ -60,6 +60,12 @@ class Aggregate implements Executable
*
* * readPreference (MongoDB\Driver\ReadPreference): Read preference.
*
* * typeMap (array): Type map for BSON deserialization. This will be
* applied to the returned Cursor (it is not sent to the server).
*
* This is not supported for inline aggregation results (i.e. useCursor
* option is false or the server versions < 2.6).
*
* * useCursor (boolean): Indicates whether the command will request that
* the server provide results using a cursor. The default is true.
*
......@@ -124,6 +130,10 @@ class Aggregate implements Executable
throw new InvalidArgumentTypeException('"readPreference" option', $options['readPreference'], 'MongoDB\Driver\ReadPreference');
}
if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
throw new InvalidArgumentTypeException('"typeMap" option', $options['typeMap'], 'array');
}
if ( ! is_bool($options['useCursor'])) {
throw new InvalidArgumentTypeException('"useCursor" option', $options['useCursor'], 'boolean');
}
......@@ -132,6 +142,10 @@ class Aggregate implements Executable
throw new InvalidArgumentException('"batchSize" option should not be used if "useCursor" is false');
}
if (isset($options['typeMap']) && ! $options['useCursor']) {
throw new InvalidArgumentException('"typeMap" option should not be used if "useCursor" is false');
}
$this->databaseName = (string) $databaseName;
$this->collectionName = (string) $collectionName;
$this->pipeline = $pipeline;
......@@ -154,6 +168,13 @@ class Aggregate implements Executable
$cursor = $server->executeCommand($this->databaseName, $command, $readPreference);
if ($isCursorSupported && $this->options['useCursor']) {
/* The type map can only be applied to command cursors until
* https://jira.mongodb.org/browse/PHPC-314 is implemented.
*/
if (isset($this->options['typeMap'])) {
$cursor->setTypeMap($this->options['typeMap']);
}
return $cursor;
}
......
......@@ -108,7 +108,6 @@ class CreateIndexes implements Executable
* "system.indexes" collection (MongoDB <2.6).
*
* @param Server $server
* @param IndexInput[] $indexes
*/
private function executeLegacy(Server $server)
{
......
<?php
namespace MongoDB\Operation;
use MongoDB\Driver\Command;
use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\InvalidArgumentTypeException;
/**
* Operation for executing a database command.
*
* @api
* @see MongoDB\Database::command()
*/
class DatabaseCommand implements Executable
{
private $databaseName;
private $command;
private $options;
/**
* Constructs a command.
*
* Supported options:
*
* * readPreference (MongoDB\Driver\ReadPreference): The read preference to
* use when executing the command. This may be used when issuing the
* command to a replica set or mongos node to ensure that the driver sets
* the wire protocol accordingly or adds the read preference to the
* command document, respectively.
*
* * typeMap (array): Type map for BSON deserialization. This will be
* applied to the returned Cursor (it is not sent to the server).
*
* @param string $databaseName Database name
* @param array|object $command Command document
* @param array $options Options for command execution
* @throws InvalidArgumentException
*/
public function __construct($databaseName, $command, array $options = [])
{
if ( ! is_array($command) && ! is_object($command)) {
throw new InvalidArgumentTypeException('$command', $command, 'array or object');
}
if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) {
throw new InvalidArgumentTypeException('"readPreference" option', $options['readPreference'], 'MongoDB\Driver\ReadPreference');
}
if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
throw new InvalidArgumentTypeException('"typeMap" option', $options['typeMap'], 'array');
}
$this->databaseName = (string) $databaseName;
$this->command = ($command instanceof Command) ? $command : new Command($command);
$this->options = $options;
}
/**
* Execute the operation.
*
* @see Executable::execute()
* @param Server $server
* @return integer
*/
public function execute(Server $server)
{
$readPreference = isset($this->options['readPreference']) ? $this->options['readPreference'] : null;
$cursor = $server->executeCommand($this->databaseName, $this->command, $readPreference);
if (isset($this->options['typeMap'])) {
$cursor->setTypeMap($this->options['typeMap']);
}
return $cursor;
}
}
......@@ -21,7 +21,6 @@ class DropDatabase implements Executable
* Constructs a dropDatabase command.
*
* @param string $databaseName Database name
* @param string $collectionName Collection name
*/
public function __construct($databaseName)
{
......
......@@ -79,6 +79,9 @@ class Find implements Executable
* "$orderby" also exists in the modifiers document, this option will
* take precedence.
*
* * typeMap (array): Type map for BSON deserialization. This will be
* applied to the returned Cursor (it is not sent to the server).
*
* @param string $databaseName Database name
* @param string $collectionName Collection name
* @param array|object $filter Query by which to filter documents
......@@ -155,6 +158,10 @@ class Find implements Executable
throw new InvalidArgumentTypeException('"sort" option', $options['sort'], 'array or object');
}
if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
throw new InvalidArgumentTypeException('"typeMap" option', $options['typeMap'], 'array');
}
$this->databaseName = (string) $databaseName;
$this->collectionName = (string) $collectionName;
$this->filter = $filter;
......@@ -172,7 +179,13 @@ class Find implements Executable
{
$readPreference = isset($this->options['readPreference']) ? $this->options['readPreference'] : null;
return $server->executeQuery($this->databaseName . '.' . $this->collectionName, $this->createQuery(), $readPreference);
$cursor = $server->executeQuery($this->databaseName . '.' . $this->collectionName, $this->createQuery(), $readPreference);
if (isset($this->options['typeMap'])) {
$cursor->setTypeMap($this->options['typeMap']);
}
return $cursor;
}
/**
......
......@@ -59,10 +59,6 @@ class FindOne implements Executable
*/
public function __construct($databaseName, $collectionName, $filter, array $options = [])
{
if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
throw new InvalidArgumentTypeException('"typeMap" option', $options['typeMap'], 'array');
}
$this->find = new Find(
$databaseName,
$collectionName,
......@@ -83,11 +79,6 @@ class FindOne implements Executable
public function execute(Server $server)
{
$cursor = $this->find->execute($server);
if (isset($this->options['typeMap'])) {
$cursor->setTypeMap($this->options['typeMap']);
}
$document = current($cursor->toArray());
return ($document === false) ? null : $document;
......
......@@ -3,6 +3,7 @@
namespace MongoDB\Tests;
use MongoDB\Client;
use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\WriteConcern;
......@@ -25,27 +26,40 @@ class ClientTest extends TestCase
$this->assertSame($this->getUri(), (string) $client);
}
public function testSelectCollectionInheritsReadPreferenceAndWriteConcern()
public function testSelectCollectionInheritsOptions()
{
$clientOptions = [
$this->markTestSkipped('Depends on https://jira.mongodb.org/browse/PHPC-523');
$uriOptions = [
'readConcernLevel' => ReadConcern::LOCAL,
'readPreference' => 'secondaryPreferred',
'w' => WriteConcern::MAJORITY,
];
$client = new Client($this->getUri(), $clientOptions);
$driverOptions = [
'typeMap' => ['root' => 'array'],
];
$client = new Client($this->getUri(), $uriOptions, $driverOptions);
$collection = $client->selectCollection($this->getDatabaseName(), $this->getCollectionName());
$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->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode());
$this->assertInternalType('array', $debug['typeMap']);
$this->assertSame(['root' => 'array'], $debug['typeMap']);
$this->assertInstanceOf('MongoDB\Driver\WriteConcern', $debug['writeConcern']);
$this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW());
}
public function testSelectCollectionPassesReadPreferenceAndWriteConcern()
public function testSelectCollectionPassesOptions()
{
$collectionOptions = [
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED),
'typeMap' => ['root' => 'array'],
'writeConcern' => new WriteConcern(WriteConcern::MAJORITY),
];
......@@ -53,33 +67,50 @@ class ClientTest extends TestCase
$collection = $client->selectCollection($this->getDatabaseName(), $this->getCollectionName(), $collectionOptions);
$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->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode());
$this->assertInternalType('array', $debug['typeMap']);
$this->assertSame(['root' => 'array'], $debug['typeMap']);
$this->assertInstanceOf('MongoDB\Driver\WriteConcern', $debug['writeConcern']);
$this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW());
}
public function testSelectDatabaseInheritsReadPreferenceAndWriteConcern()
public function testSelectDatabaseInheritsOptions()
{
$clientOptions = [
$this->markTestSkipped('Depends on https://jira.mongodb.org/browse/PHPC-523');
$uriOptions = [
'readConcernLevel' => ReadConcern::LOCAL,
'readPreference' => 'secondaryPreferred',
'w' => WriteConcern::MAJORITY,
];
$client = new Client($this->getUri(), $clientOptions);
$driverOptions = [
'typeMap' => ['root' => 'array'],
];
$client = new Client($this->getUri(), $uriOptions, $driverOptions);
$database = $client->selectDatabase($this->getDatabaseName());
$debug = $database->__debugInfo();
$this->assertInstanceOf('MongoDB\Driver\ReadConcern', $debug['readConcern']);
$this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel());
$this->assertInstanceOf('MongoDB\Driver\ReadPreference', $debug['readPreference']);
$this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode());
$this->assertInternalType('array', $debug['typeMap']);
$this->assertSame(['root' => 'array'], $debug['typeMap']);
$this->assertInstanceOf('MongoDB\Driver\WriteConcern', $debug['writeConcern']);
$this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW());
}
public function testSelectDatabasePassesReadPreferenceAndWriteConcern()
public function testSelectDatabasePassesOptions()
{
$databaseOptions = [
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED),
'typeMap' => ['root' => 'array'],
'writeConcern' => new WriteConcern(WriteConcern::MAJORITY),
];
......@@ -87,8 +118,12 @@ class ClientTest extends TestCase
$database = $client->selectDatabase($this->getDatabaseName(), $databaseOptions);
$debug = $database->__debugInfo();
$this->assertInstanceOf('MongoDB\Driver\ReadConcern', $debug['readConcern']);
$this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel());
$this->assertInstanceOf('MongoDB\Driver\ReadPreference', $debug['readPreference']);
$this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode());
$this->assertInternalType('array', $debug['typeMap']);
$this->assertSame(['root' => 'array'], $debug['typeMap']);
$this->assertInstanceOf('MongoDB\Driver\WriteConcern', $debug['writeConcern']);
$this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW());
}
......
......@@ -117,6 +117,7 @@ class CollectionFunctionalTest extends FunctionalTestCase
$collectionOptions = [
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED),
'typeMap' => ['root' => 'array'],
'writeConcern' => new WriteConcern(WriteConcern::MAJORITY),
];
......@@ -128,6 +129,8 @@ class CollectionFunctionalTest extends FunctionalTestCase
$this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel());
$this->assertInstanceOf('MongoDB\Driver\ReadPreference', $debug['readPreference']);
$this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode());
$this->assertInternalType('array', $debug['typeMap']);
$this->assertSame(['root' => 'array'], $debug['typeMap']);
$this->assertInstanceOf('MongoDB\Driver\WriteConcern', $debug['writeConcern']);
$this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW());
}
......@@ -137,6 +140,7 @@ class CollectionFunctionalTest extends FunctionalTestCase
$collectionOptions = [
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED),
'typeMap' => ['root' => 'array'],
'writeConcern' => new WriteConcern(WriteConcern::MAJORITY),
];
......@@ -147,6 +151,8 @@ class CollectionFunctionalTest extends FunctionalTestCase
$this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel());
$this->assertInstanceOf('MongoDB\Driver\ReadPreference', $debug['readPreference']);
$this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode());
$this->assertInternalType('array', $debug['typeMap']);
$this->assertSame(['root' => 'array'], $debug['typeMap']);
$this->assertInstanceOf('MongoDB\Driver\WriteConcern', $debug['writeConcern']);
$this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW());
}
......
......@@ -68,8 +68,11 @@ class DatabaseFunctionalTest extends FunctionalTestCase
public function testCommand()
{
$command = ['isMaster' => 1];
$readPreference = new ReadPreference(ReadPreference::RP_PRIMARY);
$cursor = $this->database->command($command, $readPreference);
$options = [
'readPreference' => new ReadPreference(ReadPreference::RP_PRIMARY),
];
$cursor = $this->database->command($command, $options);
$this->assertInstanceOf('MongoDB\Driver\Cursor', $cursor);
$commandResult = current($cursor->toArray());
......@@ -79,6 +82,25 @@ class DatabaseFunctionalTest extends FunctionalTestCase
$this->assertTrue($commandResult->ismaster);
}
public function testCommandAppliesTypeMapToCursor()
{
$command = ['isMaster' => 1];
$options = [
'readPreference' => new ReadPreference(ReadPreference::RP_PRIMARY),
'typeMap' => ['root' => 'array'],
];
$cursor = $this->database->command($command, $options);
$this->assertInstanceOf('MongoDB\Driver\Cursor', $cursor);
$commandResult = current($cursor->toArray());
$this->assertCommandSucceeded($commandResult);
$this->assertInternalType('array', $commandResult);
$this->assertTrue(isset($commandResult['ismaster']));
$this->assertTrue($commandResult['ismaster']);
}
/**
* @expectedException MongoDB\Exception\InvalidArgumentTypeException
* @dataProvider provideInvalidDocumentValues
......@@ -106,6 +128,7 @@ class DatabaseFunctionalTest extends FunctionalTestCase
$databaseOptions = [
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED),
'typeMap' => ['root' => 'array'],
'writeConcern' => new WriteConcern(WriteConcern::MAJORITY),
];
......@@ -117,6 +140,8 @@ class DatabaseFunctionalTest extends FunctionalTestCase
$this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel());
$this->assertInstanceOf('MongoDB\Driver\ReadPreference', $debug['readPreference']);
$this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode());
$this->assertInternalType('array', $debug['typeMap']);
$this->assertSame(['root' => 'array'], $debug['typeMap']);
$this->assertInstanceOf('MongoDB\Driver\WriteConcern', $debug['writeConcern']);
$this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW());
}
......@@ -126,6 +151,7 @@ class DatabaseFunctionalTest extends FunctionalTestCase
$collectionOptions = [
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED),
'typeMap' => ['root' => 'array'],
'writeConcern' => new WriteConcern(WriteConcern::MAJORITY),
];
......@@ -136,6 +162,8 @@ class DatabaseFunctionalTest extends FunctionalTestCase
$this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel());
$this->assertInstanceOf('MongoDB\Driver\ReadPreference', $debug['readPreference']);
$this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode());
$this->assertInternalType('array', $debug['typeMap']);
$this->assertSame(['root' => 'array'], $debug['typeMap']);
$this->assertInstanceOf('MongoDB\Driver\WriteConcern', $debug['writeConcern']);
$this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW());
}
......@@ -145,6 +173,7 @@ class DatabaseFunctionalTest extends FunctionalTestCase
$databaseOptions = [
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED),
'typeMap' => ['root' => 'array'],
'writeConcern' => new WriteConcern(WriteConcern::MAJORITY),
];
......@@ -156,6 +185,8 @@ class DatabaseFunctionalTest extends FunctionalTestCase
$this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel());
$this->assertInstanceOf('MongoDB\Driver\ReadPreference', $debug['readPreference']);
$this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode());
$this->assertInternalType('array', $debug['typeMap']);
$this->assertSame(['root' => 'array'], $debug['typeMap']);
$this->assertInstanceOf('MongoDB\Driver\WriteConcern', $debug['writeConcern']);
$this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW());
}
......@@ -165,6 +196,7 @@ class DatabaseFunctionalTest extends FunctionalTestCase
$databaseOptions = [
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED),
'typeMap' => ['root' => 'array'],
'writeConcern' => new WriteConcern(WriteConcern::MAJORITY),
];
......@@ -175,6 +207,8 @@ class DatabaseFunctionalTest extends FunctionalTestCase
$this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel());
$this->assertInstanceOf('MongoDB\Driver\ReadPreference', $debug['readPreference']);
$this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode());
$this->assertInternalType('array', $debug['typeMap']);
$this->assertSame(['root' => 'array'], $debug['typeMap']);
$this->assertInstanceOf('MongoDB\Driver\WriteConcern', $debug['writeConcern']);
$this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW());
}
......
......@@ -2,17 +2,85 @@
namespace MongoDB\Tests\Operation;
use MongoDB\Driver\BulkWrite;
use MongoDB\Operation\Aggregate;
class AggregateFunctionalTest extends FunctionalTestCase
{
private static $wireVersionForCursor = 2;
/**
* @expectedException MongoDB\Driver\Exception\RuntimeException
*/
public function testUnrecognizedPipelineState()
{
$server = $this->getPrimaryServer();
$operation = new Aggregate($this->getDatabaseName(), $this->getCollectionName(), [['$foo' => 1]]);
$operation->execute($server);
$operation->execute($this->getPrimaryServer());
}
/**
* @dataProvider provideTypeMapOptionsAndExpectedDocument
*/
public function testTypeMapOption(array $typeMap, array $expectedDocuments)
{
if ( ! \MongoDB\server_supports_feature($this->getPrimaryServer(), self::$wireVersionForCursor)) {
$this->markTestSkipped('Command cursor is not supported');
}
$this->createFixtures(3);
$pipeline = [['$match' => ['_id' => ['$ne' => 2]]]];
$operation = new Aggregate($this->getDatabaseName(), $this->getCollectionName(), $pipeline, ['typeMap' => $typeMap]);
$cursor = $operation->execute($this->getPrimaryServer());
$this->assertEquals($expectedDocuments, $cursor->toArray());
}
public function provideTypeMapOptionsAndExpectedDocument()
{
return [
[
['root' => 'array', 'document' => 'array'],
[
['_id' => 1, 'x' => ['foo' => 'bar']],
['_id' => 3, 'x' => ['foo' => 'bar']],
],
],
[
['root' => 'object', 'document' => 'array'],
[
(object) ['_id' => 1, 'x' => ['foo' => 'bar']],
(object) ['_id' => 3, 'x' => ['foo' => 'bar']],
],
],
[
['root' => 'array', 'document' => 'stdClass'],
[
['_id' => 1, 'x' => (object) ['foo' => 'bar']],
['_id' => 3, 'x' => (object) ['foo' => 'bar']],
],
],
];
}
/**
* Create data fixtures.
*
* @param integer $n
*/
private function createFixtures($n)
{
$bulkWrite = new BulkWrite(['ordered' => true]);
for ($i = 1; $i <= $n; $i++) {
$bulkWrite->insert([
'_id' => $i,
'x' => (object) ['foo' => 'bar'],
]);
}
$result = $this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite);
$this->assertEquals($n, $result->getInsertedCount());
}
}
......@@ -61,6 +61,10 @@ class AggregateTest extends TestCase
$options[][] = ['readPreference' => $value];
}
foreach ($this->getInvalidArrayValues() as $value) {
$options[][] = ['typeMap' => $value];
}
foreach ($this->getInvalidBooleanValues() as $value) {
$options[][] = ['useCursor' => $value];
}
......@@ -81,4 +85,18 @@ class AggregateTest extends TestCase
['batchSize' => 100, 'useCursor' => false]
);
}
/**
* @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessage "typeMap" option should not be used if "useCursor" is false
*/
public function testConstructorTypeMapOptionRequiresUseCursor()
{
new Aggregate(
$this->getDatabaseName(),
$this->getCollectionName(),
[['$match' => ['x' => 1]]],
['typeMap' => ['root' => 'array'], 'useCursor' => false]
);
}
}
<?php
namespace MongoDB\Tests\Operation;
use MongoDB\Operation\DatabaseCommand;
class DatabaseCommandTest extends TestCase
{
/**
* @expectedException MongoDB\Exception\InvalidArgumentTypeException
* @dataProvider provideInvalidDocumentValues
*/
public function testConstructorCommandArgumentTypeCheck($command)
{
new DatabaseCommand($this->getDatabaseName(), $command);
}
/**
* @expectedException MongoDB\Exception\InvalidArgumentTypeException
* @dataProvider provideInvalidConstructorOptions
*/
public function testConstructorOptionTypeChecks(array $options)
{
new DatabaseCommand($this->getDatabaseName(), ['ping' => 1], $options);
}
public function provideInvalidConstructorOptions()
{
$options = [];
foreach ($this->getInvalidReadPreferenceValues() as $value) {
$options[][] = ['readPreference' => $value];
}
foreach ($this->getInvalidArrayValues() as $value) {
$options[][] = ['typeMap' => $value];
}
return $options;
}
}
<?php
namespace MongoDB\Tests\Operation;
use MongoDB\Driver\BulkWrite;
use MongoDB\Operation\Find;
class FindFunctionalTest extends FunctionalTestCase
{
/**
* @dataProvider provideTypeMapOptionsAndExpectedDocuments
*/
public function testTypeMapOption(array $typeMap, array $expectedDocuments)
{
$this->createFixtures(3);
$operation = new Find($this->getDatabaseName(), $this->getCollectionName(), [], ['typeMap' => $typeMap]);
$cursor = $operation->execute($this->getPrimaryServer());
$this->assertEquals($expectedDocuments, $cursor->toArray());
}
public function provideTypeMapOptionsAndExpectedDocuments()
{
return [
[
['root' => 'array', 'document' => 'array'],
[
['_id' => 1, 'x' => ['foo' => 'bar']],
['_id' => 2, 'x' => ['foo' => 'bar']],
['_id' => 3, 'x' => ['foo' => 'bar']],
],
],
[
['root' => 'object', 'document' => 'array'],
[
(object) ['_id' => 1, 'x' => ['foo' => 'bar']],
(object) ['_id' => 2, 'x' => ['foo' => 'bar']],
(object) ['_id' => 3, 'x' => ['foo' => 'bar']],
],
],
[
['root' => 'array', 'document' => 'stdClass'],
[
['_id' => 1, 'x' => (object) ['foo' => 'bar']],
['_id' => 2, 'x' => (object) ['foo' => 'bar']],
['_id' => 3, 'x' => (object) ['foo' => 'bar']],
],
],
];
}
/**
* Create data fixtures.
*
* @param integer $n
*/
private function createFixtures($n)
{
$bulkWrite = new BulkWrite(['ordered' => true]);
for ($i = 1; $i <= $n; $i++) {
$bulkWrite->insert([
'_id' => $i,
'x' => (object) ['foo' => 'bar'],
]);
}
$result = $this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite);
$this->assertEquals($n, $result->getInsertedCount());
}
}
<?php
namespace MongoDB\Tests\Operation;
use MongoDB\Operation\FindOne;
class FindOneTest extends TestCase
{
/**
* @expectedException MongoDB\Exception\InvalidArgumentException
* @dataProvider provideInvalidConstructorTypeMapOptions
*/
public function testConstructorTypeMapOption($typeMap)
{
new FindOne($this->getDatabaseName(), $this->getCollectionName(), [], ['typeMap' => $typeMap]);
}
public function provideInvalidConstructorTypeMapOptions()
{
return $this->wrapValuesForDataProvider($this->getInvalidArrayValues());
}
}
......@@ -80,6 +80,10 @@ class FindTest extends TestCase
$options[][] = ['sort' => $value];
}
foreach ($this->getInvalidArrayValues() as $value) {
$options[][] = ['typeMap' => $value];
}
return $options;
}
......
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