Commit fbb10fec authored by Jeremy Mikola's avatar Jeremy Mikola

Merge pull request #36

parents ed4cfa1c 4146fac8
......@@ -3,6 +3,7 @@
namespace MongoDB\Operation;
use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentTypeException;
/**
* Operation for finding a single document with the find command.
......@@ -15,6 +16,7 @@ use MongoDB\Driver\Server;
class FindOne implements Executable
{
private $find;
private $options;
/**
* Constructs a find command for finding a single document.
......@@ -42,6 +44,8 @@ class FindOne implements Executable
* "$orderby" also exists in the modifiers document, this option will
* take precedence.
*
* * typeMap (array): Type map for BSON deserialization.
*
* @param string $databaseName Database name
* @param string $collectionName Collection name
* @param array|object $filter Query by which to filter documents
......@@ -50,12 +54,18 @@ class FindOne implements Executable
*/
public function __construct($databaseName, $collectionName, $filter, array $options = array())
{
if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
throw new InvalidArgumentTypeException('"typeMap" option', $options['typeMap'], 'array');
}
$this->find = new Find(
$databaseName,
$collectionName,
$filter,
array('limit' => -1) + $options
);
$this->options = $options;
}
/**
......@@ -68,6 +78,11 @@ 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;
......
......@@ -44,28 +44,23 @@ class FindOneAndDelete implements Executable
throw new InvalidArgumentTypeException('$filter', $filter, 'array or object');
}
if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {
throw new InvalidArgumentTypeException('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
}
if (isset($options['projection']) && ! is_array($options['projection']) && ! is_object($options['projection'])) {
throw new InvalidArgumentTypeException('"projection" option', $options['projection'], 'array or object');
}
if (isset($options['sort']) && ! is_array($options['sort']) && ! is_object($options['sort'])) {
throw new InvalidArgumentTypeException('"sort" option', $options['sort'], 'array or object');
if (isset($options['projection'])) {
$options['fields'] = $options['projection'];
}
unset($options['projection']);
$this->findAndModify = new FindAndModify(
$databaseName,
$collectionName,
array(
'fields' => isset($options['projection']) ? $options['projection'] : null,
'maxTimeMS' => isset($options['maxTimeMS']) ? $options['maxTimeMS'] : null,
'query' => $filter,
'remove' => true,
'sort' => isset($options['sort']) ? $options['sort'] : null,
)
) + $options
);
}
......
......@@ -68,10 +68,6 @@ class FindOneAndReplace implements Executable
'upsert' => false,
);
if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {
throw new InvalidArgumentTypeException('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
}
if (isset($options['projection']) && ! is_array($options['projection']) && ! is_object($options['projection'])) {
throw new InvalidArgumentTypeException('"projection" option', $options['projection'], 'array or object');
}
......@@ -85,26 +81,21 @@ class FindOneAndReplace implements Executable
throw new InvalidArgumentException('Invalid value for "returnDocument" option: ' . $options['returnDocument']);
}
if (isset($options['sort']) && ! is_array($options['sort']) && ! is_object($options['sort'])) {
throw new InvalidArgumentTypeException('"sort" option', $options['sort'], 'array or object');
if (isset($options['projection'])) {
$options['fields'] = $options['projection'];
}
if ( ! is_bool($options['upsert'])) {
throw new InvalidArgumentTypeException('"upsert" option', $options['upsert'], 'boolean');
}
$options['new'] = $options['returnDocument'] === self::RETURN_DOCUMENT_AFTER;
unset($options['projection'], $options['returnDocument']);
$this->findAndModify = new FindAndModify(
$databaseName,
$collectionName,
array(
'fields' => isset($options['projection']) ? $options['projection'] : null,
'maxTimeMS' => isset($options['maxTimeMS']) ? $options['maxTimeMS'] : null,
'new' => $options['returnDocument'] === self::RETURN_DOCUMENT_AFTER,
'query' => $filter,
'sort' => isset($options['sort']) ? $options['sort'] : null,
'update' => $replacement,
'upsert' => $options['upsert'],
)
) + $options
);
}
......
......@@ -68,10 +68,6 @@ class FindOneAndUpdate implements Executable
'upsert' => false,
);
if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {
throw new InvalidArgumentTypeException('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
}
if (isset($options['projection']) && ! is_array($options['projection']) && ! is_object($options['projection'])) {
throw new InvalidArgumentTypeException('"projection" option', $options['projection'], 'array or object');
}
......@@ -85,26 +81,21 @@ class FindOneAndUpdate implements Executable
throw new InvalidArgumentException('Invalid value for "returnDocument" option: ' . $options['returnDocument']);
}
if (isset($options['sort']) && ! is_array($options['sort']) && ! is_object($options['sort'])) {
throw new InvalidArgumentTypeException('"sort" option', $options['sort'], 'array or object');
if (isset($options['projection'])) {
$options['fields'] = $options['projection'];
}
if ( ! is_bool($options['upsert'])) {
throw new InvalidArgumentTypeException('"upsert" option', $options['upsert'], 'boolean');
}
$options['new'] = $options['returnDocument'] === self::RETURN_DOCUMENT_AFTER;
unset($options['projection'], $options['returnDocument']);
$this->findAndModify = new FindAndModify(
$databaseName,
$collectionName,
array(
'fields' => isset($options['projection']) ? $options['projection'] : null,
'maxTimeMS' => isset($options['maxTimeMS']) ? $options['maxTimeMS'] : null,
'new' => $options['returnDocument'] === self::RETURN_DOCUMENT_AFTER,
'query' => $filter,
'sort' => isset($options['sort']) ? $options['sort'] : null,
'update' => $update,
'upsert' => $options['upsert'],
)
) + $options
);
}
......
......@@ -4,6 +4,7 @@ namespace MongoDB\Tests\Collection\CrudSpec;
use MongoDB\Collection;
use MongoDB\Driver\ReadPreference;
use MongoDB\Operation\DropCollection;
/**
* CRUD spec functional tests for aggregate().
......@@ -48,7 +49,8 @@ class AggregateFunctionalTest extends FunctionalTestCase
}
$outputCollection = new Collection($this->manager, $this->getNamespace() . '_output');
$this->dropCollectionIfItExists($outputCollection);
$operation = new DropCollection($this->getDatabaseName(), $outputCollection->getCollectionName());
$operation->execute($this->getPrimaryServer());
$this->collection->aggregate(
array(
......@@ -66,6 +68,7 @@ class AggregateFunctionalTest extends FunctionalTestCase
$this->assertSameDocuments($expected, $outputCollection->find());
// Manually clean up our output collection
$this->dropCollectionIfItExists($outputCollection);
$operation = new DropCollection($this->getDatabaseName(), $outputCollection->getCollectionName());
$operation->execute($this->getPrimaryServer());
}
}
......@@ -3,7 +3,7 @@
namespace MongoDB\Tests\Collection;
use MongoDB\Collection;
use MongoDB\Database;
use MongoDB\Operation\DropCollection;
use MongoDB\Tests\FunctionalTestCase as BaseFunctionalTestCase;
/**
......@@ -18,7 +18,8 @@ abstract class FunctionalTestCase extends BaseFunctionalTestCase
parent::setUp();
$this->collection = new Collection($this->manager, $this->getNamespace());
$this->dropCollectionIfItExists($this->collection);
$operation = new DropCollection($this->getDatabaseName(), $this->getCollectionName());
$operation->execute($this->getPrimaryServer());
}
public function tearDown()
......@@ -27,21 +28,7 @@ abstract class FunctionalTestCase extends BaseFunctionalTestCase
return;
}
$this->dropCollectionIfItExists($this->collection);
}
/**
* Drop the collection if it exists.
*
* @param Collection $collection
*/
protected function dropCollectionIfItExists(Collection $collection)
{
$database = new Database($this->manager, $collection->getDatabaseName());
$collections = $database->listCollections(array('filter' => array('name' => $collection->getCollectionName())));
if (iterator_count($collections) > 0) {
$this->assertCommandSucceeded($collection->drop());
}
$operation = new DropCollection($this->getDatabaseName(), $this->getCollectionName());
$operation->execute($this->getPrimaryServer());
}
}
......@@ -68,6 +68,11 @@ abstract class FunctionalTestCase extends TestCase
);
}
protected function getPrimaryServer()
{
return $this->manager->selectServer(new ReadPreference(ReadPreference::RP_PRIMARY));
}
protected function getServerVersion(ReadPreference $readPreference = null)
{
$cursor = $this->manager->executeCommand(
......
<?php
namespace MongoDB\Tests\Operation;
use MongoDB\Operation\Aggregate;
class AggregateFunctionalTest extends FunctionalTestCase
{
/**
* @expectedException MongoDB\Driver\Exception\RuntimeException
*/
public function testUnrecognizedPipelineState()
{
$server = $this->getPrimaryServer();
$operation = new Aggregate($this->getDatabaseName(), $this->getCollectionName(), [['$foo' => 1]]);
$operation->execute($server);
}
}
......@@ -27,6 +27,9 @@ class DropDatabaseFunctionalTest extends FunctionalTestCase
{
$server = $this->getPrimaryServer();
$operation = new DropDatabase($this->getDatabaseName());
$operation->execute($server);
$this->assertDatabaseDoesNotExist($server, $this->getDatabaseName());
$operation = new DropDatabase($this->getDatabaseName());
......
<?php
namespace MongoDB\Tests\Operation;
use MongoDB\Operation\FindAndModify;
class FindAndModifyTest extends TestCase
{
/**
* @expectedException MongoDB\Exception\InvalidArgumentTypeException
* @dataProvider provideInvalidConstructorOptions
*/
public function testConstructorOptionTypeChecks(array $options)
{
new FindAndModify($this->getDatabaseName(), $this->getCollectionName(), $options);
}
public function provideInvalidConstructorOptions()
{
$options = [];
foreach ($this->getInvalidDocumentValues() as $value) {
$options[][] = ['fields' => $value];
}
foreach ($this->getInvalidIntegerValues() as $value) {
$options[][] = ['maxTimeMS' => $value];
}
foreach ($this->getInvalidBooleanValues() as $value) {
$options[][] = ['new' => $value];
}
foreach ($this->getInvalidDocumentValues() as $value) {
$options[][] = ['query' => $value];
}
foreach ($this->getInvalidBooleanValues() as $value) {
$options[][] = ['remove' => $value];
}
foreach ($this->getInvalidDocumentValues() as $value) {
$options[][] = ['sort' => $value];
}
foreach ($this->getInvalidDocumentValues() as $value) {
$options[][] = ['update' => $value];
}
foreach ($this->getInvalidBooleanValues() as $value) {
$options[][] = ['upsert' => $value];
}
return $options;
}
/**
* @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessage The "remove" option must be true or an "update" document must be specified, but not both
*/
public function testConstructorUpdateAndRemoveOptionsAreMutuallyExclusive()
{
new FindAndModify($this->getDatabaseName(), $this->getCollectionName(), ['remove' => true, 'update' => []]);
}
}
<?php
namespace MongoDB\Tests\Operation;
use MongoDB\Operation\FindOneAndDelete;
class FindOneAndDeleteTest extends TestCase
{
/**
* @expectedException MongoDB\Exception\InvalidArgumentTypeException
* @dataProvider provideInvalidDocumentValues
*/
public function testConstructorFilterArgumentTypeCheck($filter)
{
new FindOneAndDelete($this->getDatabaseName(), $this->getCollectionName(), $filter);
}
/**
* @expectedException MongoDB\Exception\InvalidArgumentTypeException
* @dataProvider provideInvalidConstructorOptions
*/
public function testConstructorOptionTypeChecks(array $options)
{
new FindOneAndDelete($this->getDatabaseName(), $this->getCollectionName(), [], $options);
}
public function provideInvalidConstructorOptions()
{
$options = [];
foreach ($this->getInvalidDocumentValues() as $value) {
$options[][] = ['projection' => $value];
}
return $options;
}
}
<?php
namespace MongoDB\Tests\Operation;
use MongoDB\Operation\FindOneAndReplace;
class FindOneAndReplaceTest extends TestCase
{
/**
* @expectedException MongoDB\Exception\InvalidArgumentTypeException
* @dataProvider provideInvalidDocumentValues
*/
public function testConstructorFilterArgumentTypeCheck($filter)
{
new FindOneAndReplace($this->getDatabaseName(), $this->getCollectionName(), $filter, []);
}
/**
* @expectedException MongoDB\Exception\InvalidArgumentTypeException
* @dataProvider provideInvalidDocumentValues
*/
public function testConstructorReplacementArgumentTypeCheck($replacement)
{
new FindOneAndReplace($this->getDatabaseName(), $this->getCollectionName(), [], $replacement);
}
/**
* @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessage First key in $replacement argument is an update operator
*/
public function testConstructorReplacementArgumentRequiresNoOperators()
{
new FindOneAndReplace($this->getDatabaseName(), $this->getCollectionName(), [], ['$set' => ['x' => 1]]);
}
/**
* @expectedException MongoDB\Exception\InvalidArgumentTypeException
* @dataProvider provideInvalidConstructorOptions
*/
public function testConstructorOptionTypeChecks(array $options)
{
new FindOneAndReplace($this->getDatabaseName(), $this->getCollectionName(), [], [], $options);
}
public function provideInvalidConstructorOptions()
{
$options = [];
foreach ($this->getInvalidDocumentValues() as $value) {
$options[][] = ['projection' => $value];
}
foreach ($this->getInvalidIntegerValues() as $value) {
$options[][] = ['returnDocument' => $value];
}
return $options;
}
/**
* @expectedException MongoDB\Exception\InvalidArgumentException
* @dataProvider provideInvalidConstructorReturnDocumentOptions
*/
public function testConstructorReturnDocumentOption($returnDocument)
{
new FindOneAndReplace($this->getDatabaseName(), $this->getCollectionName(), [], [], ['returnDocument' => $returnDocument]);
}
public function provideInvalidConstructorReturnDocumentOptions()
{
return $this->wrapValuesForDataProvider([-1, 0, 3]);
}
}
<?php
namespace MongoDB\Tests\Operation;
use MongoDB\Operation\FindOneAndUpdate;
class FindOneAndUpdateTest extends TestCase
{
/**
* @expectedException MongoDB\Exception\InvalidArgumentTypeException
* @dataProvider provideInvalidDocumentValues
*/
public function testConstructorFilterArgumentTypeCheck($filter)
{
new FindOneAndUpdate($this->getDatabaseName(), $this->getCollectionName(), $filter, []);
}
/**
* @expectedException MongoDB\Exception\InvalidArgumentTypeException
* @dataProvider provideInvalidDocumentValues
*/
public function testConstructorUpdateArgumentTypeCheck($update)
{
new FindOneAndUpdate($this->getDatabaseName(), $this->getCollectionName(), [], $update);
}
/**
* @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessage First key in $update argument is not an update operator
*/
public function testConstructorUpdateArgumentRequiresOperators()
{
new FindOneAndUpdate($this->getDatabaseName(), $this->getCollectionName(), [], []);
}
/**
* @expectedException MongoDB\Exception\InvalidArgumentTypeException
* @dataProvider provideInvalidConstructorOptions
*/
public function testConstructorOptionTypeChecks(array $options)
{
new FindOneAndUpdate($this->getDatabaseName(), $this->getCollectionName(), [], ['$set' => ['x' => 1]], $options);
}
public function provideInvalidConstructorOptions()
{
$options = [];
foreach ($this->getInvalidDocumentValues() as $value) {
$options[][] = ['projection' => $value];
}
foreach ($this->getInvalidIntegerValues() as $value) {
$options[][] = ['returnDocument' => $value];
}
return $options;
}
/**
* @expectedException MongoDB\Exception\InvalidArgumentException
* @dataProvider provideInvalidConstructorReturnDocumentOptions
*/
public function testConstructorReturnDocumentOption($returnDocument)
{
new FindOneAndUpdate($this->getDatabaseName(), $this->getCollectionName(), [], [], ['returnDocument' => $returnDocument]);
}
public function provideInvalidConstructorReturnDocumentOptions()
{
return $this->wrapValuesForDataProvider([-1, 0, 3]);
}
}
<?php
namespace MongoDB\Tests\Operation;
use MongoDB\Driver\BulkWrite;
use MongoDB\Operation\FindOne;
class FindOneFunctionalTest extends FunctionalTestCase
{
/**
* @dataProvider provideTypeMapOptionsAndExpectedDocument
*/
public function testTypeMapOption(array $typeMap, $expectedDocument)
{
$this->createFixtures(1);
$operation = new FindOne($this->getDatabaseName(), $this->getCollectionName(), [], ['typeMap' => $typeMap]);
$document = $operation->execute($this->getPrimaryServer());
$this->assertEquals($expectedDocument, $document);
}
public function provideTypeMapOptionsAndExpectedDocument()
{
return [
[
['root' => 'array', 'document' => 'array'],
['_id' => 1, 'x' => ['foo' => 'bar']],
],
[
['root' => 'object', 'document' => 'array'],
(object) ['_id' => 1, 'x' => ['foo' => 'bar']],
],
[
['root' => 'array', 'document' => 'stdClass'],
['_id' => 1, 'x' => (object) ['foo' => 'bar']],
],
];
}
/**
* Create data fixtures.
*
* @param integer $n
*/
private function createFixtures($n)
{
$bulkWrite = new BulkWrite(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());
}
}
......@@ -3,7 +3,6 @@
namespace MongoDB\Tests\Operation;
use MongoDB\Operation\Find;
use stdClass;
class FindTest extends TestCase
{
......
......@@ -2,7 +2,9 @@
namespace MongoDB\Tests\Operation;
use MongoDB\Collection;
use MongoDB\Driver\ReadPreference;
use MongoDB\Operation\DropCollection;
use MongoDB\Tests\FunctionalTestCase as BaseFunctionalTestCase;
/**
......@@ -10,8 +12,21 @@ use MongoDB\Tests\FunctionalTestCase as BaseFunctionalTestCase;
*/
abstract class FunctionalTestCase extends BaseFunctionalTestCase
{
public function getPrimaryServer()
public function setUp()
{
return $this->manager->selectServer(new ReadPreference(ReadPreference::RP_PRIMARY));
parent::setUp();
$operation = new DropCollection($this->getDatabaseName(), $this->getCollectionName());
$operation->execute($this->getPrimaryServer());
}
public function tearDown()
{
if ($this->hasFailed()) {
return;
}
$operation = new DropCollection($this->getDatabaseName(), $this->getCollectionName());
$operation->execute($this->getPrimaryServer());
}
}
......@@ -20,6 +20,11 @@ abstract class TestCase extends BaseTestCase
return $this->wrapValuesForDataProvider($this->getInvalidBooleanValues());
}
protected function getInvalidArrayValues()
{
return array(123, 3.14, 'foo', true, new stdClass);
}
protected function getInvalidBooleanValues()
{
return array(123, 3.14, 'foo', array(), new stdClass);
......
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