Skip to content

Commit 3b6b96e

Browse files
committed
PHPLIB-112: Support typeMap option for non-cursor aggregate operations
1 parent c73dbf2 commit 3b6b96e

File tree

6 files changed

+59
-30
lines changed

6 files changed

+59
-30
lines changed

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

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,6 @@ source:
4949
source:
5050
file: apiargs-MongoDBCollection-common-option.yaml
5151
ref: typeMap
52-
post: |
53-
.. note::
54-
55-
This is not supported for inline aggregation results (i.e. ``useCursor``
56-
option is ``false`` or the server version is < 2.6).
5752
---
5853
arg_name: option
5954
name: useCursor

docs/reference/method/MongoDBCollection-aggregate.txt

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,6 @@ MongoDB server version and whether the ``useCursor`` option is specified. If
5858
``result`` array from the command response document. In both cases, the return
5959
value will be :php:`Traversable <traversable>`.
6060

61-
.. note::
62-
63-
BSON deserialization of inline aggregation results (i.e. not using a command
64-
cursor) does not yet support a ``typeMap`` option. Classes implementing
65-
:php:`MongoDB\\BSON\\Persistable <mongodb-bson-persistable>` will still be
66-
deserialized according to the
67-
:php:`Persistence <mongodb.persistence.deserialization>` specification.
68-
6961
.. todo: add examples
7062

7163
See Also

src/Collection.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,6 @@ public function __toString()
153153
* returned; otherwise, an ArrayIterator is returned, which wraps the
154154
* "result" array from the command response document.
155155
*
156-
* Note: BSON deserialization of inline aggregation results (i.e. not using
157-
* a command cursor) does not yet support a custom type map
158-
* (depends on: https://jira.mongodb.org/browse/PHPC-314).
159-
*
160156
* @see Aggregate::__construct() for supported options
161157
* @param array $pipeline List of pipeline operations
162158
* @param array $options Command options

src/Model/TypeMapArrayIterator.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
namespace MongoDB\Model;
4+
5+
use ArrayIterator;
6+
7+
/**
8+
* Iterator for applying a type map to documents in inline command results.
9+
*
10+
* This iterator may be used to apply a type map to an array of documents
11+
* returned by a database command (e.g. aggregate on servers < 2.6) and allows
12+
* for functional equivalence with commands that return their results via a
13+
* cursor (e.g. aggregate on servers >= 2.6).
14+
*
15+
* @internal
16+
*/
17+
class TypeMapArrayIterator extends ArrayIterator
18+
{
19+
private $typeMap;
20+
21+
/**
22+
* Constructor.
23+
*
24+
* @param array $documents
25+
* @param array $typeMap
26+
*/
27+
public function __construct(array $documents = [], array $typeMap)
28+
{
29+
parent::__construct($documents);
30+
31+
$this->typeMap = $typeMap;
32+
}
33+
34+
/**
35+
* Return the current element with the type map applied to it.
36+
*
37+
* @see http://php.net/arrayiterator.current
38+
* @return array|object
39+
*/
40+
public function current()
41+
{
42+
return \MongoDB\apply_type_map_to_document(parent::current(), $this->typeMap);
43+
}
44+
}

src/Operation/Aggregate.php

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use MongoDB\Exception\InvalidArgumentException;
1212
use MongoDB\Exception\UnexpectedValueException;
1313
use MongoDB\Exception\UnsupportedException;
14+
use MongoDB\Model\TypeMapArrayIterator;
1415
use ArrayIterator;
1516
use stdClass;
1617
use Traversable;
@@ -72,9 +73,6 @@ class Aggregate implements Executable
7273
* * typeMap (array): Type map for BSON deserialization. This will be
7374
* applied to the returned Cursor (it is not sent to the server).
7475
*
75-
* This is not supported for inline aggregation results (i.e. useCursor
76-
* option is false or the server version is < 2.6).
77-
*
7876
* * useCursor (boolean): Indicates whether the command will request that
7977
* the server provide results using a cursor. The default is true.
8078
*
@@ -206,9 +204,6 @@ public function execute(Server $server)
206204
$cursor = $server->executeCommand($this->databaseName, $command, $readPreference);
207205

208206
if ($isCursorSupported && $this->options['useCursor']) {
209-
/* The type map can only be applied to command cursors until
210-
* https://jira.mongodb.org/browse/PHPC-314 is implemented.
211-
*/
212207
if (isset($this->options['typeMap'])) {
213208
$cursor->setTypeMap($this->options['typeMap']);
214209
}
@@ -222,6 +217,10 @@ public function execute(Server $server)
222217
throw new UnexpectedValueException('aggregate command did not return a "result" array');
223218
}
224219

220+
if (isset($this->options['typeMap'])) {
221+
return new TypeMapArrayIterator($result->result, $this->options['typeMap']);
222+
}
223+
225224
return new ArrayIterator($result->result);
226225
}
227226

tests/Operation/AggregateFunctionalTest.php

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,27 @@ public function testUnrecognizedPipelineState()
2121
/**
2222
* @dataProvider provideTypeMapOptionsAndExpectedDocument
2323
*/
24-
public function testTypeMapOption(array $typeMap, array $expectedDocuments)
24+
public function testTypeMapOption(array $typeMap = null, array $expectedDocuments)
2525
{
26-
if ( ! \MongoDB\server_supports_feature($this->getPrimaryServer(), self::$wireVersionForCursor)) {
27-
$this->markTestSkipped('Command cursor is not supported');
28-
}
29-
3026
$this->createFixtures(3);
3127

3228
$pipeline = [['$match' => ['_id' => ['$ne' => 2]]]];
3329
$operation = new Aggregate($this->getDatabaseName(), $this->getCollectionName(), $pipeline, ['typeMap' => $typeMap]);
34-
$cursor = $operation->execute($this->getPrimaryServer());
30+
$results = iterator_to_array($operation->execute($this->getPrimaryServer()));
3531

36-
$this->assertEquals($expectedDocuments, $cursor->toArray());
32+
$this->assertEquals($expectedDocuments, $results);
3733
}
3834

3935
public function provideTypeMapOptionsAndExpectedDocument()
4036
{
4137
return [
38+
[
39+
null,
40+
[
41+
(object) ['_id' => 1, 'x' => (object) ['foo' => 'bar']],
42+
(object) ['_id' => 3, 'x' => (object) ['foo' => 'bar']],
43+
],
44+
],
4245
[
4346
['root' => 'array', 'document' => 'array'],
4447
[

0 commit comments

Comments
 (0)