Skip to content

Commit 0ad3202

Browse files
committed
PHPLIB-156: Create methods for explain on Find and Count
1 parent 966511c commit 0ad3202

File tree

5 files changed

+191
-2
lines changed

5 files changed

+191
-2
lines changed

src/Operation/Count.php

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
* @see \MongoDB\Collection::count()
3535
* @see http://docs.mongodb.org/manual/reference/command/count/
3636
*/
37-
class Count implements Executable
37+
class Count implements Explainable
3838
{
3939
private static $wireVersionForCollation = 5;
4040
private static $wireVersionForReadConcern = 4;
@@ -162,6 +162,39 @@ public function execute(Server $server)
162162
return (integer) $result->n;
163163
}
164164

165+
/**
166+
* Explain the operation.
167+
*
168+
* @see Explainable::explain()
169+
* @param Server $server
170+
* @param $command
171+
* @param array $options
172+
* @return array|object
173+
* @throws DriverRuntimeException for other driver errors (e.g. connection errors)
174+
*/
175+
public function explain($server, $command, $options = [])
176+
{
177+
if ( ! isset($options['verbosity'])) {
178+
$options['verbosity'] = 'allPlansExecution';
179+
}
180+
181+
$cmd = new Command(['explain' => ['count' => $this->collectionName, 'query' => $command], 'verbosity' => $options['verbosity']]);
182+
183+
if (empty($command)) {
184+
$cmd = new Command(['explain' => ['count' => $this->collectionName], 'verbosity' => $options['verbosity']]);
185+
}
186+
187+
$result = $server->executeCommand($this->databaseName, $cmd);
188+
189+
$resultArray = get_object_vars($result->toArray()[0]); // cast $result to array
190+
191+
if ($options['verbosity'] === 'queryPlanner') {
192+
return ['queryPlanner' => $resultArray['queryPlanner']];
193+
}
194+
195+
return ['queryPlanner' => $resultArray['queryPlanner'], 'executionStats' => $resultArray['executionStats']];
196+
}
197+
165198
/**
166199
* Create the count command.
167200
*

src/Operation/Explainable.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
/*
3+
* Copyright 2018 MongoDB, Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
namespace MongoDB\Operation;
19+
20+
use MongoDB\Driver\Server;
21+
22+
/**
23+
* Explainable interface for explainable operations (count, distinct, group,
24+
* find, findAndModify, delete, and update).
25+
*
26+
* @internal
27+
*/
28+
interface Explainable extends Executable
29+
{
30+
/**
31+
* Explain the operation.
32+
*
33+
* @param $command
34+
* @param array $options
35+
* @return mixed
36+
*/
37+
public function explain($server, $command, $options = []);
38+
}

src/Operation/Find.php

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
namespace MongoDB\Operation;
1919

20+
use MongoDB\Driver\Command;
2021
use MongoDB\Driver\Cursor;
2122
use MongoDB\Driver\Query;
2223
use MongoDB\Driver\ReadConcern;
@@ -35,7 +36,7 @@
3536
* @see http://docs.mongodb.org/manual/tutorial/query-documents/
3637
* @see http://docs.mongodb.org/manual/reference/operator/query-modifier/
3738
*/
38-
class Find implements Executable
39+
class Find implements Explainable
3940
{
4041
const NON_TAILABLE = 1;
4142
const TAILABLE = 2;
@@ -293,6 +294,38 @@ public function execute(Server $server)
293294
return $cursor;
294295
}
295296

