Skip to content

Commit f9493e7

Browse files
committed
Merge branch 'v1.5'
* v1.5: PHPLIB-485: Inherit collection's typeMap in distinct PHPLIB-481: Add functional test for pipeline-style bulk updates PHPLIB-481: Remove duplicated bulk write tests PHPLIB-481: Add the ability to specify a pipeline to an update within bulk write
2 parents 06a0000 + 32cfb9b commit f9493e7

File tree

6 files changed

+291
-68
lines changed

6 files changed

+291
-68
lines changed

src/Collection.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,10 @@ public function distinct($fieldName, $filter = [], array $options = [])
470470
$options['readPreference'] = $this->readPreference;
471471
}
472472

473+
if (! isset($options['typeMap'])) {
474+
$options['typeMap'] = $this->typeMap;
475+
}
476+
473477
$server = select_server($this->manager, $options);
474478

475479
if (! isset($options['readConcern']) && server_supports_feature($server, self::$wireVersionForReadConcern) && ! is_in_transaction($options)) {

src/Operation/BulkWrite.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
use function is_object;
3434
use function key;
3535
use function MongoDB\is_first_key_operator;
36+
use function MongoDB\is_pipeline;
3637
use function MongoDB\server_supports_feature;
3738
use function sprintf;
3839

@@ -255,8 +256,8 @@ public function __construct($databaseName, $collectionName, array $operations, a
255256
throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][1]', $i, $type), $args[1], 'array or object');
256257
}
257258

