Skip to content

Commit b8c159d

Browse files
committed
PHPLIB-242: Add typeMap support for distinct
1 parent 3c9064a commit b8c159d

File tree

4 files changed

+107
-0
lines changed

4 files changed

+107
-0
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,10 @@ source:
1919
ref: session
2020
post: |
2121
.. versionadded:: 1.3
22+
---
23+
source:
24+
file: apiargs-MongoDBCollection-common-option.yaml
25+
ref: typeMap
26+
post: |
27+
.. versionadded:: 1.5
2228
...

src/Operation/Distinct.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ class Distinct implements Executable, Explainable
6969
*
7070
* Sessions are not supported for server versions < 3.6.
7171
*
72+
* * typeMap (array): Type map for BSON deserialization.
73+
*
7274
* @param string $databaseName Database name
7375
* @param string $collectionName Collection name
7476
* @param string $fieldName Field for which to return distinct values
@@ -102,6 +104,10 @@ public function __construct($databaseName, $collectionName, $fieldName, $filter
102104
throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class);
103105
}
104106

107+
if (isset($options['typeMap']) && ! is_array($options['typeMap'])) {
108+
throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array');
109+
}
110+
105111
if (isset($options['readConcern']) && $options['readConcern']->isDefault()) {
106112
unset($options['readConcern']);
107113
}
@@ -139,6 +145,11 @@ public function execute(Server $server)
139145
}
140146

141147
$cursor = $server->executeReadCommand($this->databaseName, new Command($this->createCommandDocument()), $this->createOptions());
148+
149+
if (isset($this->options['typeMap'])) {
150+
$cursor->setTypeMap(\MongoDB\create_field_path_type_map($this->options['typeMap'], 'values.$'));
151+
}
152+
142153
$result = current($cursor->toArray());
143154

144155
if ( ! isset($result->values) || ! is_array($result->values)) {

tests/Operation/DistinctFunctionalTest.php

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace MongoDB\Tests\Operation;
44

5+
use MongoDB\Driver\BulkWrite;
56
use MongoDB\Operation\Distinct;
67
use MongoDB\Tests\CommandObserver;
78
use stdClass;
@@ -51,4 +52,89 @@ function(array $event) {
5152
}
5253
);
5354
}
55+
56+
/**
57+
* @dataProvider provideTypeMapOptionsAndExpectedDocuments
58+
*/
59+
public function testTypeMapOption(array $typeMap, array $expectedDocuments)
60+
{
61+
$bulkWrite = new BulkWrite(['ordered' => true]);
62+
$bulkWrite->insert([
63+
'x' => (object) ['foo' => 'bar'],
64+
]);
65+
$bulkWrite->insert([
66+
'x' => 4,
67+
]);
68+
$bulkWrite->insert([
69+
'x' => (object) ['foo' => ['foo' => 'bar']],
70+
]);
71+
$this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite);
72+
73+
$distinct = new Distinct($this->getDatabaseName(), $this->getCollectionName(), 'x', [], ['typeMap' => $typeMap]);
74+
$values = $distinct->execute($this->getPrimaryServer());
75+
76+
/* This sort callable sorts all scalars to the front of the list. All
77+
* non-scalar values are sorted by running json_encode on them and
78+
* comparing their string representations.
79+
*/
80+
$sort = function ($a, $b) {
81+
if (is_scalar($a) && ! is_scalar($b)) {
82+
return -1;
83+
}
84+
85+
if (! is_scalar($a)) {
86+
if (is_scalar($b)) {
87+
return 1;
88+
}
89+
90+
$a = json_encode($a);
91+
$b = json_encode($b);
92+
}
93+
94+
return $a < $b ? -1 : 1;
95+
};
96+
97+
usort($expectedDocuments, $sort);
98+
usort($values, $sort);
99+
100+
$this->assertEquals($expectedDocuments, $values);
101+
}
102+
103+
public function provideTypeMapOptionsAndExpectedDocuments()
104+
{
105+
return [
106+
'No type map' => [
107+
['root' => 'array', 'document' => 'array'],
108+
[
109+
['foo' => 'bar'],
110+
4,
111+
['foo' => ['foo' => 'bar']],
112+
],
113+
],
114+
'array/array' => [
115+
['root' => 'array', 'document' => 'array'],
116+
[
117+
['foo' => 'bar'],
118+
4,
119+
['foo' => ['foo' => 'bar']],
120+
],
121+
],
122+
'object/array' => [
123+
['root' => 'object', 'document' => 'array'],
124+
[
125+
(object) ['foo' => 'bar'],
126+
4,
127+
(object) ['foo' => ['foo' => 'bar']],
128+
],
129+
],
130+
'array/stdClass' => [
131+
['root' => 'array', 'document' => 'stdClass'],
132+
[
133+
['foo' => 'bar'],
134+
4,
135+
['foo' => (object) ['foo' => 'bar']],
136+
],
137+
],
138+
];
139+
}
54140
}

tests/Operation/DistinctTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ public function provideInvalidConstructorOptions()
4949
$options[][] = ['session' => $value];
5050
}
5151

52+
foreach ($this->getInvalidArrayValues() as $value) {
53+
$options[][] = ['typeMap' => $value];
54+
}
55+
5256
return $options;
5357
}
5458
}

0 commit comments

Comments
 (0)