Commit 0ad32023 authored by Katherine Walker's avatar Katherine Walker

PHPLIB-156: Create methods for explain on Find and Count

parent 966511c8
...@@ -34,7 +34,7 @@ use MongoDB\Exception\UnsupportedException; ...@@ -34,7 +34,7 @@ use MongoDB\Exception\UnsupportedException;
* @see \MongoDB\Collection::count() * @see \MongoDB\Collection::count()
* @see http://docs.mongodb.org/manual/reference/command/count/ * @see http://docs.mongodb.org/manual/reference/command/count/
*/ */
class Count implements Executable class Count implements Explainable
{ {
private static $wireVersionForCollation = 5; private static $wireVersionForCollation = 5;
private static $wireVersionForReadConcern = 4; private static $wireVersionForReadConcern = 4;
...@@ -162,6 +162,39 @@ class Count implements Executable ...@@ -162,6 +162,39 @@ class Count implements Executable
return (integer) $result->n; return (integer) $result->n;
} }
/**
* Explain the operation.
*
* @see Explainable::explain()
* @param Server $server
* @param $command
* @param array $options
* @return array|object
* @throws DriverRuntimeException for other driver errors (e.g. connection errors)
*/
public function explain($server, $command, $options = [])
{
if ( ! isset($options['verbosity'])) {
$options['verbosity'] = 'allPlansExecution';
}
$cmd = new Command(['explain' => ['count' => $this->collectionName, 'query' => $command], 'verbosity' => $options['verbosity']]);
if (empty($command)) {
$cmd = new Command(['explain' => ['count' => $this->collectionName], 'verbosity' => $options['verbosity']]);
}
$result = $server->executeCommand($this->databaseName, $cmd);
$resultArray = get_object_vars($result->toArray()[0]); // cast $result to array
if ($options['verbosity'] === 'queryPlanner') {
return ['queryPlanner' => $resultArray['queryPlanner']];
}
return ['queryPlanner' => $resultArray['queryPlanner'], 'executionStats' => $resultArray['executionStats']];
}
/** /**
* Create the count command. * Create the count command.
* *
......
<?php
/*
* Copyright 2018 MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace MongoDB\Operation;
use MongoDB\Driver\Server;
/**
* Explainable interface for explainable operations (count, distinct, group,
* find, findAndModify, delete, and update).
*
* @internal
*/
interface Explainable extends Executable
{
/**
* Explain the operation.
*
* @param $command
* @param array $options
* @return mixed
*/
public function explain($server, $command, $options = []);
}
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
namespace MongoDB\Operation; namespace MongoDB\Operation;
use MongoDB\Driver\Command;
use MongoDB\Driver\Cursor; use MongoDB\Driver\Cursor;
use MongoDB\Driver\Query; use MongoDB\Driver\Query;
use MongoDB\Driver\ReadConcern; use MongoDB\Driver\ReadConcern;
...@@ -35,7 +36,7 @@ use MongoDB\Exception\UnsupportedException; ...@@ -35,7 +36,7 @@ use MongoDB\Exception\UnsupportedException;
* @see http://docs.mongodb.org/manual/tutorial/query-documents/ * @see http://docs.mongodb.org/manual/tutorial/query-documents/
* @see http://docs.mongodb.org/manual/reference/operator/query-modifier/ * @see http://docs.mongodb.org/manual/reference/operator/query-modifier/
*/ */
class Find implements Executable class Find implements Explainable
{ {
const NON_TAILABLE = 1; const NON_TAILABLE = 1;
const TAILABLE = 2; const TAILABLE = 2;
...@@ -293,6 +294,38 @@ class Find implements Executable ...@@ -293,6 +294,38 @@ class Find implements Executable
return $cursor; return $cursor;
} }
/**
* Explain the operation.
*
* @see Explainable::explain()
* @param Server $server
* @param $command
* @param array $options
* @return array|object
* @throws DriverRuntimeException for other driver errors (e.g. connection errors)
*/
public function explain($server, $command, $options = [])
{
if ( ! isset($options['verbosity'])) {
$options['verbosity'] = 'allPlansExecution';
}
$cmd = new Command(['explain' => ['find' => $this->collectionName, 'query' => $command], 'verbosity' => $options['verbosity']]);
if (empty($command)) {
$cmd = new Command(['explain' => ['find' => $this->collectionName], 'verbosity' => $options['verbosity']]);
}
$result = $server->executeCommand($this->databaseName, $cmd);
$resultArray = get_object_vars($result->toArray()[0]);
if ($options['verbosity'] === 'queryPlanner') {
return ['queryPlanner' => $resultArray['queryPlanner']];
}
return ['queryPlanner' => $resultArray['queryPlanner'], 'executionStats' => $resultArray['executionStats']];
}
/** /**
* Create options for executing the command. * Create options for executing the command.
* *
......
...@@ -29,6 +29,56 @@ class CountFunctionalTest extends FunctionalTestCase ...@@ -29,6 +29,56 @@ class CountFunctionalTest extends FunctionalTestCase
); );
} }
public function testExplainAllPlansExecution()
{
$insertMany = new InsertMany($this->getDatabaseName(), $this->getCollectionName(), [
['x' => 0],
['x' => 1],
['x' => 2],
['y' => 3]
]);
$insertMany->execute($this->getPrimaryServer());
$operation = new Count($this->getDatabaseName(), $this->getCollectionName(), [], []);
$result = $operation->explain($this->getPrimaryServer(), ['x' => ['$gte' => 1]]);
$this->assertSame(['queryPlanner', 'executionStats'], array_keys($result));
$this->assertTrue(array_key_exists('allPlansExecution', $result['executionStats']));
}
public function testExplainExecutionStats()
{
$insertMany = new InsertMany($this->getDatabaseName(), $this->getCollectionName(), [
['x' => 0],
['x' => 1],
['x' => 2],
['y' => 3]
]);
$insertMany->execute($this->getPrimaryServer());
$operation = new Count($this->getDatabaseName(), $this->getCollectionName(), [], []);
$result = $operation->explain($this->getPrimaryServer(), ['x' => ['$gte' => 1]], ['verbosity' => 'executionStats']);
$this->assertSame(['queryPlanner', 'executionStats'], array_keys($result));
$this->assertFalse(array_key_exists('allPlansExecution', $result['executionStats']));
}
public function testExplainQueryPlanner()
{
$insertMany = new InsertMany($this->getDatabaseName(), $this->getCollectionName(), [
['x' => 0],
['x' => 1],
['x' => 2],
['y' => 3]
]);
$insertMany->execute($this->getPrimaryServer());
$operation = new Count($this->getDatabaseName(), $this->getCollectionName(), [], []);
$result = $operation->explain($this->getPrimaryServer(), ['x' => ['$gte' => 1]], ['verbosity' => 'queryPlanner']);
$this->assertSame(['queryPlanner'], array_keys($result));
}
public function testHintOption() public function testHintOption()
{ {
$insertMany = new InsertMany($this->getDatabaseName(), $this->getCollectionName(), [ $insertMany = new InsertMany($this->getDatabaseName(), $this->getCollectionName(), [
......
...@@ -217,6 +217,41 @@ class FindFunctionalTest extends FunctionalTestCase ...@@ -217,6 +217,41 @@ class FindFunctionalTest extends FunctionalTestCase
$this->assertFalse($it->valid()); $this->assertFalse($it->valid());
} }
public function testExplainAllPlansExecution()
{
$this->createFixtures(3);
$operation = new Find($this->getDatabaseName(), $this->getCollectionName(), [], []);
$command = [];
$result = $operation->explain($this->getPrimaryServer(), $command);
$this->assertSame(['queryPlanner', 'executionStats'], array_keys($result));
$this->assertTrue(array_key_exists('allPlansExecution', $result['executionStats']));
}
public function testExplainExecutionStats()
{
$this->createFixtures(3);
$operation = new Find($this->getDatabaseName(), $this->getCollectionName(), [], []);
$command = [];
$result = $operation->explain($this->getPrimaryServer(), $command, ['verbosity' => 'executionStats']);
$this->assertSame(['queryPlanner', 'executionStats'], array_keys($result));
$this->assertFalse(array_key_exists('allPlansExecution', $result['executionStats']));
}
public function testExplainQueryPlanner()
{
$this->createFixtures(3);
$operation = new Find($this->getDatabaseName(), $this->getCollectionName(), [], []);
$command = [];
$result = $operation->explain($this->getPrimaryServer(), $command, ['verbosity' => 'queryPlanner']);
$this->assertSame(['queryPlanner'], array_keys($result));
}
/** /**
* Create data fixtures. * Create data fixtures.
* *
......
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