258-
if (! is_first_key_operator($args[1])) {
259-
throw new InvalidArgumentException(sprintf('First key in $operations[%d]["%s"][1] is not an update operator', $i, $type));
259+
if (! is_first_key_operator($args[1]) && ! is_pipeline($args[1])) {
260+
throw new InvalidArgumentException(sprintf('First key in $operations[%d]["%s"][1] is neither an update operator nor a pipeline', $i, $type));
260261
}
261262

262263
if (! isset($args[2])) {

tests/Collection/CollectionFunctionalTest.php

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@
1616
use MongoDB\Tests\CommandObserver;
1717
use function array_filter;
1818
use function call_user_func;
19+
use function is_scalar;
20+
use function json_encode;
1921
use function strchr;
22+
use function usort;
2023
use function version_compare;
2124

2225
/**
@@ -170,6 +173,88 @@ function (array $event) {
170173
);
171174
}
172175

176+
/**
177+
* @dataProvider provideTypeMapOptionsAndExpectedDocuments
178+
*/
179+
public function testDistinctWithTypeMap(array $typeMap, array $expectedDocuments)
180+
{
181+
$bulkWrite = new BulkWrite(['ordered' => true]);
182+
$bulkWrite->insert([
183+
'x' => (object) ['foo' => 'bar'],
184+
]);
185+
$bulkWrite->insert(['x' => 4]);
186+
$bulkWrite->insert([
187+
'x' => (object) ['foo' => ['foo' => 'bar']],
188+
]);
189+
$this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite);
190+
191+
$values = $this->collection->withOptions(['typeMap' => $typeMap])->distinct('x');
192+
193+
/* This sort callable sorts all scalars to the front of the list. All
194+
* non-scalar values are sorted by running json_encode on them and
195+
* comparing their string representations.
196+
*/
197+
$sort = function ($a, $b) {
198+
if (is_scalar($a) && ! is_scalar($b)) {
199+
return -1;
200+
}
201+
202+
if (! is_scalar($a)) {
203+
if (is_scalar($b)) {
204+
return 1;
205+
}
206+
207+
$a = json_encode($a);
208+
$b = json_encode($b);
209+
}
210+
211+
return $a < $b ? -1 : 1;
212+
};
213+
214+
usort($expectedDocuments, $sort);
215+
usort($values, $sort);
216+
217+
$this->assertEquals($expectedDocuments, $values);
218+
}
219+
220+
public function provideTypeMapOptionsAndExpectedDocuments()
221+
{
222+
return [
223+
'No type map' => [
224+
['root' => 'array', 'document' => 'array'],
225+
[
226+
['foo' => 'bar'],
227+
4,
228+
['foo' => ['foo' => 'bar']],
229+
],
230+
],
231+
'array/array' => [
232+
['root' => 'array', 'document' => 'array'],
233+
[
234+
['foo' => 'bar'],
235+
4,
236+
['foo' => ['foo' => 'bar']],
237+
],
238+
],
239+
'object/array' => [
240+
['root' => 'object', 'document' => 'array'],
241+
[
242+
(object) ['foo' => 'bar'],
243+
4,
244+
(object) ['foo' => ['foo' => 'bar']],
245+
],
246+
],
247+
'array/stdClass' => [
248+
['root' => 'array', 'document' => 'stdClass'],
249+
[
250+
['foo' => 'bar'],
251+
4,
252+
['foo' => (object) ['foo' => 'bar']],
253+
],
254+
],
255+
];
256+
}
257+
173258
public function testDrop()
174259
{
175260
$writeResult = $this->collection->insertOne(['x' => 1]);

tests/Operation/BulkWriteFunctionalTest.php

Lines changed: 30 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
use MongoDB\Driver\BulkWrite as Bulk;
99
use MongoDB\Driver\WriteConcern;
1010
use MongoDB\Exception\BadMethodCallException;
11-
use MongoDB\Exception\InvalidArgumentException;
1211
use MongoDB\Model\BSONDocument;
1312
use MongoDB\Operation\BulkWrite;
1413
use MongoDB\Tests\CommandObserver;
@@ -226,67 +225,6 @@ public function testUnacknowledgedWriteConcernAccessesUpsertedIds(BulkWriteResul
226225
$result->getUpsertedIds();
227226
}
228227

229-
public function testUnknownOperation()
230-
{
231-
$this->expectException(InvalidArgumentException::class);
232-
$this->expectExceptionMessage('Unknown operation type "foo" in $operations[0]');
233-
new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [
234-
['foo' => [['_id' => 1]]],
235-
]);
236-
}
237-
238-
/**
239-
* @dataProvider provideOpsWithMissingArguments
240-
*/
241-
public function testMissingArguments(array $ops)
242-
{
243-
$this->expectException(InvalidArgumentException::class);
244-
$this->expectExceptionMessageRegExp('/Missing (first|second) argument for \$operations\[\d+\]\["\w+\"]/');
245-
new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), $ops);
246-
}
247-
248-
public function provideOpsWithMissingArguments()
249-
{
250-
return [
251-
[[['insertOne' => []]]],
252-
[[['updateOne' => []]]],
253-
[[['updateOne' => [['_id' => 1]]]]],
254-
[[['updateMany' => []]]],
255-
[[['updateMany' => [['_id' => 1]]]]],
256-
[[['replaceOne' => []]]],
257-
[[['replaceOne' => [['_id' => 1]]]]],
258-
[[['deleteOne' => []]]],
259-
[[['deleteMany' => []]]],
260-
];
261-
}
262-
263-
public function testUpdateOneRequiresUpdateOperators()
264-
{
265-
$this->expectException(InvalidArgumentException::class);
266-
$this->expectExceptionMessage('First key in $operations[0]["updateOne"][1] is not an update operator');
267-
new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [
268-
['updateOne' => [['_id' => 1], ['x' => 1]]],
269-
]);
270-
}
271-
272-
public function testUpdateManyRequiresUpdateOperators()
273-
{
274-
$this->expectException(InvalidArgumentException::class);
275-
$this->expectExceptionMessage('First key in $operations[0]["updateMany"][1] is not an update operator');
276-
new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [
277-
['updateMany' => [['_id' => ['$gt' => 1]], ['x' => 1]]],
278-
]);
279-
}
280-
281-
public function testReplaceOneRequiresReplacementDocument()
282-
{
283-
$this->expectException(InvalidArgumentException::class);
284-
$this->expectExceptionMessage('First key in $operations[0]["replaceOne"][1] is an update operator');
285-
new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [
286-
['replaceOne' => [['_id' => 1], ['$inc' => ['x' => 1]]]],
287-
]);
288-
}
289-
290228
public function testSessionOption()
291229
{
292230
if (version_compare($this->getServerVersion(), '3.6.0', '<')) {
@@ -357,6 +295,36 @@ function (array $event) {
357295
);
358296
}
359297

298+
public function testBulkWriteWithPipelineUpdates()
299+
{
300+
if (version_compare($this->getServerVersion(), '4.2.0', '<')) {
301+
$this->markTestSkipped('Pipeline-style updates are not supported');
302+
}
303+
304+
$this->createFixtures(4);
305+
306+
$ops = [
307+
['updateOne' => [['_id' => 2], [['$addFields' => ['y' => 2]]]]],
308+
['updateMany' => [['_id' => ['$gt' => 2]], [['$addFields' => ['y' => '$_id']]]]],
309+
];
310+
311+
$operation = new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), $ops);
312+
$result = $operation->execute($this->getPrimaryServer());
313+
314+
$this->assertInstanceOf(BulkWriteResult::class, $result);
315+
$this->assertSame(3, $result->getMatchedCount());
316+
$this->assertSame(3, $result->getModifiedCount());
317+
318+
$expected = [
319+
['_id' => 1, 'x' => 11],
320+
['_id' => 2, 'x' => 22, 'y' => 2],
321+
['_id' => 3, 'x' => 33, 'y' => 3],
322+
['_id' => 4, 'x' => 44, 'y' => 4],
323+
];
324+
325+
$this->assertSameDocuments($expected, $this->collection->find());
326+
}
327+
360328
/**
361329
* Create data fixtures.
362330
*

tests/Operation/BulkWriteTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -258,10 +258,10 @@ public function testUpdateManyUpdateArgumentTypeCheck($update)
258258
]);
259259
}
260260

261-
public function testUpdateManyUpdateArgumentRequiresOperators()
261+
public function testUpdateManyUpdateArgumentRequiresOperatorsOrPipeline()
262262
{
263263
$this->expectException(InvalidArgumentException::class);
264-
$this->expectExceptionMessage('First key in $operations[0]["updateMany"][1] is not an update operator');
264+
$this->expectExceptionMessage('First key in $operations[0]["updateMany"][1] is neither an update operator nor a pipeline');
265265
new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [
266266
[BulkWrite::UPDATE_MANY => [['_id' => ['$gt' => 1]], ['x' => 1]]],
267267
]);
@@ -345,10 +345,10 @@ public function testUpdateOneUpdateArgumentTypeCheck($update)
345345
]);
346346
}
347347

348-
public function testUpdateOneUpdateArgumentRequiresOperators()
348+
public function testUpdateOneUpdateArgumentRequiresOperatorsOrPipeline()
349349
{
350350
$this->expectException(InvalidArgumentException::class);
351-
$this->expectExceptionMessage('First key in $operations[0]["updateOne"][1] is not an update operator');
351+
$this->expectExceptionMessage('First key in $operations[0]["updateOne"][1] is neither an update operator nor a pipeline');
352352
new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [
353353
[BulkWrite::UPDATE_ONE => [['_id' => 1], ['x' => 1]]],
354354
]);

0 commit comments

Comments
 (0)