Skip to content

Commit 5aff53f

Browse files
authored
PHPLIB-1192: Remove useCursor option in aggregate (#1130)
* PHPLIB-1192: Remove useCursor option in aggregate * Remove unnecessary property for isExplain
1 parent f81b4a6 commit 5aff53f

File tree

8 files changed

+13
-105
lines changed

8 files changed

+13
-105
lines changed

docs/includes/apiargs-MongoDBCollection-method-aggregate-option.yaml

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -59,22 +59,6 @@ source:
5959
file: apiargs-MongoDBCollection-common-option.yaml
6060
ref: typeMap
6161
---
62-
arg_name: option
63-
name: useCursor
64-
type: boolean
65-
description: |
66-
Indicates whether the command will request that the server provide results
67-
using a cursor. The default is ``true``.
68-
69-
.. note::
70-
71-
MongoDB 3.6+ no longer supports returning results without a cursor (excluding
72-
when the explain option is used) and specifying false for this option will
73-
result in a server error.
74-
interface: phpmethod
75-
operation: ~
76-
optional: true
77-
---
7862
source:
7963
file: apiargs-MongoDBCollection-common-option.yaml
8064
ref: writeConcern

docs/reference/method/MongoDBCollection-aggregate.txt

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,8 @@ Errors/Exceptions
5050
Behavior
5151
--------
5252

53-
:phpmethod:`MongoDB\\Collection::aggregate()`'s return value depends on the
54-
MongoDB server version and whether the ``useCursor`` option is specified. If
55-
``useCursor`` is ``true``, a :php:`MongoDB\\Driver\\Cursor
56-
<class.mongodb-driver-cursor>` object is returned. If ``useCursor`` is
57-
``false``, an :php:`ArrayIterator <arrayiterator>` is returned that wraps the
58-
``result`` array from the command response document. In both cases, the return
59-
value will be :php:`Traversable <traversable>`.
53+
:phpmethod:`MongoDB\\Collection::aggregate()`'s returns a
54+
:php:`MongoDB\\Driver\\Cursor <class.mongodb-driver-cursor>` object.
6055

6156
Examples
6257
--------

src/Collection.php

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@
5959
use MongoDB\Operation\UpdateMany;
6060
use MongoDB\Operation\UpdateOne;
6161
use MongoDB\Operation\Watch;
62-
use Traversable;
6362

6463
use function array_diff_key;
6564
use function array_intersect_key;
@@ -186,15 +185,10 @@ public function __toString()
186185
/**
187186
* Executes an aggregation framework pipeline on the collection.
188187
*
189-
* Note: this method's return value depends on the MongoDB server version
190-
* and the "useCursor" option. If "useCursor" is true, a Cursor will be
191-
* returned; otherwise, an ArrayIterator is returned, which wraps the
192-
* "result" array from the command response document.
193-
*
194188
* @see Aggregate::__construct() for supported options
195189
* @param array $pipeline Aggregation pipeline
196190
* @param array $options Command options
197-
* @return Traversable
191+
* @return Cursor
198192
* @throws UnexpectedValueException if the command response was malformed
199193
* @throws UnsupportedException if options are not supported by the selected server
200194
* @throws InvalidArgumentException for parameter/option parsing errors

src/Operation/Aggregate.php

Lines changed: 7 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
namespace MongoDB\Operation;
1919

20-
use ArrayIterator;
2120
use MongoDB\Driver\Command;
2221
use MongoDB\Driver\Cursor;
2322
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
@@ -31,13 +30,11 @@
3130
use MongoDB\Exception\UnsupportedException;
3231
use stdClass;
3332

34-
use function current;
3533
use function is_array;
3634
use function is_bool;
3735
use function is_integer;
3836
use function is_object;
3937
use function is_string;
40-
use function MongoDB\create_field_path_type_map;
4138
use function MongoDB\is_document;
4239
use function MongoDB\is_last_pipeline_operator_write;
4340
use function MongoDB\is_pipeline;
@@ -58,8 +55,6 @@ class Aggregate implements Executable, Explainable
5855

5956
private array $options;
6057

61-
private bool $isExplain;
62-
6358
private bool $isWrite;
6459

6560
/**
@@ -112,12 +107,6 @@ class Aggregate implements Executable, Explainable
112107
* * typeMap (array): Type map for BSON deserialization. This will be
113108
* applied to the returned Cursor (it is not sent to the server).
114109
*
115-
* * useCursor (boolean): Indicates whether the command will request that
116-
* the server provide results using a cursor. The default is true.
117-
*
118-
* This option allows users to turn off cursors if necessary to aid in
119-
* mongod/mongos upgrades.
120-
*
121110
* * writeConcern (MongoDB\Driver\WriteConcern): Write concern. This only
122111
* applies when an $out or $merge stage is specified.
123112
*
@@ -136,8 +125,6 @@ public function __construct(string $databaseName, ?string $collectionName, array
136125
throw new InvalidArgumentException('$pipeline is not a valid aggregation pipeline');
137126
}
138127

139-
$options += ['useCursor' => true];
140-
141128
if (isset($options['allowDiskUse']) && ! is_bool($options['allowDiskUse'])) {
142129
throw InvalidArgumentException::invalidType('"allowDiskUse" option', $options['allowDiskUse'], 'boolean');
143130
}
@@ -190,18 +177,10 @@ public function __construct(string $databaseName, ?string $collectionName, array
190177
throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array');
191178
}
192179

193-
if (! is_bool($options['useCursor'])) {
194-
throw InvalidArgumentException::invalidType('"useCursor" option', $options['useCursor'], 'boolean');
195-
}
196-
197180
if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
198181
throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class);
199182
}
200183

201-
if (isset($options['batchSize']) && ! $options['useCursor']) {
202-
throw new InvalidArgumentException('"batchSize" option should not be used if "useCursor" is false');
203-
}
204-
205184
if (isset($options['bypassDocumentValidation']) && ! $options['bypassDocumentValidation']) {
206185
unset($options['bypassDocumentValidation']);
207186
}
@@ -214,14 +193,7 @@ public function __construct(string $databaseName, ?string $collectionName, array
214193
unset($options['writeConcern']);
215194
}
216195

217-
$this->isExplain = ! empty($options['explain']);
218-
$this->isWrite = is_last_pipeline_operator_write($pipeline) && ! $this->isExplain;
219-
220-
// Explain does not use a cursor
221-
if ($this->isExplain) {
222-
$options['useCursor'] = false;
223-
unset($options['batchSize']);
224-
}
196+
$this->isWrite = is_last_pipeline_operator_write($pipeline) && ! ($options['explain'] ?? false);
225197

226198
if ($this->isWrite) {
227199
/* Ignore batchSize for writes, since no documents are returned and
@@ -241,7 +213,7 @@ public function __construct(string $databaseName, ?string $collectionName, array
241213
* Execute the operation.
242214
*
243215
* @see Executable::execute()
244-
* @return ArrayIterator|Cursor
216+
* @return Cursor
245217
* @throws UnexpectedValueException if the command response was malformed
246218
* @throws UnsupportedException if read concern or write concern is used and unsupported
247219
* @throws DriverRuntimeException for other driver errors (e.g. connection errors)
@@ -266,25 +238,11 @@ public function execute(Server $server)
266238

267239
$cursor = $this->executeCommand($server, $command);
268240

269-
if ($this->options['useCursor'] || $this->isExplain) {
270-
if (isset($this->options['typeMap'])) {
271-
$cursor->setTypeMap($this->options['typeMap']);
272-
}
273-
274-
return $cursor;
275-
}
276-
277241
if (isset($this->options['typeMap'])) {
278-
$cursor->setTypeMap(create_field_path_type_map($this->options['typeMap'], 'result.$'));
242+
$cursor->setTypeMap($this->options['typeMap']);
279243
}
280244

281-
$result = current($cursor->toArray());
282-
283-
if (! is_object($result) || ! isset($result->result) || ! is_array($result->result)) {
284-
throw new UnexpectedValueException('aggregate command did not return a "result" array');
285-
}
286-
287-
return new ArrayIterator($result->result);
245+
return $cursor;
288246
}
289247

290248
/**
@@ -331,11 +289,9 @@ private function createCommandDocument(): array
331289
$cmd['hint'] = is_array($this->options['hint']) ? (object) $this->options['hint'] : $this->options['hint'];
332290
}
333291

334-
if ($this->options['useCursor']) {
335-
$cmd['cursor'] = isset($this->options["batchSize"])
336-
? ['batchSize' => $this->options["batchSize"]]
337-
: new stdClass();
338-
}
292+
$cmd['cursor'] = isset($this->options['batchSize'])
293+
? ['batchSize' => $this->options['batchSize']]
294+
: new stdClass();
339295

340296
return $cmd;
341297
}

src/Operation/CountDocuments.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,13 @@
1717

1818
namespace MongoDB\Operation;
1919

20-
use MongoDB\Driver\Cursor;
2120
use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException;
2221
use MongoDB\Driver\Server;
2322
use MongoDB\Exception\InvalidArgumentException;
2423
use MongoDB\Exception\UnexpectedValueException;
2524
use MongoDB\Exception\UnsupportedException;
2625

2726
use function array_intersect_key;
28-
use function assert;
2927
use function count;
3028
use function current;
3129
use function is_float;
@@ -125,7 +123,6 @@ public function __construct(string $databaseName, string $collectionName, $filte
125123
public function execute(Server $server)
126124
{
127125
$cursor = $this->aggregate->execute($server);
128-
assert($cursor instanceof Cursor);
129126

130127
$allResults = $cursor->toArray();
131128

src/Operation/Watch.php

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
use function array_intersect_key;
3737
use function array_key_exists;
3838
use function array_unshift;
39-
use function assert;
4039
use function count;
4140
use function is_array;
4241
use function is_bool;
@@ -355,10 +354,7 @@ private function executeAggregate(Server $server): Cursor
355354
addSubscriber($this);
356355

357356
try {
358-
$cursor = $this->aggregate->execute($server);
359-
assert($cursor instanceof Cursor);
360-
361-
return $cursor;
357+
return $this->aggregate->execute($server);
362358
} finally {
363359
removeSubscriber($this);
364360
}

tests/Operation/AggregateTest.php

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,23 +40,10 @@ public function provideInvalidConstructorOptions()
4040
'readPreference' => $this->getInvalidReadPreferenceValues(),
4141
'session' => $this->getInvalidSessionValues(),
4242
'typeMap' => $this->getInvalidArrayValues(),
43-
'useCursor' => $this->getInvalidBooleanValues(true),
4443
'writeConcern' => $this->getInvalidWriteConcernValues(),
4544
]);
4645
}
4746

48-
public function testConstructorBatchSizeOptionRequiresUseCursor(): void
49-
{
50-
$this->expectException(InvalidArgumentException::class);
51-
$this->expectExceptionMessage('"batchSize" option should not be used if "useCursor" is false');
52-
new Aggregate(
53-
$this->getDatabaseName(),
54-
$this->getCollectionName(),
55-
[['$match' => ['x' => 1]]],
56-
['batchSize' => 100, 'useCursor' => false],
57-
);
58-
}
59-
6047
public function testExplainableCommandDocument(): void
6148
{
6249
$options = [
@@ -69,7 +56,6 @@ public function testExplainableCommandDocument(): void
6956
'let' => ['a' => 1],
7057
'maxTimeMS' => 100,
7158
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
72-
'useCursor' => true,
7359
// Intentionally omitted options
7460
// The "explain" option is illegal
7561
'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED),

tests/UnifiedSpecTests/Util.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ final class Util
7272
'rewrapManyDataKey' => ['filter', 'opts'],
7373
],
7474
Database::class => [
75-
'aggregate' => ['pipeline', 'session', 'useCursor', 'allowDiskUse', 'batchSize', 'bypassDocumentValidation', 'collation', 'comment', 'explain', 'hint', 'let', 'maxAwaitTimeMS', 'maxTimeMS'],
75+
'aggregate' => ['pipeline', 'session', 'allowDiskUse', 'batchSize', 'bypassDocumentValidation', 'collation', 'comment', 'explain', 'hint', 'let', 'maxAwaitTimeMS', 'maxTimeMS'],
7676
'createChangeStream' => ['pipeline', 'session', 'fullDocument', 'resumeAfter', 'startAfter', 'startAtOperationTime', 'batchSize', 'collation', 'maxAwaitTimeMS', 'showExpandedEvents'],
7777
'createCollection' => ['collection', 'session', 'autoIndexId', 'capped', 'changeStreamPreAndPostImages', 'clusteredIndex', 'collation', 'expireAfterSeconds', 'flags', 'indexOptionDefaults', 'max', 'maxTimeMS', 'pipeline', 'size', 'storageEngine', 'timeseries', 'validationAction', 'validationLevel', 'validator', 'viewOn'],
7878
'dropCollection' => ['collection', 'session'],
@@ -83,7 +83,7 @@ final class Util
8383
'runCommand' => ['command', 'commandName', 'readPreference', 'session'],
8484
],
8585
Collection::class => [
86-
'aggregate' => ['pipeline', 'session', 'useCursor', 'allowDiskUse', 'batchSize', 'bypassDocumentValidation', 'collation', 'comment', 'explain', 'hint', 'let', 'maxAwaitTimeMS', 'maxTimeMS'],
86+
'aggregate' => ['pipeline', 'session', 'allowDiskUse', 'batchSize', 'bypassDocumentValidation', 'collation', 'comment', 'explain', 'hint', 'let', 'maxAwaitTimeMS', 'maxTimeMS'],
8787
'bulkWrite' => ['let', 'requests', 'session', 'ordered', 'bypassDocumentValidation', 'comment'],
8888
'createChangeStream' => ['pipeline', 'session', 'fullDocument', 'fullDocumentBeforeChange', 'resumeAfter', 'startAfter', 'startAtOperationTime', 'batchSize', 'collation', 'maxAwaitTimeMS', 'comment', 'showExpandedEvents'],
8989
'createFindCursor' => ['filter', 'session', 'allowDiskUse', 'allowPartialResults', 'batchSize', 'collation', 'comment', 'cursorType', 'hint', 'limit', 'max', 'maxAwaitTimeMS', 'maxScan', 'maxTimeMS', 'min', 'modifiers', 'noCursorTimeout', 'oplogReplay', 'projection', 'returnKey', 'showRecordId', 'skip', 'snapshot', 'sort'],

0 commit comments

Comments
 (0)