diff --git a/src/Operation/Delete.php b/src/Operation/Delete.php index ecf8f05f54d411d615bbf67ee302a3731bd24c39..96cec8768d136a8f170e5b88f362de72631b51b3 100644 --- a/src/Operation/Delete.php +++ b/src/Operation/Delete.php @@ -117,8 +117,6 @@ class Delete implements Executable, Explainable throw UnsupportedException::collationNotSupported(); } - $deleteOptions = $this->createDeleteOptions(); - $bulk = new Bulk(); $bulk->delete($this->filter, $this->createDeleteOptions()); @@ -136,7 +134,7 @@ class Delete implements Executable, Explainable * Create options for the delete command. * * Note that these options are different from the bulk write options, which - * are created in createOptions(). + * are created in createExecuteOptions(). * * @return array */ diff --git a/src/Operation/Find.php b/src/Operation/Find.php index d667044bdfe7f0b03ca51437895732e73ce035db..94690ae7fd37a8e683ea408f52ee2cc5f759afff 100644 --- a/src/Operation/Find.php +++ b/src/Operation/Find.php @@ -284,7 +284,7 @@ class Find implements Executable, Explainable throw UnsupportedException::readConcernNotSupported(); } - $cursor = $server->executeQuery($this->databaseName . '.' . $this->collectionName, new Query($this->filter, $this->createFindOptions()), $this->createOptions()); + $cursor = $server->executeQuery($this->databaseName . '.' . $this->collectionName, new Query($this->filter, $this->createQueryOptions()), $this->createExecuteOptions()); if (isset($this->options['typeMap'])) { $cursor->setTypeMap($this->options['typeMap']); @@ -303,9 +303,9 @@ class Find implements Executable, Explainable */ private function createCommandDocument() { - $cmd = ['find' => $this->collectionName, 'filter' => new BSONDocument($this->filter)]; + $cmd = ['find' => $this->collectionName, 'filter' => (object) $this->filter]; - $options = $this->createFindOptions(); + $options = $this->createQueryOptions(); if (empty($options)) { return $cmd; @@ -314,28 +314,60 @@ class Find implements Executable, Explainable // maxAwaitTimeMS is a Query level option so should not be considered here unset($options['maxAwaitTimeMS']); - if (isset($options['cursorType'])) { - if ($options['cursorType'] === self::TAILABLE) { - $cmd['tailable'] = true; - } - if ($options['cursorType'] === self::TAILABLE_AWAIT) { - $cmd['tailable'] = true; - $cmd['awaitData'] = true; + $modifierFallback = [ + ['allowPartialResults' , 'partial'], + ['comment' , '$comment'], + ['hint' , '$hint'], + ['maxScan' , '$maxScan'], + ['max' , '$max'], + ['maxTimeMS' , '$maxTimeMS'], + ['min' , '$min'], + ['returnKey' , '$returnKey'], + ['showRecordId' , '$showDiskLoc'], + ['sort' , '$orderby'], + ['snapshot' , '$snapshot'], + ]; + + foreach ($modifierFallback as $modifier) { + if ( ! isset($options[$modifier[0]]) && isset($options['modifiers'][$modifier[1]])) { + $options[$modifier[0]] = $options['modifiers'][$modifier[1]]; } } + unset($options['modifiers']); return $cmd + $options; } + /** + * Create options for executing the command. + * + * @see http://php.net/manual/en/mongodb-driver-server.executequery.php + * @return array + */ + private function createExecuteOptions() + { + $options = []; + + if (isset($this->options['readPreference'])) { + $options['readPreference'] = $this->options['readPreference']; + } + + if (isset($this->options['session'])) { + $options['session'] = $this->options['session']; + } + + return $options; + } + /** * Create options for the find query. * * Note that these are separate from the options for executing the command, - * which are created in createOptions(). + * which are created in createExecuteOptions(). * * @return array */ - private function createFindOptions() + private function createQueryOptions() { $options = []; @@ -369,25 +401,4 @@ class Find implements Executable, Explainable return $options; } - - /** - * Create options for executing the command. - * - * @see http://php.net/manual/en/mongodb-driver-server.executequery.php - * @return array - */ - private function createOptions() - { - $options = []; - - if (isset($this->options['readPreference'])) { - $options['readPreference'] = $this->options['readPreference']; - } - - if (isset($this->options['session'])) { - $options['session'] = $this->options['session']; - } - - return $options; - } } diff --git a/src/Operation/Update.php b/src/Operation/Update.php index b7ee16ead0deddbb8e0bd6bb600207832a5e228d..5ccd305aec0f70767c48683f8b5169a4e1b6b1e2 100644 --- a/src/Operation/Update.php +++ b/src/Operation/Update.php @@ -183,7 +183,7 @@ class Update implements Executable, Explainable public function getCommandDocument() { - return ['update' => $this->collectionName, 'updates' => [['q' => $this->filter] + ['u' => $this->update] + $this->createUpdateOptions()]]; + return ['update' => $this->collectionName, 'updates' => [['q' => $this->filter, 'u' => $this->update] + $this->createUpdateOptions()]]; } /** @@ -211,7 +211,7 @@ class Update implements Executable, Explainable * Create options for the update command. * * Note that these options are different from the bulk write options, which - * are created in createOptions(). + * are created in createExecuteOptions(). * * @return array */ diff --git a/tests/Operation/ExplainFunctionalTest.php b/tests/Operation/ExplainFunctionalTest.php index 40edf9318997a55d501ae1851e5f3c3f77cc8d04..eec61d822ee841cb9dbc6683ef19c6ec57f519a4 100644 --- a/tests/Operation/ExplainFunctionalTest.php +++ b/tests/Operation/ExplainFunctionalTest.php @@ -12,8 +12,9 @@ use MongoDB\Operation\Explain; use MongoDB\Operation\Find; use MongoDB\Operation\FindAndModify; use MongoDB\Operation\FindOne; -use MongoDB\Operation\InsertMany; use MongoDB\Operation\Update; +use MongoDB\Tests\CommandObserver; +use stdClass; class ExplainFunctionalTest extends FunctionalTestCase { @@ -30,13 +31,7 @@ class ExplainFunctionalTest extends FunctionalTestCase */ public function testCount($verbosity, $executionStatsExpected, $allPlansExecutionExpected) { - $insertMany = new InsertMany($this->getDatabaseName(), $this->getCollectionName(), [ - ['x' => 0], - ['x' => 1], - ['x' => 2], - ['y' => 3] - ]); - $insertMany->execute($this->getPrimaryServer()); + $this->createFixtures(3); $operation = new Count($this->getDatabaseName(), $this->getCollectionName(), ['x' => ['$gte' => 1]], []); @@ -114,10 +109,7 @@ class ExplainFunctionalTest extends FunctionalTestCase $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); } - /** - * @dataProvider provideVerbosityInformation - */ - public function testFindMaxAwait($verbosity, $executionStatsExpected, $allPlansExecutionExpected) + public function testFindMaxAwait() { if (version_compare($this->getServerVersion(), '3.2.0', '<')) { $this->markTestSkipped('maxAwaitTimeMS option is not supported'); @@ -142,20 +134,47 @@ class ExplainFunctionalTest extends FunctionalTestCase $operation = new CreateCollection($databaseName, $cappedCollectionName, $cappedCollectionOptions); $operation->execute($this->getPrimaryServer()); - // Insert documents into the capped collection. - $bulkWrite = new BulkWrite(['ordered' => true]); - $bulkWrite->insert(['_id' => 1]); - $bulkWrite->insert(['_id' => 2]); - $result = $this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite); + $this->createFixtures(2); $operation = new Find($databaseName, $cappedCollectionName, [], ['cursorType' => Find::TAILABLE_AWAIT, 'maxAwaitTimeMS' => $maxAwaitTimeMS]); - $explainOperation = new Explain($this->getDatabaseName(), $operation, ['verbosity' => $verbosity, 'typeMap' => ['root' => 'array', 'document' => 'array']]); - $result = $explainOperation->execute($this->getPrimaryServer()); + (new CommandObserver)->observe( + function() use ($operation) { + $explainOperation = new Explain($this->getDatabaseName(), $operation, ['typeMap' => ['root' => 'array', 'document' => 'array']]); + $explainOperation->execute($this->getPrimaryServer()); + }, + function(stdClass $command) { + $this->assertObjectNotHasAttribute('maxAwaitTimeMS', $command->explain); + $this->assertObjectHasAttribute('tailable', $command->explain); + $this->assertObjectHasAttribute('awaitData', $command->explain); + } + ); + } - $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); + public function testFindModifiers() + { + $this->createFixtures(3); + + $operation = new Find( + $this->getDatabaseName(), + $this->getCollectionName(), + [], + ['readConcern' => $this->createDefaultReadConcern(), 'modifiers' => ['$max' => ['_id' => 2.2]]] + ); + + (new CommandObserver)->observe( + function() use ($operation) { + $explainOperation = new Explain($this->getDatabaseName(), $operation, ['typeMap' => ['root' => 'array', 'document' => 'array']]); + $explainOperation->execute($this->getPrimaryServer()); + }, + function(stdClass $command) { + $this->assertObjectHasAttribute('max', $command->explain); + $this->assertObjectNotHasAttribute('modifiers', $command->explain); + } + ); } + /** * @dataProvider provideVerbosityInformation */ @@ -194,7 +213,7 @@ class ExplainFunctionalTest extends FunctionalTestCase return [ [Explain::VERBOSITY_ALL_PLANS, true, true], [Explain::VERBOSITY_EXEC_STATS, true, false], - [Explain::VERBOSITY_QUERY, false, false] + [Explain::VERBOSITY_QUERY, false, false], ]; } diff --git a/tests/Operation/ExplainTest.php b/tests/Operation/ExplainTest.php index 6711e887fd620ec5e041e3ab91ca53fb7538aabf..a0ee6149f0aaaf73ccd4b360d7f6ef73869330f3 100644 --- a/tests/Operation/ExplainTest.php +++ b/tests/Operation/ExplainTest.php @@ -9,6 +9,7 @@ use MongoDB\Operation\Explain; class ExplainTest extends TestCase { /** + * @requires PHPUnit 5.4.0 * @expectedException MongoDB\Exception\InvalidArgumentException * @dataProvider provideInvalidConstructorOptions */