297+
/**
298+
* Explain the operation.
299+
*
300+
* @see Explainable::explain()
301+
* @param Server $server
302+
* @param $command
303+
* @param array $options
304+
* @return array|object
305+
* @throws DriverRuntimeException for other driver errors (e.g. connection errors)
306+
*/
307+
public function explain($server, $command, $options = [])
308+
{
309+
if ( ! isset($options['verbosity'])) {
310+
$options['verbosity'] = 'allPlansExecution';
311+
}
312+
313+
$cmd = new Command(['explain' => ['find' => $this->collectionName, 'query' => $command], 'verbosity' => $options['verbosity']]);
314+
315+
if (empty($command)) {
316+
$cmd = new Command(['explain' => ['find' => $this->collectionName], 'verbosity' => $options['verbosity']]);
317+
}
318+
319+
$result = $server->executeCommand($this->databaseName, $cmd);
320+
$resultArray = get_object_vars($result->toArray()[0]);
321+
322+
if ($options['verbosity'] === 'queryPlanner') {
323+
return ['queryPlanner' => $resultArray['queryPlanner']];
324+
}
325+
326+
return ['queryPlanner' => $resultArray['queryPlanner'], 'executionStats' => $resultArray['executionStats']];
327+
}
328+
296329
/**
297330
* Create options for executing the command.
298331
*

tests/Operation/CountFunctionalTest.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,56 @@ function(stdClass $command) {
2929
);
3030
}
3131

32+
public function testExplainAllPlansExecution()
33+
{
34+
$insertMany = new InsertMany($this->getDatabaseName(), $this->getCollectionName(), [
35+
['x' => 0],
36+
['x' => 1],
37+
['x' => 2],
38+
['y' => 3]
39+
]);
40+
$insertMany->execute($this->getPrimaryServer());
41+
42+
$operation = new Count($this->getDatabaseName(), $this->getCollectionName(), [], []);
43+
$result = $operation->explain($this->getPrimaryServer(), ['x' => ['$gte' => 1]]);
44+
45+
$this->assertSame(['queryPlanner', 'executionStats'], array_keys($result));
46+
$this->assertTrue(array_key_exists('allPlansExecution', $result['executionStats']));
47+
}
48+
49+
public function testExplainExecutionStats()
50+
{
51+
$insertMany = new InsertMany($this->getDatabaseName(), $this->getCollectionName(), [
52+
['x' => 0],
53+
['x' => 1],
54+
['x' => 2],
55+
['y' => 3]
56+
]);
57+
$insertMany->execute($this->getPrimaryServer());
58+
59+
$operation = new Count($this->getDatabaseName(), $this->getCollectionName(), [], []);
60+
$result = $operation->explain($this->getPrimaryServer(), ['x' => ['$gte' => 1]], ['verbosity' => 'executionStats']);
61+
62+
$this->assertSame(['queryPlanner', 'executionStats'], array_keys($result));
63+
$this->assertFalse(array_key_exists('allPlansExecution', $result['executionStats']));
64+
}
65+
66+
public function testExplainQueryPlanner()
67+
{
68+
$insertMany = new InsertMany($this->getDatabaseName(), $this->getCollectionName(), [
69+
['x' => 0],
70+
['x' => 1],
71+
['x' => 2],
72+
['y' => 3]
73+
]);
74+
$insertMany->execute($this->getPrimaryServer());
75+
76+
$operation = new Count($this->getDatabaseName(), $this->getCollectionName(), [], []);
77+
$result = $operation->explain($this->getPrimaryServer(), ['x' => ['$gte' => 1]], ['verbosity' => 'queryPlanner']);
78+
79+
$this->assertSame(['queryPlanner'], array_keys($result));
80+
}
81+
3282
public function testHintOption()
3383
{
3484
$insertMany = new InsertMany($this->getDatabaseName(), $this->getCollectionName(), [

tests/Operation/FindFunctionalTest.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,41 @@ public function testMaxAwaitTimeMS()
217217
$this->assertFalse($it->valid());
218218
}
219219

220+
public function testExplainAllPlansExecution()
221+
{
222+
$this->createFixtures(3);
223+
224+
$operation = new Find($this->getDatabaseName(), $this->getCollectionName(), [], []);
225+
$command = [];
226+
$result = $operation->explain($this->getPrimaryServer(), $command);
227+
228+
$this->assertSame(['queryPlanner', 'executionStats'], array_keys($result));
229+
$this->assertTrue(array_key_exists('allPlansExecution', $result['executionStats']));
230+
}
231+
232+
public function testExplainExecutionStats()
233+
{
234+
$this->createFixtures(3);
235+
236+
$operation = new Find($this->getDatabaseName(), $this->getCollectionName(), [], []);
237+
$command = [];
238+
$result = $operation->explain($this->getPrimaryServer(), $command, ['verbosity' => 'executionStats']);
239+
240+
$this->assertSame(['queryPlanner', 'executionStats'], array_keys($result));
241+
$this->assertFalse(array_key_exists('allPlansExecution', $result['executionStats']));
242+
}
243+
244+
public function testExplainQueryPlanner()
245+
{
246+
$this->createFixtures(3);
247+
248+
$operation = new Find($this->getDatabaseName(), $this->getCollectionName(), [], []);
249+
$command = [];
250+
$result = $operation->explain($this->getPrimaryServer(), $command, ['verbosity' => 'queryPlanner']);
251+
252+
$this->assertSame(['queryPlanner'], array_keys($result));
253+
}
254+
220255
/**
221256
* Create data fixtures.
222257
*

0 commit comments

Comments
 (0)