Commit d56f01d4 authored by Katherine Walker's avatar Katherine Walker

Merge pull request #440

parents 0496705c 11a843eb
arg_name: option arg_name: option
name: arrayFilters
type: array
description: |
An array of filter documents that determines which array elements to modify
for an update operation on an array field.
.. versionadded:: 1.3
interface: phpmethod
operation: ~
optional: true
---
arg_name: option
name: bypassDocumentValidation name: bypassDocumentValidation
type: boolean type: boolean
description: | description: |
......
...@@ -6,6 +6,10 @@ source: ...@@ -6,6 +6,10 @@ source:
file: apiargs-MongoDBCollection-method-find-option.yaml file: apiargs-MongoDBCollection-method-find-option.yaml
ref: sort ref: sort
--- ---
source:
file: apiargs-MongoDBCollection-common-option.yaml
ref: arrayFilters
---
source: source:
file: apiargs-MongoDBCollection-common-option.yaml file: apiargs-MongoDBCollection-common-option.yaml
ref: collation ref: collation
......
...@@ -2,6 +2,10 @@ source: ...@@ -2,6 +2,10 @@ source:
file: apiargs-MongoDBCollection-common-option.yaml file: apiargs-MongoDBCollection-common-option.yaml
ref: upsert ref: upsert
--- ---
source:
file: apiargs-MongoDBCollection-common-option.yaml
ref: arrayFilters
---
source: source:
file: apiargs-MongoDBCollection-common-option.yaml file: apiargs-MongoDBCollection-common-option.yaml
ref: bypassDocumentValidation ref: bypassDocumentValidation
......
...@@ -20,6 +20,16 @@ namespace MongoDB\Exception; ...@@ -20,6 +20,16 @@ namespace MongoDB\Exception;
class UnsupportedException extends RuntimeException class UnsupportedException extends RuntimeException
{ {
/** /**
* Thrown when array filters are not supported by a server.
*
* @return self
*/
public static function arrayFiltersNotSupported()
{
return new static('Array filters are not supported by the server executing this operation');
}
/**
* Thrown when collations are not supported by a server. * Thrown when collations are not supported by a server.
* *
* @return self * @return self
......
...@@ -40,6 +40,7 @@ class BulkWrite implements Executable ...@@ -40,6 +40,7 @@ class BulkWrite implements Executable
const UPDATE_MANY = 'updateMany'; const UPDATE_MANY = 'updateMany';
const UPDATE_ONE = 'updateOne'; const UPDATE_ONE = 'updateOne';
private static $wireVersionForArrayFilters = 6;
private static $wireVersionForCollation = 5; private static $wireVersionForCollation = 5;
private static $wireVersionForDocumentLevelValidation = 4; private static $wireVersionForDocumentLevelValidation = 4;
...@@ -47,6 +48,7 @@ class BulkWrite implements Executable ...@@ -47,6 +48,7 @@ class BulkWrite implements Executable
private $collectionName; private $collectionName;
private $operations; private $operations;
private $options; private $options;
private $isArrayFiltersUsed = false;
private $isCollationUsed = false; private $isCollationUsed = false;
/** /**
...@@ -84,6 +86,14 @@ class BulkWrite implements Executable ...@@ -84,6 +86,14 @@ class BulkWrite implements Executable
* * upsert (boolean): When true, a new document is created if no document * * upsert (boolean): When true, a new document is created if no document
* matches the query. The default is false. * matches the query. The default is false.
* *
* Supported options for updateMany and updateOne operations:
*
* * arrayFilters (document array): A set of filters specifying to which
* array elements an update should apply.
*
* This is not supported for server versions < 3.6 and will result in an
* exception at execution time if used.
*
* Supported options for the bulk write operation: * Supported options for the bulk write operation:
* *
* * bypassDocumentValidation (boolean): If true, allows the write to * * bypassDocumentValidation (boolean): If true, allows the write to
...@@ -229,6 +239,14 @@ class BulkWrite implements Executable ...@@ -229,6 +239,14 @@ class BulkWrite implements Executable
$args[2]['multi'] = ($type === self::UPDATE_MANY); $args[2]['multi'] = ($type === self::UPDATE_MANY);
$args[2] += ['upsert' => false]; $args[2] += ['upsert' => false];
if (isset($args[2]['arrayFilters'])) {
$this->isArrayFiltersUsed = true;
if ( ! is_array($args[2]['arrayFilters'])) {
throw InvalidArgumentException::InvalidType(sprintf('$operations[%d]["%s"][2]["arrayFilters"]', $i, $type), $args[2]['arrayFilters'], 'array');
}
}
if (isset($args[2]['collation'])) { if (isset($args[2]['collation'])) {
$this->isCollationUsed = true; $this->isCollationUsed = true;
...@@ -282,11 +300,15 @@ class BulkWrite implements Executable ...@@ -282,11 +300,15 @@ class BulkWrite implements Executable
* @see Executable::execute() * @see Executable::execute()
* @param Server $server * @param Server $server
* @return BulkWriteResult * @return BulkWriteResult
* @throws UnsupportedException if collation is used and unsupported * @throws UnsupportedException if array filters or collation is used and unsupported
* @throws DriverRuntimeException for other driver errors (e.g. connection errors) * @throws DriverRuntimeException for other driver errors (e.g. connection errors)
*/ */
public function execute(Server $server) public function execute(Server $server)
{ {
if ($this->isArrayFiltersUsed && ! \MongoDB\server_supports_feature($server, self::$wireVersionForArrayFilters)) {
throw UnsupportedException::arrayFiltersNotSupported();
}
if ($this->isCollationUsed && ! \MongoDB\server_supports_feature($server, self::$wireVersionForCollation)) { if ($this->isCollationUsed && ! \MongoDB\server_supports_feature($server, self::$wireVersionForCollation)) {
throw UnsupportedException::collationNotSupported(); throw UnsupportedException::collationNotSupported();
} }
......
...@@ -36,6 +36,7 @@ use MongoDB\Exception\UnsupportedException; ...@@ -36,6 +36,7 @@ use MongoDB\Exception\UnsupportedException;
*/ */
class FindAndModify implements Executable class FindAndModify implements Executable
{ {
private static $wireVersionForArrayFilters = 6;
private static $wireVersionForCollation = 5; private static $wireVersionForCollation = 5;
private static $wireVersionForDocumentLevelValidation = 4; private static $wireVersionForDocumentLevelValidation = 4;
private static $wireVersionForWriteConcern = 4; private static $wireVersionForWriteConcern = 4;
...@@ -49,6 +50,12 @@ class FindAndModify implements Executable ...@@ -49,6 +50,12 @@ class FindAndModify implements Executable
* *
* Supported options: * Supported options:
* *
* * arrayFilters (document array): A set of filters specifying to which
* array elements an update should apply.
*
* This is not supported for server versions < 3.6 and will result in an
* exception at execution time if used.
*
* * collation (document): Collation specification. * * collation (document): Collation specification.
* *
* This is not supported for server versions < 3.4 and will result in an * This is not supported for server versions < 3.4 and will result in an
...@@ -105,6 +112,10 @@ class FindAndModify implements Executable ...@@ -105,6 +112,10 @@ class FindAndModify implements Executable
'upsert' => false, 'upsert' => false,
]; ];
if (isset($options['arrayFilters']) && ! is_array($options['arrayFilters'])) {
throw InvalidArgumentException::invalidType('"arrayFilters" option', $options['arrayFilters'], 'array');
}
if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) { if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) {
throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean'); throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean');
} }
...@@ -173,11 +184,15 @@ class FindAndModify implements Executable ...@@ -173,11 +184,15 @@ class FindAndModify implements Executable
* @param Server $server * @param Server $server
* @return array|object|null * @return array|object|null
* @throws UnexpectedValueException if the command response was malformed * @throws UnexpectedValueException if the command response was malformed
* @throws UnsupportedException if collation or write concern is used and unsupported * @throws UnsupportedException if array filters, collation, or write concern is used and unsupported
* @throws DriverRuntimeException for other driver errors (e.g. connection errors) * @throws DriverRuntimeException for other driver errors (e.g. connection errors)
*/ */
public function execute(Server $server) public function execute(Server $server)
{ {
if (isset($this->options['arrayFilters']) && ! \MongoDB\server_supports_feature($server, self::$wireVersionForArrayFilters)) {
throw UnsupportedException::arrayFiltersNotSupported();
}
if (isset($this->options['collation']) && ! \MongoDB\server_supports_feature($server, self::$wireVersionForCollation)) { if (isset($this->options['collation']) && ! \MongoDB\server_supports_feature($server, self::$wireVersionForCollation)) {
throw UnsupportedException::collationNotSupported(); throw UnsupportedException::collationNotSupported();
} }
...@@ -238,6 +253,10 @@ class FindAndModify implements Executable ...@@ -238,6 +253,10 @@ class FindAndModify implements Executable
} }
} }
if (isset($this->options['arrayFilters'])) {
$cmd['arrayFilters'] = $this->options['arrayFilters'];
}
if (isset($this->options['maxTimeMS'])) { if (isset($this->options['maxTimeMS'])) {
$cmd['maxTimeMS'] = $this->options['maxTimeMS']; $cmd['maxTimeMS'] = $this->options['maxTimeMS'];
} }
......
...@@ -41,6 +41,9 @@ class FindOneAndUpdate implements Executable ...@@ -41,6 +41,9 @@ class FindOneAndUpdate implements Executable
* *
* Supported options: * Supported options:
* *
* * arrayFilters (document array): A set of filters specifying to which
* array elements an update should apply.
*
* * bypassDocumentValidation (boolean): If true, allows the write to * * bypassDocumentValidation (boolean): If true, allows the write to
* circumvent document level validation. * circumvent document level validation.
* *
......
...@@ -36,6 +36,7 @@ use MongoDB\Exception\UnsupportedException; ...@@ -36,6 +36,7 @@ use MongoDB\Exception\UnsupportedException;
*/ */
class Update implements Executable class Update implements Executable
{ {
private static $wireVersionForArrayFilters = 6;
private static $wireVersionForCollation = 5; private static $wireVersionForCollation = 5;
private static $wireVersionForDocumentLevelValidation = 4; private static $wireVersionForDocumentLevelValidation = 4;
...@@ -50,6 +51,12 @@ class Update implements Executable ...@@ -50,6 +51,12 @@ class Update implements Executable
* *
* Supported options: * Supported options:
* *
* * arrayFilters (document array): A set of filters specifying to which
* array elements an update should apply.
*
* This is not supported for server versions < 3.6 and will result in an
* exception at execution time if used.
*
* * bypassDocumentValidation (boolean): If true, allows the write to * * bypassDocumentValidation (boolean): If true, allows the write to
* circumvent document level validation. * circumvent document level validation.
* *
...@@ -93,6 +100,10 @@ class Update implements Executable ...@@ -93,6 +100,10 @@ class Update implements Executable
'upsert' => false, 'upsert' => false,
]; ];
if (isset($options['arrayFilters']) && ! is_array($options['arrayFilters'])) {
throw InvalidArgumentException::invalidType('"arrayFilters" option', $options['arrayFilters'], 'array');
}
if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) { if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) {
throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean'); throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean');
} }
...@@ -134,11 +145,15 @@ class Update implements Executable ...@@ -134,11 +145,15 @@ class Update implements Executable
* @see Executable::execute() * @see Executable::execute()
* @param Server $server * @param Server $server
* @return UpdateResult * @return UpdateResult
* @throws UnsupportedException if collation is used and unsupported * @throws UnsupportedException if array filters or collation is used and unsupported
* @throws DriverRuntimeException for other driver errors (e.g. connection errors) * @throws DriverRuntimeException for other driver errors (e.g. connection errors)
*/ */
public function execute(Server $server) public function execute(Server $server)
{ {
if (isset($this->options['arrayFilters']) && ! \MongoDB\server_supports_feature($server, self::$wireVersionForArrayFilters)) {
throw UnsupportedException::arrayFiltersNotSupported();
}
if (isset($this->options['collation']) && ! \MongoDB\server_supports_feature($server, self::$wireVersionForCollation)) { if (isset($this->options['collation']) && ! \MongoDB\server_supports_feature($server, self::$wireVersionForCollation)) {
throw UnsupportedException::collationNotSupported(); throw UnsupportedException::collationNotSupported();
} }
...@@ -148,6 +163,10 @@ class Update implements Executable ...@@ -148,6 +163,10 @@ class Update implements Executable
'upsert' => $this->options['upsert'], 'upsert' => $this->options['upsert'],
]; ];
if (isset($this->options['arrayFilters'])) {
$updateOptions['arrayFilters'] = $this->options['arrayFilters'];
}
if (isset($this->options['collation'])) { if (isset($this->options['collation'])) {
$updateOptions['collation'] = (object) $this->options['collation']; $updateOptions['collation'] = (object) $this->options['collation'];
} }
......
...@@ -39,6 +39,12 @@ class UpdateMany implements Executable ...@@ -39,6 +39,12 @@ class UpdateMany implements Executable
* *
* Supported options: * Supported options:
* *
* * arrayFilters (document array): A set of filters specifying to which
* array elements an update should apply.
*
* This is not supported for server versions < 3.6 and will result in an$
* exception at execution time if used.
*
* * bypassDocumentValidation (boolean): If true, allows the write to * * bypassDocumentValidation (boolean): If true, allows the write to
* circumvent document level validation. * circumvent document level validation.
* *
......
...@@ -39,6 +39,12 @@ class UpdateOne implements Executable ...@@ -39,6 +39,12 @@ class UpdateOne implements Executable
* *
* Supported options: * Supported options:
* *
* * arrayFilters (document array): A set of filters specifying to which
* array elements an update should apply.
*
* This is not supported for server versions < 3.6 and will result in an$
* exception at execution time if used.
*
* * bypassDocumentValidation (boolean): If true, allows the write to * * bypassDocumentValidation (boolean): If true, allows the write to
* circumvent document level validation. * circumvent document level validation.
* *
......
...@@ -115,6 +115,13 @@ class CrudSpecFunctionalTest extends FunctionalTestCase ...@@ -115,6 +115,13 @@ class CrudSpecFunctionalTest extends FunctionalTestCase
array_diff_key($operation['arguments'], ['pipeline' => 1]) array_diff_key($operation['arguments'], ['pipeline' => 1])
); );
case 'bulkWrite':
$results = $this->prepareBulkWriteArguments($operation['arguments']);
return $this->collection->bulkWrite(
array_diff_key($results, ['options' => 1]),
$results['options']
);
case 'count': case 'count':
case 'find': case 'find':
return $this->collection->{$operation['name']}( return $this->collection->{$operation['name']}(
...@@ -224,6 +231,38 @@ class CrudSpecFunctionalTest extends FunctionalTestCase ...@@ -224,6 +231,38 @@ class CrudSpecFunctionalTest extends FunctionalTestCase
} }
break; break;
case 'bulkWrite':
if (isset($expectedResult['deletedCount'])) {
$this->assertSame($expectedResult['deletedCount'], $actualResult->getDeletedCount());
}
if (isset($expectedResult['insertedIds'])) {
$this->assertSameDocument(
['insertedIds' => $expectedResult['insertedIds']],
['insertedIds' => $actualResult->getInsertedIds()]
);
}
if (isset($expectedResult['matchedCount'])) {
$this->assertSame($expectedResult['matchedCount'], $actualResult->getMatchedCount());
}
if (isset($expectedResult['modifiedCount'])) {
$this->assertSame($expectedResult['modifiedCount'], $actualResult->getModifiedCount());
}
if (isset($expectedResult['upsertedCount'])) {
$this->assertSame($expectedResult['upsertedCount'], $actualResult->getUpsertedCount());
}
if (array_key_exists('upsertedId', $expectedResult)) {
$this->assertSameDocument(
['upsertedId' => $expectedResult['upsertedId']],
['upsertedId' => $actualResult->getUpsertedId()]
);
}
break;
case 'count': case 'count':
$this->assertSame($expectedResult, $actualResult); $this->assertSame($expectedResult, $actualResult);
break; break;
...@@ -334,6 +373,38 @@ class CrudSpecFunctionalTest extends FunctionalTestCase ...@@ -334,6 +373,38 @@ class CrudSpecFunctionalTest extends FunctionalTestCase
} }
} }
private function prepareBulkWriteArguments($arguments)
{
$operations = [];
$operations['options'] = $arguments['options'];
foreach ($arguments['requests'] as $request) {
$innerArray = [];
switch ($request['name']) {
case 'deleteMany':
case 'deleteOne':
$options = array_diff_key($request['arguments'], ['filter' => 1]);
$innerArray = [$request['arguments']['filter'], $options];
break;
case 'insertOne':
$innerArray = [$request['arguments']['document']];
break;
case 'replaceOne':
$options = array_diff_key($request['arguments'], ['filter' => 1, 'replacement' => 1]);
$innerArray = [$request['arguments']['filter'], $request['arguments']['replacement'], $options];
break;
case 'updateMany':
case 'updateOne':
$options = array_diff_key($request['arguments'], ['filter' => 1, 'update' => 1]);
$innerArray = [$request['arguments']['filter'], $request['arguments']['update'], $options];
break;
default:
throw new LogicException('Unsupported bulk write request: ' . $request['name']);
}
$operations[] = [$request['name'] => $innerArray];
}
return $operations;
}
/** /**
* Prepares arguments for findOneAndReplace and findOneAndUpdate operations. * Prepares arguments for findOneAndReplace and findOneAndUpdate operations.
* *
......
...@@ -35,4 +35,4 @@ ...@@ -35,4 +35,4 @@
} }
} }
] ]
} }
\ No newline at end of file
...@@ -65,6 +65,57 @@ ...@@ -65,6 +65,57 @@
] ]
} }
} }
},
{
"description": "Aggregate with $out and batch size of 0",
"operation": {
"name": "aggregate",
"arguments": {
"pipeline": [
{
"$sort": {
"x": 1
}
},
{
"$match": {
"_id": {
"$gt": 1
}
}
},
{
"$out": "other_test_collection"
}
],
"batchSize": 0
}
},
"outcome": {
"result": [
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
],
"collection": {
"name": "other_test_collection",
"data": [
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
]
}
}
} }
] ]
} }
\ No newline at end of file
...@@ -50,4 +50,4 @@ ...@@ -50,4 +50,4 @@
} }
} }
] ]
} }
\ No newline at end of file
...@@ -26,4 +26,4 @@ ...@@ -26,4 +26,4 @@
} }
} }
] ]
} }
\ No newline at end of file
...@@ -57,4 +57,4 @@ ...@@ -57,4 +57,4 @@
} }
} }
] ]
} }
\ No newline at end of file
...@@ -30,4 +30,4 @@ ...@@ -30,4 +30,4 @@
} }
} }
] ]
} }
\ No newline at end of file
...@@ -52,4 +52,4 @@ ...@@ -52,4 +52,4 @@
} }
} }
] ]
} }
\ No newline at end of file
...@@ -31,4 +31,4 @@ ...@@ -31,4 +31,4 @@
} }
} }
] ]
} }
\ No newline at end of file
...@@ -102,4 +102,4 @@ ...@@ -102,4 +102,4 @@
} }
} }
] ]
} }
\ No newline at end of file
{
"data": [
{
"_id": 1,
"y": [
{
"b": 3
},
{
"b": 1
}
]
},
{
"_id": 2,
"y": [
{
"b": 0
},
{
"b": 1
}
]
}
],
"minServerVersion": "3.5.6",
"tests": [
{
"description": "BulkWrite with arrayFilters",
"operation": {
"arguments": {
"options": {
"ordered": true
},
"requests": [
{
"arguments": {
"arrayFilters": [
{
"i.b": 3
}
],
"filter": {},
"update": {
"$set": {
"y.$[i].b": 2
}
}
},
"name": "updateOne"
},
{
"arguments": {
"arrayFilters": [
{
"i.b": 1
}
],
"filter": {},
"update": {
"$set": {
"y.$[i].b": 2
}
}
},
"name": "updateMany"
}
]
},
"name": "bulkWrite"
},
"outcome": {
"collection": {
"data": [
{
"_id": 1,
"y": [
{
"b": 2
},
{
"b": 2
}
]
},
{
"_id": 2,
"y": [
{
"b": 0
},
{
"b": 2
}
]
}
]
},
"result": {
"deletedCount": 0,
"insertedIds": {},
"matchedCount": 3,
"modifiedCount": 3,
"upsertedCount": 0,
"upsertedIds": {}
}
}
}
]
}
\ No newline at end of file
...@@ -44,4 +44,4 @@ ...@@ -44,4 +44,4 @@
} }
} }
] ]
} }
\ No newline at end of file
...@@ -73,4 +73,4 @@ ...@@ -73,4 +73,4 @@
} }
} }
] ]
} }
\ No newline at end of file
...@@ -48,4 +48,4 @@ ...@@ -48,4 +48,4 @@
} }
} }
] ]
} }
\ No newline at end of file
...@@ -93,4 +93,4 @@ ...@@ -93,4 +93,4 @@
} }
} }
] ]
} }
\ No newline at end of file
...@@ -56,4 +56,4 @@ ...@@ -56,4 +56,4 @@
} }
} }
] ]
} }
\ No newline at end of file
...@@ -124,4 +124,4 @@ ...@@ -124,4 +124,4 @@
} }
} }
] ]
} }
\ No newline at end of file
...@@ -55,4 +55,4 @@ ...@@ -55,4 +55,4 @@
} }
} }
] ]
} }
\ No newline at end of file
...@@ -198,4 +198,4 @@ ...@@ -198,4 +198,4 @@
} }
} }
] ]
} }
\ No newline at end of file
{
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
],
"maxServerVersion": "2.4.99",
"tests": [
{
"description": "FindOneAndReplace when no documents match without id specified with upsert returning the document before modification",
"operation": {
"name": "findOneAndReplace",
"arguments": {
"filter": {
"_id": 4
},
"replacement": {
"x": 44
},
"projection": {
"x": 1,
"_id": 0
},
"upsert": true
}
},
"outcome": {
"result": null
}
},
{
"description": "FindOneAndReplace when no documents match without id specified with upsert returning the document after modification",
"operation": {
"name": "findOneAndReplace",
"arguments": {
"filter": {
"_id": 4
},
"replacement": {
"x": 44
},
"projection": {
"x": 1,
"_id": 0
},
"returnDocument": "After",
"sort": {
"x": 1
},
"upsert": true
}
},
"outcome": {
"result": {
"x": 44
}
}
},
{
"description": "FindOneAndReplace when no documents match with id specified with upsert returning the document before modification",
"operation": {
"name": "findOneAndReplace",
"arguments": {
"filter": {
"_id": 4
},
"replacement": {
"_id": 4,
"x": 44
},
"projection": {
"x": 1,
"_id": 0
},
"upsert": true
}
},
"outcome": {
"result": null,
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
},
{
"_id": 4,
"x": 44
}
]
}
}
},
{
"description": "FindOneAndReplace when no documents match with id specified with upsert returning the document after modification",
"operation": {
"name": "findOneAndReplace",
"arguments": {
"filter": {
"_id": 4
},
"replacement": {
"_id": 4,
"x": 44
},
"projection": {
"x": 1,
"_id": 0
},
"returnDocument": "After",
"sort": {
"x": 1
},
"upsert": true
}
},
"outcome": {
"result": {
"x": 44
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
},
{
"_id": 4,
"x": 44
}
]
}
}
}
]
}
\ No newline at end of file
...@@ -270,4 +270,4 @@ ...@@ -270,4 +270,4 @@
} }
} }
] ]
} }
\ No newline at end of file
...@@ -2,162 +2,202 @@ ...@@ -2,162 +2,202 @@
"data": [ "data": [
{ {
"_id": 1, "_id": 1,
"x": 11 "y": [
{
"b": 3
},
{
"b": 1
}
]
}, },
{ {
"_id": 2, "_id": 2,
"x": 22 "y": [
}, {
{ "b": 0
"_id": 3, },
"x": 33 {
"b": 1
}
]
} }
], ],
"maxServerVersion": "2.4.99", "minServerVersion": "3.5.6",
"tests": [ "tests": [
{ {
"description": "UpdateOne when many documents match", "description": "FindOneAndUpdate when no document matches arrayFilters",
"operation": { "operation": {
"name": "updateOne", "name": "findOneAndUpdate",
"arguments": { "arguments": {
"filter": { "filter": {},
"_id": {
"$gt": 1
}
},
"update": { "update": {
"$inc": { "$set": {
"x": 1 "y.$[i].b": 2
} }
}
}
},
"outcome": {
"result": {
"matchedCount": 1,
"upsertedCount": 0
}
}
},
{
"description": "UpdateOne when one document matches",
"operation": {
"name": "updateOne",
"arguments": {
"filter": {
"_id": 1
}, },
"update": { "arrayFilters": [
"$inc": { {
"x": 1 "i.b": 4
} }
} ]
} }
}, },
"outcome": { "outcome": {
"result": { "result": {
"matchedCount": 1, "_id": 1,
"upsertedCount": 0 "y": [
{
"b": 3
},
{
"b": 1
}
]
}, },
"collection": { "collection": {
"data": [ "data": [
{ {
"_id": 1, "_id": 1,
"x": 12 "y": [
{
"b": 3
},
{
"b": 1
}
]
}, },
{ {
"_id": 2, "_id": 2,
"x": 22 "y": [
}, {
{ "b": 0
"_id": 3, },
"x": 33 {
"b": 1
}
]
} }
] ]
} }
} }
}, },
{ {
"description": "UpdateOne when no documents match", "description": "FindOneAndUpdate when one document matches arrayFilters",
"operation": { "operation": {
"name": "updateOne", "name": "findOneAndUpdate",
"arguments": { "arguments": {
"filter": { "filter": {},
"_id": 4
},
"update": { "update": {
"$inc": { "$set": {
"x": 1 "y.$[i].b": 2
} }
} },
"arrayFilters": [
{
"i.b": 3
}
]
} }
}, },
"outcome": { "outcome": {
"result": { "result": {
"matchedCount": 0, "_id": 1,
"upsertedCount": 0 "y": [
{
"b": 3
},
{
"b": 1
}
]
}, },
"collection": { "collection": {
"data": [ "data": [
{ {
"_id": 1, "_id": 1,
"x": 11 "y": [
{
"b": 2
},
{
"b": 1
}
]
}, },
{ {
"_id": 2, "_id": 2,
"x": 22 "y": [
}, {
{ "b": 0
"_id": 3, },
"x": 33 {
"b": 1
}
]
} }
] ]
} }
} }
}, },
{ {
"description": "UpdateOne with upsert when no documents match", "description": "FindOneAndUpdate when multiple documents match arrayFilters",
"operation": { "operation": {
"name": "updateOne", "name": "findOneAndUpdate",
"arguments": { "arguments": {
"filter": { "filter": {},
"_id": 4
},
"update": { "update": {
"$inc": { "$set": {
"x": 1 "y.$[i].b": 2
} }
}, },
"upsert": true "arrayFilters": [
{
"i.b": 1
}
]
} }
}, },
"outcome": { "outcome": {
"result": { "result": {
"matchedCount": 0, "_id": 1,
"upsertedCount": 1, "y": [
"upsertedId": 4 {
"b": 3
},
{
"b": 1
}
]
}, },
"collection": { "collection": {
"data": [ "data": [
{ {
"_id": 1, "_id": 1,
"x": 11 "y": [
{
"b": 3
},
{
"b": 2
}
]
}, },
{ {
"_id": 2, "_id": 2,
"x": 22 "y": [
}, {
{ "b": 0
"_id": 3, },
"x": 33 {
}, "b": 1
{ }
"_id": 4, ]
"x": 1
} }
] ]
} }
} }
} }
] ]
} }
\ No newline at end of file
...@@ -64,4 +64,4 @@ ...@@ -64,4 +64,4 @@
} }
} }
] ]
} }
\ No newline at end of file
...@@ -376,4 +376,4 @@ ...@@ -376,4 +376,4 @@
} }
} }
] ]
} }
\ No newline at end of file
...@@ -49,4 +49,4 @@ ...@@ -49,4 +49,4 @@
} }
} }
] ]
} }
\ No newline at end of file
...@@ -36,4 +36,4 @@ ...@@ -36,4 +36,4 @@
} }
} }
] ]
} }
\ No newline at end of file
...@@ -50,4 +50,4 @@ ...@@ -50,4 +50,4 @@
} }
} }
] ]
} }
\ No newline at end of file
{
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
],
"maxServerVersion": "2.4.99",
"tests": [
{
"description": "ReplaceOne when many documents match",
"operation": {
"name": "replaceOne",
"arguments": {
"filter": {
"_id": {
"$gt": 1
}
},
"replacement": {
"x": 111
}
}
},
"outcome": {
"result": {
"matchedCount": 1,
"upsertedCount": 0
}
}
},
{
"description": "ReplaceOne when one document matches",
"operation": {
"name": "replaceOne",
"arguments": {
"filter": {
"_id": 1
},
"replacement": {
"_id": 1,
"x": 111
}
}
},
"outcome": {
"result": {
"matchedCount": 1,
"upsertedCount": 0
},
"collection": {
"data": [
{
"_id": 1,
"x": 111
},
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
]
}
}
},
{
"description": "ReplaceOne when no documents match",
"operation": {
"name": "replaceOne",
"arguments": {
"filter": {
"_id": 4
},
"replacement": {
"_id": 4,
"x": 1
}
}
},
"outcome": {
"result": {
"matchedCount": 0,
"upsertedCount": 0
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
]
}
}
},
{
"description": "ReplaceOne with upsert when no documents match without an id specified",
"operation": {
"name": "replaceOne",
"arguments": {
"filter": {
"_id": 4
},
"replacement": {
"x": 1
},
"upsert": true
}
},
"outcome": {
"result": {
"matchedCount": 0,
"upsertedCount": 1
}
}
},
{
"description": "ReplaceOne with upsert when no documents match with an id specified",
"operation": {
"name": "replaceOne",
"arguments": {
"filter": {
"_id": 4
},
"replacement": {
"_id": 4,
"x": 1
},
"upsert": true
}
},
"outcome": {
"result": {
"matchedCount": 0,
"upsertedCount": 1,
"upsertedId": 4
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
},
{
"_id": 4,
"x": 1
}
]
}
}
}
]
}
\ No newline at end of file
{
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
],
"minServerVersion": "2.6",
"tests": [
{
"description": "ReplaceOne with upsert when no documents match without an id specified",
"operation": {
"arguments": {
"filter": {
"_id": 4
},
"replacement": {
"x": 1
},
"upsert": true
},
"name": "replaceOne"
},
"outcome": {
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
},
{
"_id": 4,
"x": 1
}
]
},
"result": {
"matchedCount": 0,
"modifiedCount": 0,
"upsertedId": 4
}
}
},
{
"description": "ReplaceOne with upsert when no documents match with an id specified",
"operation": {
"arguments": {
"filter": {
"_id": 4
},
"replacement": {
"_id": 4,
"x": 1
},
"upsert": true
},
"name": "replaceOne"
},
"outcome": {
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
},
{
"_id": 4,
"x": 1
}
]
},
"result": {
"matchedCount": 0,
"modifiedCount": 0,
"upsertedId": 4
}
}
}
]
}
...@@ -202,4 +202,4 @@ ...@@ -202,4 +202,4 @@
} }
} }
] ]
} }
\ No newline at end of file
...@@ -2,178 +2,181 @@ ...@@ -2,178 +2,181 @@
"data": [ "data": [
{ {
"_id": 1, "_id": 1,
"x": 11 "y": [
{
"b": 3
},
{
"b": 1
}
]
}, },
{ {
"_id": 2, "_id": 2,
"x": 22 "y": [
}, {
{ "b": 0
"_id": 3, },
"x": 33 {
"b": 1
}
]
} }
], ],
"maxServerVersion": "2.4.99", "minServerVersion": "3.5.6",
"tests": [ "tests": [
{ {
"description": "UpdateMany when many documents match", "description": "UpdateMany when no documents match arrayFilters",
"operation": { "operation": {
"name": "updateMany", "name": "updateMany",
"arguments": { "arguments": {
"filter": { "filter": {},
"_id": { "update": {
"$gt": 1 "$set": {
"y.$[i].b": 2
} }
}, },
"update": { "arrayFilters": [
"$inc": { {
"x": 1 "i.b": 4
} }
} ]
} }
}, },
"outcome": { "outcome": {
"result": { "result": {
"matchedCount": 2, "matchedCount": 2,
"upsertedCount": 0 "modifiedCount": 0
}, },
"collection": { "collection": {
"data": [ "data": [
{ {
"_id": 1, "_id": 1,
"x": 11 "y": [
{
"b": 3
},
{
"b": 1
}
]
}, },
{ {
"_id": 2, "_id": 2,
"x": 23 "y": [
}, {
{ "b": 0
"_id": 3, },
"x": 34 {
"b": 1
}
]
} }
] ]
} }
} }
}, },
{ {
"description": "UpdateMany when one document matches", "description": "UpdateMany when one document matches arrayFilters",
"operation": { "operation": {
"name": "updateMany", "name": "updateMany",
"arguments": { "arguments": {
"filter": { "filter": {},
"_id": 1
},
"update": { "update": {
"$inc": { "$set": {
"x": 1 "y.$[i].b": 2
} }
} },
} "arrayFilters": [
},
"outcome": {
"result": {
"matchedCount": 1,
"upsertedCount": 0
},
"collection": {
"data": [
{
"_id": 1,
"x": 12
},
{
"_id": 2,
"x": 22
},
{ {
"_id": 3, "i.b": 3
"x": 33
} }
] ]
} }
}
},
{
"description": "UpdateMany when no documents match",
"operation": {
"name": "updateMany",
"arguments": {
"filter": {
"_id": 4
},
"update": {
"$inc": {
"x": 1
}
}
}
}, },
"outcome": { "outcome": {
"result": { "result": {
"matchedCount": 0, "matchedCount": 2,
"upsertedCount": 0 "modifiedCount": 1
}, },
"collection": { "collection": {
"data": [ "data": [
{ {
"_id": 1, "_id": 1,
"x": 11 "y": [
{
"b": 2
},
{
"b": 1
}
]
}, },
{ {
"_id": 2, "_id": 2,
"x": 22 "y": [
}, {
{ "b": 0
"_id": 3, },
"x": 33 {
"b": 1
}
]
} }
] ]
} }
} }
}, },
{ {
"description": "UpdateMany with upsert when no documents match", "description": "UpdateMany when multiple documents match arrayFilters",
"operation": { "operation": {
"name": "updateMany", "name": "updateMany",
"arguments": { "arguments": {
"filter": { "filter": {},
"_id": 4
},
"update": { "update": {
"$inc": { "$set": {
"x": 1 "y.$[i].b": 2
} }
}, },
"upsert": true "arrayFilters": [
{
"i.b": 1
}
]
} }
}, },
"outcome": { "outcome": {
"result": { "result": {
"matchedCount": 0, "matchedCount": 2,
"upsertedCount": 1, "modifiedCount": 2
"upsertedId": 4
}, },
"collection": { "collection": {
"data": [ "data": [
{ {
"_id": 1, "_id": 1,
"x": 11 "y": [
{
"b": 3
},
{
"b": 2
}
]
}, },
{ {
"_id": 2, "_id": 2,
"x": 22 "y": [
}, {
{ "b": 0
"_id": 3, },
"x": 33 {
}, "b": 2
{ }
"_id": 4, ]
"x": 1
} }
] ]
} }
} }
} }
] ]
} }
\ No newline at end of file
...@@ -59,4 +59,4 @@ ...@@ -59,4 +59,4 @@
} }
} }
] ]
} }
\ No newline at end of file
...@@ -180,4 +180,4 @@ ...@@ -180,4 +180,4 @@
} }
} }
] ]
} }
\ No newline at end of file
{
"data": [
{
"_id": 1,
"y": [
{
"b": 3
},
{
"b": 1
}
]
},
{
"_id": 2,
"y": [
{
"b": 0
},
{
"b": 1
}
]
},
{
"_id": 3,
"y": [
{
"b": 5,
"c": [
{
"d": 2
},
{
"d": 1
}
]
}
]
}
],
"minServerVersion": "3.5.6",
"tests": [
{
"description": "UpdateOne when no document matches arrayFilters",
"operation": {
"name": "updateOne",
"arguments": {
"filter": {},
"update": {
"$set": {
"y.$[i].b": 2
}
},
"arrayFilters": [
{
"i.b": 4
}
]
}
},
"outcome": {
"result": {
"matchedCount": 1,
"modifiedCount": 0
},
"collection": {
"data": [
{
"_id": 1,
"y": [
{
"b": 3
},
{
"b": 1
}
]
},
{
"_id": 2,
"y": [
{
"b": 0
},
{
"b": 1
}
]
},
{
"_id": 3,
"y": [
{
"b": 5,
"c": [
{
"d": 2
},
{
"d": 1
}
]
}
]
}
]
}
}
},
{
"description": "UpdateOne when one document matches arrayFilters",
"operation": {
"name": "updateOne",
"arguments": {
"filter": {},
"update": {
"$set": {
"y.$[i].b": 2
}
},
"arrayFilters": [
{
"i.b": 3
}
]
}
},
"outcome": {
"result": {
"matchedCount": 1,
"modifiedCount": 1
},
"collection": {
"data": [
{
"_id": 1,
"y": [
{
"b": 2
},
{
"b": 1
}
]
},
{
"_id": 2,
"y": [
{
"b": 0
},
{
"b": 1
}
]
},
{
"_id": 3,
"y": [
{
"b": 5,
"c": [
{
"d": 2
},
{
"d": 1
}
]
}
]
}
]
}
}
},
{
"description": "UpdateOne when multiple documents match arrayFilters",
"operation": {
"name": "updateOne",
"arguments": {
"filter": {},
"update": {
"$set": {
"y.$[i].b": 2
}
},
"arrayFilters": [
{
"i.b": 1
}
]
}
},
"outcome": {
"result": {
"matchedCount": 1,
"modifiedCount": 1
},
"collection": {
"data": [
{
"_id": 1,
"y": [
{
"b": 3
},
{
"b": 2
}
]
},
{
"_id": 2,
"y": [
{
"b": 0
},
{
"b": 1
}
]
},
{
"_id": 3,
"y": [
{
"b": 5,
"c": [
{
"d": 2
},
{
"d": 1
}
]
}
]
}
]
}
}
},
{
"description": "UpdateOne when no documents match multiple arrayFilters",
"operation": {
"name": "updateOne",
"arguments": {
"filter": {
"_id": 3
},
"update": {
"$set": {
"y.$[i].c.$[j].d": 0
}
},
"arrayFilters": [
{
"i.b": 5
},
{
"j.d": 3
}
]
}
},
"outcome": {
"result": {
"matchedCount": 1,
"modifiedCount": 0
},
"collection": {
"data": [
{
"_id": 1,
"y": [
{
"b": 3
},
{
"b": 1
}
]
},
{
"_id": 2,
"y": [
{
"b": 0
},
{
"b": 1
}
]
},
{
"_id": 3,
"y": [
{
"b": 5,
"c": [
{
"d": 2
},
{
"d": 1
}
]
}
]
}
]
}
}
},
{
"description": "UpdateOne when one document matches multiple arrayFilters",
"operation": {
"name": "updateOne",
"arguments": {
"filter": {
"_id": 3
},
"update": {
"$set": {
"y.$[i].c.$[j].d": 0
}
},
"arrayFilters": [
{
"i.b": 5
},
{
"j.d": 1
}
]
}
},
"outcome": {
"result": {
"matchedCount": 1,
"modifiedCount": 1
},
"collection": {
"data": [
{
"_id": 1,
"y": [
{
"b": 3
},
{
"b": 1
}
]
},
{
"_id": 2,
"y": [
{
"b": 0
},
{
"b": 1
}
]
},
{
"_id": 3,
"y": [
{
"b": 5,
"c": [
{
"d": 2
},
{
"d": 0
}
]
}
]
}
]
}
}
}
]
}
...@@ -51,4 +51,4 @@ ...@@ -51,4 +51,4 @@
} }
} }
] ]
} }
\ No newline at end of file
...@@ -164,4 +164,4 @@ ...@@ -164,4 +164,4 @@
} }
} }
] ]
} }
\ No newline at end of file
...@@ -292,6 +292,18 @@ class BulkWriteTest extends TestCase ...@@ -292,6 +292,18 @@ class BulkWriteTest extends TestCase
]); ]);
} }
/**
* @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessageRegExp /Expected \$operations\[0\]\["updateMany"\]\[2\]\["arrayFilters"\] to have type "array" but found "[\w ]+"/
* @dataProvider provideInvalidArrayValues
*/
public function testUpdateManyArrayFiltersOptionTypeCheck($arrayFilters)
{
new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [
[BulkWrite::UPDATE_MANY => [['x' => 1], ['$set' => ['x' => 1]], ['arrayFilters' => $arrayFilters]]],
]);
}
/** /**
* @expectedException MongoDB\Exception\InvalidArgumentException * @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessageRegExp /Expected \$operations\[0\]\["updateMany"\]\[2\]\["collation"\] to have type "array or object" but found "[\w ]+"/ * @expectedExceptionMessageRegExp /Expected \$operations\[0\]\["updateMany"\]\[2\]\["collation"\] to have type "array or object" but found "[\w ]+"/
...@@ -373,6 +385,18 @@ class BulkWriteTest extends TestCase ...@@ -373,6 +385,18 @@ class BulkWriteTest extends TestCase
]); ]);
} }
/**
* @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessageRegExp /Expected \$operations\[0\]\["updateOne"\]\[2\]\["arrayFilters"\] to have type "array" but found "[\w ]+"/
* @dataProvider provideInvalidArrayValues
*/
public function testUpdateOneArrayFiltersOptionTypeCheck($arrayFilters)
{
new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [
[BulkWrite::UPDATE_ONE => [['x' => 1], ['$set' => ['x' => 1]], ['arrayFilters' => $arrayFilters]]],
]);
}
/** /**
* @expectedException MongoDB\Exception\InvalidArgumentException * @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessageRegExp /Expected \$operations\[0\]\["updateOne"\]\[2\]\["collation"\] to have type "array or object" but found "[\w ]+"/ * @expectedExceptionMessageRegExp /Expected \$operations\[0\]\["updateOne"\]\[2\]\["collation"\] to have type "array or object" but found "[\w ]+"/
......
...@@ -19,6 +19,10 @@ class FindAndModifyTest extends TestCase ...@@ -19,6 +19,10 @@ class FindAndModifyTest extends TestCase
{ {
$options = []; $options = [];
foreach ($this->getInvalidArrayValues() as $value) {
$options[][] = ['arrayFilters' => $value];
}
foreach ($this->getInvalidBooleanValues() as $value) { foreach ($this->getInvalidBooleanValues() as $value) {
$options[][] = ['bypassDocumentValidation' => $value]; $options[][] = ['bypassDocumentValidation' => $value];
} }
......
...@@ -39,6 +39,10 @@ class UpdateTest extends TestCase ...@@ -39,6 +39,10 @@ class UpdateTest extends TestCase
{ {
$options = []; $options = [];
foreach ($this->getInvalidArrayValues() as $value) {
$options[][] = ['arrayFilters' => $value];
}
foreach ($this->getInvalidBooleanValues() as $value) { foreach ($this->getInvalidBooleanValues() as $value) {
$options[][] = ['bypassDocumentValidation' => $value]; $options[][] = ['bypassDocumentValidation' => $value];
} }
......
...@@ -11,6 +11,11 @@ use stdClass; ...@@ -11,6 +11,11 @@ use stdClass;
abstract class TestCase extends BaseTestCase abstract class TestCase extends BaseTestCase
{ {
public function provideInvalidArrayValues()
{
return $this->wrapValuesForDataProvider($this->getInvalidArrayValues());
}
public function provideInvalidDocumentValues() public function provideInvalidDocumentValues()
{ {
return $this->wrapValuesForDataProvider($this->getInvalidDocumentValues()); return $this->wrapValuesForDataProvider($this->getInvalidDocumentValues());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment