Commit d9958464 authored by Katherine Walker's avatar Katherine Walker

Merge pull request #500

parents bde81988 0bc9a21a
......@@ -42,6 +42,18 @@ operation: ~
optional: true
---
arg_name: option
name: explain
type: boolean
description: |
Specifies whether or not to return the information on the processing of the
pipeline.
.. versionadded:: 1.4
interface: phpmethod
operation: ~
optional: true
---
arg_name: option
name: hint
type: string|array|object
description: |
......
......@@ -77,6 +77,9 @@ class Aggregate implements Executable
* * comment (string): An arbitrary string to help trace the operation
* through the database profiler, currentOp, and logs.
*
* * explain (boolean): Specifies whether or not to return the information
* on the processing of the pipeline.
*
* * hint (string|document): The index to use. Specify either the index
* name as a string or the index key pattern as a document. If specified,
* then the query system will only consider plans using the hinted index.
......@@ -160,6 +163,10 @@ class Aggregate implements Executable
throw InvalidArgumentException::invalidType('"comment" option', $options['comment'], 'string');
}
if (isset($options['explain']) && ! is_bool($options['explain'])) {
throw InvalidArgumentException::invalidType('"explain" option', $options['explain'], 'boolean');
}
if (isset($options['hint']) && ! is_string($options['hint']) && ! is_array($options['hint']) && ! is_object($options['hint'])) {
throw InvalidArgumentException::invalidType('"hint" option', $options['hint'], 'string or array or object');
}
......@@ -208,6 +215,10 @@ class Aggregate implements Executable
unset($options['writeConcern']);
}
if ( ! empty($options['explain'])) {
$options['useCursor'] = false;
}
$this->databaseName = (string) $databaseName;
$this->collectionName = (string) $collectionName;
$this->pipeline = $pipeline;
......@@ -238,16 +249,17 @@ class Aggregate implements Executable
throw UnsupportedException::writeConcernNotSupported();
}
$hasExplain = ! empty($this->options['explain']);
$hasOutStage = \MongoDB\is_last_pipeline_operator_out($this->pipeline);
$command = $this->createCommand($server);
$options = $this->createOptions($hasOutStage);
$options = $this->createOptions($hasOutStage, $hasExplain);
$cursor = $hasOutStage
$cursor = ($hasOutStage && ! $hasExplain)
? $server->executeReadWriteCommand($this->databaseName, $command, $options)
: $server->executeReadCommand($this->databaseName, $command, $options);
if ($this->options['useCursor']) {
if ($this->options['useCursor'] || $hasExplain) {
if (isset($this->options['typeMap'])) {
$cursor->setTypeMap($this->options['typeMap']);
}
......@@ -288,7 +300,7 @@ class Aggregate implements Executable
$cmd['bypassDocumentValidation'] = $this->options['bypassDocumentValidation'];
}
foreach (['comment', 'maxTimeMS'] as $option) {
foreach (['comment', 'explain', 'maxTimeMS'] as $option) {
if (isset($this->options[$option])) {
$cmd[$option] = $this->options[$option];
}
......@@ -323,7 +335,7 @@ class Aggregate implements Executable
* @param boolean $hasOutStage
* @return array
*/
private function createOptions($hasOutStage)
private function createOptions($hasOutStage, $hasExplain)
{
$options = [];
......@@ -339,7 +351,7 @@ class Aggregate implements Executable
$options['session'] = $this->options['session'];
}
if ($hasOutStage && isset($this->options['writeConcern'])) {
if ($hasOutStage && ! $hasExplain && isset($this->options['writeConcern'])) {
$options['writeConcern'] = $this->options['writeConcern'];
}
......
......@@ -3,6 +3,7 @@
namespace MongoDB\Tests\Operation;
use MongoDB\Driver\BulkWrite;
use MongoDB\Driver\WriteConcern;
use MongoDB\Operation\Aggregate;
use MongoDB\Tests\CommandObserver;
use ArrayIterator;
......@@ -131,6 +132,46 @@ class AggregateFunctionalTest extends FunctionalTestCase
$this->assertEquals($expectedDocuments, iterator_to_array($results));
}
public function testExplainOption()
{
$this->createFixtures(3);
$pipeline = [['$match' => ['_id' => ['$ne' => 2]]]];
$operation = new Aggregate($this->getDatabaseName(), $this->getCollectionName(), $pipeline, ['explain' => true, 'typeMap' => ['root' => 'array']]);
$results = iterator_to_array($operation->execute($this->getPrimaryServer()));
$this->assertCount(1, $results);
$this->assertArrayHasKey('stages', $results[0]);
}
public function testExplainOptionWithWriteConcern()
{
if (version_compare($this->getServerVersion(), '3.4.0', '<')) {
$this->markTestSkipped('The writeConcern option is not supported');
}
$this->createFixtures(3);
$pipeline = [['$match' => ['_id' => ['$ne' => 2]]], ['$out' => $this->getCollectionName() . '.output']];
$options = ['explain' => true, 'writeConcern' => new WriteConcern(1)];
(new CommandObserver)->observe(
function() use ($pipeline, $options) {
$operation = new Aggregate($this->getDatabaseName(), $this->getCollectionName(), $pipeline, $options);
$results = iterator_to_array($operation->execute($this->getPrimaryServer()));
$this->assertCount(1, $results);
$this->assertObjectHasAttribute('stages', current($results));
},
function(stdClass $command) {
$this->assertObjectNotHasAttribute('writeConcern', $command);
}
);
$this->assertCollectionCount($this->getCollectionName() . '.output', 0);
}
public function provideTypeMapOptionsAndExpectedDocuments()
{
return [
......
......@@ -52,6 +52,10 @@ class AggregateTest extends TestCase
$options[][] = ['hint' => $value];
}
foreach ($this->getInvalidBooleanValues() as $value) {
$options[][] = ['explain' => $value];
}
foreach ($this->getInvalidIntegerValues() as $value) {
$options[][] = ['maxAwaitTimeMS' => $value];
}
......
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