Commit c405cec3 authored by Jeremy Mikola's avatar Jeremy Mikola

PHPLIB-205: Support providing collation per operation

This bumps the extension requirement to 1.2.0. Composer allows dev versions with the "^" operator, so this will accept 1.2.0alpha3, which introduces a "collation" option for Query and BulkWrite methods.
parent 55e2a31d
...@@ -13,17 +13,15 @@ env: ...@@ -13,17 +13,15 @@ env:
- MONGO_REPO_TYPE="precise/mongodb-enterprise/" - MONGO_REPO_TYPE="precise/mongodb-enterprise/"
- SOURCES_LOC="/etc/apt/sources.list.d/mongodb.list" - SOURCES_LOC="/etc/apt/sources.list.d/mongodb.list"
matrix: matrix:
- DRIVER_VERSION=stable SERVER_VERSION=2.6 - DRIVER_VERSION=1.2.0alpha3 SERVER_VERSION=2.6
- DRIVER_VERSION=stable SERVER_VERSION=3.0 - DRIVER_VERSION=1.2.0alpha3 SERVER_VERSION=3.0
- DRIVER_VERSION=stable SERVER_VERSION=3.2 - DRIVER_VERSION=1.2.0alpha3 SERVER_VERSION=3.2
matrix: matrix:
fast_finish: true fast_finish: true
include: include:
- php: 5.6
env: DRIVER_VERSION=1.1.0 SERVER_VERSION=3.2
- php: 7.0 - php: 7.0
env: DRIVER_VERSION=stable SERVER_VERSION=2.4 env: DRIVER_VERSION=1.2.0alpha3 SERVER_VERSION=2.4
- php: 7.0 - php: 7.0
env: DRIVER_VERSION=devel SERVER_VERSION=3.2 env: DRIVER_VERSION=devel SERVER_VERSION=3.2
exclude: exclude:
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
], ],
"require": { "require": {
"php": ">=5.4", "php": ">=5.4",
"ext-mongodb": "^1.1.0" "ext-mongodb": "^1.2.0"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^4.8" "phpunit/phpunit": "^4.8"
......
<?php
namespace MongoDB\Exception;
class UnsupportedException extends RuntimeException implements Exception
{
/**
* Thrown when collations are not supported by a server.
*
* @return self
*/
public static function collationNotSupported()
{
return new static('Collations are not supported by the server executing this operation');
}
}
...@@ -8,6 +8,7 @@ use MongoDB\Driver\ReadPreference; ...@@ -8,6 +8,7 @@ use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Server; use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnexpectedValueException; use MongoDB\Exception\UnexpectedValueException;
use MongoDB\Exception\UnsupportedException;
use ArrayIterator; use ArrayIterator;
use stdClass; use stdClass;
use Traversable; use Traversable;
...@@ -21,6 +22,7 @@ use Traversable; ...@@ -21,6 +22,7 @@ use Traversable;
*/ */
class Aggregate implements Executable class Aggregate implements Executable
{ {
private static $wireVersionForCollation = 5;
private static $wireVersionForCursor = 2; private static $wireVersionForCursor = 2;
private static $wireVersionForDocumentLevelValidation = 4; private static $wireVersionForDocumentLevelValidation = 4;
private static $wireVersionForReadConcern = 4; private static $wireVersionForReadConcern = 4;
...@@ -48,6 +50,11 @@ class Aggregate implements Executable ...@@ -48,6 +50,11 @@ class Aggregate implements Executable
* For servers < 3.2, this option is ignored as document level validation * For servers < 3.2, this option is ignored as document level validation
* is not available. * is not available.
* *
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * maxTimeMS (integer): The maximum amount of time to allow the query to * * maxTimeMS (integer): The maximum amount of time to allow the query to
* run. * run.
* *
...@@ -117,6 +124,10 @@ class Aggregate implements Executable ...@@ -117,6 +124,10 @@ class Aggregate implements Executable
throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean'); throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean');
} }
if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) {
throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object');
}
if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) { if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {
throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer'); throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
} }
...@@ -158,9 +169,14 @@ class Aggregate implements Executable ...@@ -158,9 +169,14 @@ class Aggregate implements Executable
* @param Server $server * @param Server $server
* @return Traversable * @return Traversable
* @throws UnexpectedValueException if the command response was malformed * @throws UnexpectedValueException if the command response was malformed
* @throws UnsupportedException if collation is used and unsupported
*/ */
public function execute(Server $server) public function execute(Server $server)
{ {
if (isset($this->options['collation']) && ! \MongoDB\server_supports_feature($server, self::$wireVersionForCollation)) {
throw UnsupportedException::collationNotSupported();
}
$isCursorSupported = \MongoDB\server_supports_feature($server, self::$wireVersionForCursor); $isCursorSupported = \MongoDB\server_supports_feature($server, self::$wireVersionForCursor);
$readPreference = isset($this->options['readPreference']) ? $this->options['readPreference'] : null; $readPreference = isset($this->options['readPreference']) ? $this->options['readPreference'] : null;
...@@ -212,6 +228,10 @@ class Aggregate implements Executable ...@@ -212,6 +228,10 @@ class Aggregate implements Executable
$cmd['bypassDocumentValidation'] = $this->options['bypassDocumentValidation']; $cmd['bypassDocumentValidation'] = $this->options['bypassDocumentValidation'];
} }
if (isset($this->options['collation'])) {
$cmd['collation'] = (object) $this->options['collation'];
}
if (isset($this->options['maxTimeMS'])) { if (isset($this->options['maxTimeMS'])) {
$cmd['maxTimeMS'] = $this->options['maxTimeMS']; $cmd['maxTimeMS'] = $this->options['maxTimeMS'];
} }
......
...@@ -7,6 +7,7 @@ use MongoDB\Driver\BulkWrite as Bulk; ...@@ -7,6 +7,7 @@ use MongoDB\Driver\BulkWrite as Bulk;
use MongoDB\Driver\Server; use MongoDB\Driver\Server;
use MongoDB\Driver\WriteConcern; use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/** /**
* Operation for executing multiple write operations. * Operation for executing multiple write operations.
...@@ -23,12 +24,14 @@ class BulkWrite implements Executable ...@@ -23,12 +24,14 @@ class BulkWrite implements Executable
const UPDATE_MANY = 'updateMany'; const UPDATE_MANY = 'updateMany';
const UPDATE_ONE = 'updateOne'; const UPDATE_ONE = 'updateOne';
private static $wireVersionForCollation = 5;
private static $wireVersionForDocumentLevelValidation = 4; private static $wireVersionForDocumentLevelValidation = 4;
private $databaseName; private $databaseName;
private $collectionName; private $collectionName;
private $operations; private $operations;
private $options; private $options;
private $isCollationUsed = false;
/** /**
* Constructs a bulk write operation. * Constructs a bulk write operation.
...@@ -36,8 +39,8 @@ class BulkWrite implements Executable ...@@ -36,8 +39,8 @@ class BulkWrite implements Executable
* Example array structure for all supported operation types: * Example array structure for all supported operation types:
* *
* [ * [
* [ 'deleteMany' => [ $filter ] ], * [ 'deleteMany' => [ $filter, $options ] ],
* [ 'deleteOne' => [ $filter ] ], * [ 'deleteOne' => [ $filter, $options ] ],
* [ 'insertOne' => [ $document ] ], * [ 'insertOne' => [ $document ] ],
* [ 'replaceOne' => [ $filter, $replacement, $options ] ], * [ 'replaceOne' => [ $filter, $replacement, $options ] ],
* [ 'updateMany' => [ $filter, $update, $options ] ], * [ 'updateMany' => [ $filter, $update, $options ] ],
...@@ -48,8 +51,20 @@ class BulkWrite implements Executable ...@@ -48,8 +51,20 @@ class BulkWrite implements Executable
* writeConcern option is specified for the top-level bulk write operation * writeConcern option is specified for the top-level bulk write operation
* instead of each individual operation. * instead of each individual operation.
* *
* Supported options for deleteMany and deleteOne operations:
*
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* Supported options for replaceOne, updateMany, and updateOne operations: * Supported options for replaceOne, updateMany, and updateOne operations:
* *
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * 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.
* *
...@@ -108,7 +123,25 @@ class BulkWrite implements Executable ...@@ -108,7 +123,25 @@ class BulkWrite implements Executable
case self::DELETE_MANY: case self::DELETE_MANY:
case self::DELETE_ONE: case self::DELETE_ONE:
$operations[$i][$type][1] = ['limit' => ($type === self::DELETE_ONE ? 1 : 0)]; if ( ! isset($args[1])) {
$args[1] = [];
}
if ( ! is_array($args[1])) {
throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][1]', $i, $type), $args[1], 'array');
}
$args[1]['limit'] = ($type === self::DELETE_ONE ? 1 : 0);
if (isset($args[1]['collation'])) {
$this->isCollationUsed = true;
if ( ! is_array($args[1]['collation']) && ! is_object($args[1]['collation'])) {
throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][1]["collation"]', $i, $type), $args[1]['collation'], 'array or object');
}
}
$operations[$i][$type][1] = $args[1];
break; break;
...@@ -136,6 +169,14 @@ class BulkWrite implements Executable ...@@ -136,6 +169,14 @@ class BulkWrite implements Executable
$args[2]['multi'] = false; $args[2]['multi'] = false;
$args[2] += ['upsert' => false]; $args[2] += ['upsert' => false];
if (isset($args[2]['collation'])) {
$this->isCollationUsed = true;
if ( ! is_array($args[2]['collation']) && ! is_object($args[2]['collation'])) {
throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["collation"]', $i, $type), $args[2]['collation'], 'array or object');
}
}
if ( ! is_bool($args[2]['upsert'])) { if ( ! is_bool($args[2]['upsert'])) {
throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["upsert"]', $i, $type), $args[2]['upsert'], 'boolean'); throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["upsert"]', $i, $type), $args[2]['upsert'], 'boolean');
} }
...@@ -169,6 +210,14 @@ class BulkWrite implements Executable ...@@ -169,6 +210,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]['collation'])) {
$this->isCollationUsed = true;
if ( ! is_array($args[2]['collation']) && ! is_object($args[2]['collation'])) {
throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["collation"]', $i, $type), $args[2]['collation'], 'array or object');
}
}
if ( ! is_bool($args[2]['upsert'])) { if ( ! is_bool($args[2]['upsert'])) {
throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["upsert"]', $i, $type), $args[2]['upsert'], 'boolean'); throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["upsert"]', $i, $type), $args[2]['upsert'], 'boolean');
} }
...@@ -210,9 +259,14 @@ class BulkWrite implements Executable ...@@ -210,9 +259,14 @@ 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
*/ */
public function execute(Server $server) public function execute(Server $server)
{ {
if ($this->isCollationUsed && ! \MongoDB\server_supports_feature($server, self::$wireVersionForCollation)) {
throw UnsupportedException::collationNotSupported();
}
$options = ['ordered' => $this->options['ordered']]; $options = ['ordered' => $this->options['ordered']];
if (isset($this->options['bypassDocumentValidation']) && \MongoDB\server_supports_feature($server, self::$wireVersionForDocumentLevelValidation)) { if (isset($this->options['bypassDocumentValidation']) && \MongoDB\server_supports_feature($server, self::$wireVersionForDocumentLevelValidation)) {
......
...@@ -8,6 +8,7 @@ use MongoDB\Driver\ReadPreference; ...@@ -8,6 +8,7 @@ use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Server; use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnexpectedValueException; use MongoDB\Exception\UnexpectedValueException;
use MongoDB\Exception\UnsupportedException;
/** /**
* Operation for the count command. * Operation for the count command.
...@@ -18,6 +19,7 @@ use MongoDB\Exception\UnexpectedValueException; ...@@ -18,6 +19,7 @@ use MongoDB\Exception\UnexpectedValueException;
*/ */
class Count implements Executable class Count implements Executable
{ {
private static $wireVersionForCollation = 5;
private static $wireVersionForReadConcern = 4; private static $wireVersionForReadConcern = 4;
private $databaseName; private $databaseName;
...@@ -30,6 +32,11 @@ class Count implements Executable ...@@ -30,6 +32,11 @@ class Count implements Executable
* *
* Supported options: * Supported options:
* *
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * hint (string|document): The index to use. If a document, it will be * * hint (string|document): The index to use. If a document, it will be
* interpretted as an index specification and a name will be generated. * interpretted as an index specification and a name will be generated.
* *
...@@ -60,6 +67,10 @@ class Count implements Executable ...@@ -60,6 +67,10 @@ class Count implements Executable
throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object'); throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object');
} }
if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) {
throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object');
}
if (isset($options['hint'])) { if (isset($options['hint'])) {
if (is_array($options['hint']) || is_object($options['hint'])) { if (is_array($options['hint']) || is_object($options['hint'])) {
$options['hint'] = \MongoDB\generate_index_name($options['hint']); $options['hint'] = \MongoDB\generate_index_name($options['hint']);
...@@ -103,9 +114,14 @@ class Count implements Executable ...@@ -103,9 +114,14 @@ class Count implements Executable
* @param Server $server * @param Server $server
* @return integer * @return integer
* @throws UnexpectedValueException if the command response was malformed * @throws UnexpectedValueException if the command response was malformed
* @throws UnsupportedException if collation is used and unsupported
*/ */
public function execute(Server $server) public function execute(Server $server)
{ {
if (isset($this->options['collation']) && ! \MongoDB\server_supports_feature($server, self::$wireVersionForCollation)) {
throw UnsupportedException::collationNotSupported();
}
$readPreference = isset($this->options['readPreference']) ? $this->options['readPreference'] : null; $readPreference = isset($this->options['readPreference']) ? $this->options['readPreference'] : null;
$cursor = $server->executeCommand($this->databaseName, $this->createCommand($server), $readPreference); $cursor = $server->executeCommand($this->databaseName, $this->createCommand($server), $readPreference);
...@@ -133,6 +149,10 @@ class Count implements Executable ...@@ -133,6 +149,10 @@ class Count implements Executable
$cmd['query'] = (object) $this->filter; $cmd['query'] = (object) $this->filter;
} }
if (isset($this->options['collation'])) {
$cmd['collation'] = (object) $this->options['collation'];
}
foreach (['hint', 'limit', 'maxTimeMS', 'skip'] as $option) { foreach (['hint', 'limit', 'maxTimeMS', 'skip'] as $option) {
if (isset($this->options[$option])) { if (isset($this->options[$option])) {
$cmd[$option] = $this->options[$option]; $cmd[$option] = $this->options[$option];
......
...@@ -5,6 +5,7 @@ namespace MongoDB\Operation; ...@@ -5,6 +5,7 @@ namespace MongoDB\Operation;
use MongoDB\Driver\Command; use MongoDB\Driver\Command;
use MongoDB\Driver\Server; use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/** /**
* Operation for the create command. * Operation for the create command.
...@@ -18,6 +19,8 @@ class CreateCollection implements Executable ...@@ -18,6 +19,8 @@ class CreateCollection implements Executable
const USE_POWER_OF_2_SIZES = 1; const USE_POWER_OF_2_SIZES = 1;
const NO_PADDING = 2; const NO_PADDING = 2;
private static $wireVersionForCollation = 5;
private $databaseName; private $databaseName;
private $collectionName; private $collectionName;
private $options = []; private $options = [];
...@@ -34,6 +37,11 @@ class CreateCollection implements Executable ...@@ -34,6 +37,11 @@ class CreateCollection implements Executable
* * capped (boolean): Specify true to create a capped collection. If set, * * capped (boolean): Specify true to create a capped collection. If set,
* the size option must also be specified. The default is false. * the size option must also be specified. The default is false.
* *
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * flags (integer): Options for the MMAPv1 storage engine only. Must be a * * flags (integer): Options for the MMAPv1 storage engine only. Must be a
* bitwise combination CreateCollection::USE_POWER_OF_2_SIZES and * bitwise combination CreateCollection::USE_POWER_OF_2_SIZES and
* CreateCollection::NO_PADDING. The default is * CreateCollection::NO_PADDING. The default is
...@@ -78,6 +86,10 @@ class CreateCollection implements Executable ...@@ -78,6 +86,10 @@ class CreateCollection implements Executable
throw InvalidArgumentException::invalidType('"capped" option', $options['capped'], 'boolean'); throw InvalidArgumentException::invalidType('"capped" option', $options['capped'], 'boolean');
} }
if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) {
throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object');
}
if (isset($options['flags']) && ! is_integer($options['flags'])) { if (isset($options['flags']) && ! is_integer($options['flags'])) {
throw InvalidArgumentException::invalidType('"flags" option', $options['flags'], 'integer'); throw InvalidArgumentException::invalidType('"flags" option', $options['flags'], 'integer');
} }
...@@ -129,9 +141,14 @@ class CreateCollection implements Executable ...@@ -129,9 +141,14 @@ class CreateCollection implements Executable
* @see Executable::execute() * @see Executable::execute()
* @param Server $server * @param Server $server
* @return array|object Command result document * @return array|object Command result document
* @throws UnsupportedException if collation is used and unsupported
*/ */
public function execute(Server $server) public function execute(Server $server)
{ {
if (isset($this->options['collation']) && ! \MongoDB\server_supports_feature($server, self::$wireVersionForCollation)) {
throw UnsupportedException::collationNotSupported();
}
$cursor = $server->executeCommand($this->databaseName, $this->createCommand()); $cursor = $server->executeCommand($this->databaseName, $this->createCommand());
if (isset($this->options['typeMap'])) { if (isset($this->options['typeMap'])) {
...@@ -156,16 +173,10 @@ class CreateCollection implements Executable ...@@ -156,16 +173,10 @@ class CreateCollection implements Executable
} }
} }
if (isset($this->options['indexOptionDefaults'])) { foreach (['collation', 'indexOptionDefaults', 'storageEngine', 'validator'] as $option) {
$cmd['indexOptionDefaults'] = (object) $this->options['indexOptionDefaults']; if (isset($this->options[$option])) {
} $cmd[$option] = (object) $this->options[$option];
if (isset($this->options['storageEngine'])) {
$cmd['storageEngine'] = (object) $this->options['storageEngine'];
} }
if (isset($this->options['validator'])) {
$cmd['validator'] = (object) $this->options['validator'];
} }
return new Command($cmd); return new Command($cmd);
......
...@@ -7,6 +7,7 @@ use MongoDB\Driver\Server; ...@@ -7,6 +7,7 @@ use MongoDB\Driver\Server;
use MongoDB\Driver\BulkWrite as Bulk; use MongoDB\Driver\BulkWrite as Bulk;
use MongoDB\Driver\WriteConcern; use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
use MongoDB\Model\IndexInput; use MongoDB\Model\IndexInput;
/** /**
...@@ -19,11 +20,13 @@ use MongoDB\Model\IndexInput; ...@@ -19,11 +20,13 @@ use MongoDB\Model\IndexInput;
*/ */
class CreateIndexes implements Executable class CreateIndexes implements Executable
{ {
private static $wireVersionForCollation = 5;
private static $wireVersionForCommand = 2; private static $wireVersionForCommand = 2;
private $databaseName; private $databaseName;
private $collectionName; private $collectionName;
private $indexes = []; private $indexes = [];
private $isCollationUsed = false;
/** /**
* Constructs a createIndexes command. * Constructs a createIndexes command.
...@@ -54,6 +57,10 @@ class CreateIndexes implements Executable ...@@ -54,6 +57,10 @@ class CreateIndexes implements Executable
$index['ns'] = $databaseName . '.' . $collectionName; $index['ns'] = $databaseName . '.' . $collectionName;
} }
if (isset($index['collation'])) {
$this->isCollationUsed = true;
}
$this->indexes[] = new IndexInput($index); $this->indexes[] = new IndexInput($index);
$expectedIndex += 1; $expectedIndex += 1;
...@@ -72,9 +79,14 @@ class CreateIndexes implements Executable ...@@ -72,9 +79,14 @@ class CreateIndexes implements Executable
* @see Executable::execute() * @see Executable::execute()
* @param Server $server * @param Server $server
* @return string[] The names of the created indexes * @return string[] The names of the created indexes
* @throws UnsupportedException if collation is used and unsupported
*/ */
public function execute(Server $server) public function execute(Server $server)
{ {
if ($this->isCollationUsed && ! \MongoDB\server_supports_feature($server, self::$wireVersionForCollation)) {
throw UnsupportedException::collationNotSupported();
}
if (\MongoDB\server_supports_feature($server, self::$wireVersionForCommand)) { if (\MongoDB\server_supports_feature($server, self::$wireVersionForCommand)) {
$this->executeCommand($server); $this->executeCommand($server);
} else { } else {
......
...@@ -7,6 +7,7 @@ use MongoDB\Driver\BulkWrite as Bulk; ...@@ -7,6 +7,7 @@ use MongoDB\Driver\BulkWrite as Bulk;
use MongoDB\Driver\Server; use MongoDB\Driver\Server;
use MongoDB\Driver\WriteConcern; use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/** /**
* Operation for the delete command. * Operation for the delete command.
...@@ -19,6 +20,8 @@ use MongoDB\Exception\InvalidArgumentException; ...@@ -19,6 +20,8 @@ use MongoDB\Exception\InvalidArgumentException;
*/ */
class Delete implements Executable class Delete implements Executable
{ {
private static $wireVersionForCollation = 5;
private $databaseName; private $databaseName;
private $collectionName; private $collectionName;
private $filter; private $filter;
...@@ -30,6 +33,11 @@ class Delete implements Executable ...@@ -30,6 +33,11 @@ class Delete implements Executable
* *
* Supported options: * Supported options:
* *
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * writeConcern (MongoDB\Driver\WriteConcern): Write concern. * * writeConcern (MongoDB\Driver\WriteConcern): Write concern.
* *
* @param string $databaseName Database name * @param string $databaseName Database name
...@@ -51,6 +59,10 @@ class Delete implements Executable ...@@ -51,6 +59,10 @@ class Delete implements Executable
throw new InvalidArgumentException('$limit must be 0 or 1'); throw new InvalidArgumentException('$limit must be 0 or 1');
} }
if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) {
throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object');
}
if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) { if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) {
throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], 'MongoDB\Driver\WriteConcern'); throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], 'MongoDB\Driver\WriteConcern');
} }
...@@ -71,8 +83,18 @@ class Delete implements Executable ...@@ -71,8 +83,18 @@ class Delete implements Executable
*/ */
public function execute(Server $server) public function execute(Server $server)
{ {
if (isset($this->options['collation']) && ! \MongoDB\server_supports_feature($server, self::$wireVersionForCollation)) {
throw UnsupportedException::collationNotSupported();
}
$deleteOptions = ['limit' => $this->limit];
if (isset($this->options['collation'])) {
$deleteOptions['collation'] = (object) $this->options['collation'];
}
$bulk = new Bulk(); $bulk = new Bulk();
$bulk->delete($this->filter, ['limit' => $this->limit]); $bulk->delete($this->filter, $deleteOptions);
$writeConcern = isset($this->options['writeConcern']) ? $this->options['writeConcern'] : null; $writeConcern = isset($this->options['writeConcern']) ? $this->options['writeConcern'] : null;
$writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $writeConcern); $writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $writeConcern);
......
...@@ -5,6 +5,7 @@ namespace MongoDB\Operation; ...@@ -5,6 +5,7 @@ namespace MongoDB\Operation;
use MongoDB\DeleteResult; use MongoDB\DeleteResult;
use MongoDB\Driver\Server; use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/** /**
* Operation for deleting multiple document with the delete command. * Operation for deleting multiple document with the delete command.
...@@ -22,6 +23,11 @@ class DeleteMany implements Executable ...@@ -22,6 +23,11 @@ class DeleteMany implements Executable
* *
* Supported options: * Supported options:
* *
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * writeConcern (MongoDB\Driver\WriteConcern): Write concern. * * writeConcern (MongoDB\Driver\WriteConcern): Write concern.
* *
* @param string $databaseName Database name * @param string $databaseName Database name
...@@ -41,6 +47,7 @@ class DeleteMany implements Executable ...@@ -41,6 +47,7 @@ class DeleteMany implements Executable
* @see Executable::execute() * @see Executable::execute()
* @param Server $server * @param Server $server
* @return DeleteResult * @return DeleteResult
* @throws UnsupportedException if collation is used and unsupported
*/ */
public function execute(Server $server) public function execute(Server $server)
{ {
......
...@@ -5,6 +5,7 @@ namespace MongoDB\Operation; ...@@ -5,6 +5,7 @@ namespace MongoDB\Operation;
use MongoDB\DeleteResult; use MongoDB\DeleteResult;
use MongoDB\Driver\Server; use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/** /**
* Operation for deleting a single document with the delete command. * Operation for deleting a single document with the delete command.
...@@ -22,6 +23,11 @@ class DeleteOne implements Executable ...@@ -22,6 +23,11 @@ class DeleteOne implements Executable
* *
* Supported options: * Supported options:
* *
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * writeConcern (MongoDB\Driver\WriteConcern): Write concern. * * writeConcern (MongoDB\Driver\WriteConcern): Write concern.
* *
* @param string $databaseName Database name * @param string $databaseName Database name
...@@ -41,6 +47,7 @@ class DeleteOne implements Executable ...@@ -41,6 +47,7 @@ class DeleteOne implements Executable
* @see Executable::execute() * @see Executable::execute()
* @param Server $server * @param Server $server
* @return DeleteResult * @return DeleteResult
* @throws UnsupportedException if collation is used and unsupported
*/ */
public function execute(Server $server) public function execute(Server $server)
{ {
......
...@@ -8,6 +8,7 @@ use MongoDB\Driver\ReadPreference; ...@@ -8,6 +8,7 @@ use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Server; use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnexpectedValueException; use MongoDB\Exception\UnexpectedValueException;
use MongoDB\Exception\UnsupportedException;
/** /**
* Operation for the distinct command. * Operation for the distinct command.
...@@ -18,6 +19,7 @@ use MongoDB\Exception\UnexpectedValueException; ...@@ -18,6 +19,7 @@ use MongoDB\Exception\UnexpectedValueException;
*/ */
class Distinct implements Executable class Distinct implements Executable
{ {
private static $wireVersionForCollation = 5;
private static $wireVersionForReadConcern = 4; private static $wireVersionForReadConcern = 4;
private $databaseName; private $databaseName;
...@@ -31,6 +33,11 @@ class Distinct implements Executable ...@@ -31,6 +33,11 @@ class Distinct implements Executable
* *
* Supported options: * Supported options:
* *
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * maxTimeMS (integer): The maximum amount of time to allow the query to * * maxTimeMS (integer): The maximum amount of time to allow the query to
* run. * run.
* *
...@@ -54,6 +61,10 @@ class Distinct implements Executable ...@@ -54,6 +61,10 @@ class Distinct implements Executable
throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object'); throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object');
} }
if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) {
throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object');
}
if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) { if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {
throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer'); throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
} }
...@@ -83,6 +94,10 @@ class Distinct implements Executable ...@@ -83,6 +94,10 @@ class Distinct implements Executable
*/ */
public function execute(Server $server) public function execute(Server $server)
{ {
if (isset($this->options['collation']) && ! \MongoDB\server_supports_feature($server, self::$wireVersionForCollation)) {
throw UnsupportedException::collationNotSupported();
}
$readPreference = isset($this->options['readPreference']) ? $this->options['readPreference'] : null; $readPreference = isset($this->options['readPreference']) ? $this->options['readPreference'] : null;
$cursor = $server->executeCommand($this->databaseName, $this->createCommand($server), $readPreference); $cursor = $server->executeCommand($this->databaseName, $this->createCommand($server), $readPreference);
...@@ -112,6 +127,10 @@ class Distinct implements Executable ...@@ -112,6 +127,10 @@ class Distinct implements Executable
$cmd['query'] = (object) $this->filter; $cmd['query'] = (object) $this->filter;
} }
if (isset($this->options['collation'])) {
$cmd['collation'] = (object) $this->options['collation'];
}
if (isset($this->options['maxTimeMS'])) { if (isset($this->options['maxTimeMS'])) {
$cmd['maxTimeMS'] = $this->options['maxTimeMS']; $cmd['maxTimeMS'] = $this->options['maxTimeMS'];
} }
......
...@@ -8,6 +8,7 @@ use MongoDB\Driver\ReadConcern; ...@@ -8,6 +8,7 @@ use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference; use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Server; use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/** /**
* Operation for the find command. * Operation for the find command.
...@@ -23,6 +24,8 @@ class Find implements Executable ...@@ -23,6 +24,8 @@ class Find implements Executable
const TAILABLE = 2; const TAILABLE = 2;
const TAILABLE_AWAIT = 3; const TAILABLE_AWAIT = 3;
private static $wireVersionForCollation = 5;
private $databaseName; private $databaseName;
private $collectionName; private $collectionName;
private $filter; private $filter;
...@@ -38,6 +41,11 @@ class Find implements Executable ...@@ -38,6 +41,11 @@ class Find implements Executable
* *
* * batchSize (integer): The number of documents to return per batch. * * batchSize (integer): The number of documents to return per batch.
* *
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * comment (string): Attaches a comment to the query. If "$comment" also * * comment (string): Attaches a comment to the query. If "$comment" also
* exists in the modifiers document, this option will take precedence. * exists in the modifiers document, this option will take precedence.
* *
...@@ -100,6 +108,10 @@ class Find implements Executable ...@@ -100,6 +108,10 @@ class Find implements Executable
throw InvalidArgumentException::invalidType('"batchSize" option', $options['batchSize'], 'integer'); throw InvalidArgumentException::invalidType('"batchSize" option', $options['batchSize'], 'integer');
} }
if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) {
throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object');
}
if (isset($options['comment']) && ! is_string($options['comment'])) { if (isset($options['comment']) && ! is_string($options['comment'])) {
throw InvalidArgumentException::invalidType('"comment" option', $options['comment'], 'comment'); throw InvalidArgumentException::invalidType('"comment" option', $options['comment'], 'comment');
} }
...@@ -175,6 +187,10 @@ class Find implements Executable ...@@ -175,6 +187,10 @@ class Find implements Executable
*/ */
public function execute(Server $server) public function execute(Server $server)
{ {
if (isset($this->options['collation']) && ! \MongoDB\server_supports_feature($server, self::$wireVersionForCollation)) {
throw UnsupportedException::collationNotSupported();
}
$readPreference = isset($this->options['readPreference']) ? $this->options['readPreference'] : null; $readPreference = isset($this->options['readPreference']) ? $this->options['readPreference'] : null;
$cursor = $server->executeQuery($this->databaseName . '.' . $this->collectionName, $this->createQuery(), $readPreference); $cursor = $server->executeQuery($this->databaseName . '.' . $this->collectionName, $this->createQuery(), $readPreference);
...@@ -215,6 +231,10 @@ class Find implements Executable ...@@ -215,6 +231,10 @@ class Find implements Executable
} }
} }
if (isset($this->options['collation'])) {
$options['collation'] = (object) $this->options['collation'];
}
$modifiers = empty($this->options['modifiers']) ? [] : (array) $this->options['modifiers']; $modifiers = empty($this->options['modifiers']) ? [] : (array) $this->options['modifiers'];
if (isset($this->options['comment'])) { if (isset($this->options['comment'])) {
......
...@@ -7,6 +7,7 @@ use MongoDB\Driver\Server; ...@@ -7,6 +7,7 @@ use MongoDB\Driver\Server;
use MongoDB\Driver\WriteConcern; use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnexpectedValueException; use MongoDB\Exception\UnexpectedValueException;
use MongoDB\Exception\UnsupportedException;
/** /**
* Operation for the findAndModify command. * Operation for the findAndModify command.
...@@ -19,6 +20,7 @@ use MongoDB\Exception\UnexpectedValueException; ...@@ -19,6 +20,7 @@ use MongoDB\Exception\UnexpectedValueException;
*/ */
class FindAndModify implements Executable class FindAndModify implements Executable
{ {
private static $wireVersionForCollation = 5;
private static $wireVersionForDocumentLevelValidation = 4; private static $wireVersionForDocumentLevelValidation = 4;
private static $wireVersionForWriteConcern = 4; private static $wireVersionForWriteConcern = 4;
...@@ -31,6 +33,11 @@ class FindAndModify implements Executable ...@@ -31,6 +33,11 @@ class FindAndModify implements Executable
* *
* Supported options: * Supported options:
* *
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * bypassDocumentValidation (boolean): If true, allows the write to opt * * bypassDocumentValidation (boolean): If true, allows the write to opt
* out of document level validation. * out of document level validation.
* *
...@@ -79,6 +86,10 @@ class FindAndModify implements Executable ...@@ -79,6 +86,10 @@ class FindAndModify implements Executable
throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean'); throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean');
} }
if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) {
throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object');
}
if (isset($options['fields']) && ! is_array($options['fields']) && ! is_object($options['fields'])) { if (isset($options['fields']) && ! is_array($options['fields']) && ! is_object($options['fields'])) {
throw InvalidArgumentException::invalidType('"fields" option', $options['fields'], 'array or object'); throw InvalidArgumentException::invalidType('"fields" option', $options['fields'], 'array or object');
} }
...@@ -134,6 +145,10 @@ class FindAndModify implements Executable ...@@ -134,6 +145,10 @@ class FindAndModify implements Executable
*/ */
public function execute(Server $server) public function execute(Server $server)
{ {
if (isset($this->options['collation']) && ! \MongoDB\server_supports_feature($server, self::$wireVersionForCollation)) {
throw UnsupportedException::collationNotSupported();
}
$cursor = $server->executeCommand($this->databaseName, $this->createCommand($server)); $cursor = $server->executeCommand($this->databaseName, $this->createCommand($server));
$result = current($cursor->toArray()); $result = current($cursor->toArray());
...@@ -176,7 +191,7 @@ class FindAndModify implements Executable ...@@ -176,7 +191,7 @@ class FindAndModify implements Executable
$cmd['upsert'] = $this->options['upsert']; $cmd['upsert'] = $this->options['upsert'];
} }
foreach (['fields', 'query', 'sort', 'update'] as $option) { foreach (['collation', 'fields', 'query', 'sort', 'update'] as $option) {
if (isset($this->options[$option])) { if (isset($this->options[$option])) {
$cmd[$option] = (object) $this->options[$option]; $cmd[$option] = (object) $this->options[$option];
} }
......
...@@ -4,6 +4,7 @@ namespace MongoDB\Operation; ...@@ -4,6 +4,7 @@ namespace MongoDB\Operation;
use MongoDB\Driver\Server; use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/** /**
* Operation for finding a single document with the find command. * Operation for finding a single document with the find command.
...@@ -23,6 +24,11 @@ class FindOne implements Executable ...@@ -23,6 +24,11 @@ class FindOne implements Executable
* *
* Supported options: * Supported options:
* *
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * comment (string): Attaches a comment to the query. If "$comment" also * * comment (string): Attaches a comment to the query. If "$comment" also
* exists in the modifiers document, this option will take precedence. * exists in the modifiers document, this option will take precedence.
* *
...@@ -75,6 +81,7 @@ class FindOne implements Executable ...@@ -75,6 +81,7 @@ class FindOne implements Executable
* @see Executable::execute() * @see Executable::execute()
* @param Server $server * @param Server $server
* @return array|object|null * @return array|object|null
* @throws UnsupportedException if collation is used and unsupported
*/ */
public function execute(Server $server) public function execute(Server $server)
{ {
......
...@@ -4,6 +4,7 @@ namespace MongoDB\Operation; ...@@ -4,6 +4,7 @@ namespace MongoDB\Operation;
use MongoDB\Driver\Server; use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/** /**
* Operation for deleting a document with the findAndModify command. * Operation for deleting a document with the findAndModify command.
...@@ -21,6 +22,11 @@ class FindOneAndDelete implements Executable ...@@ -21,6 +22,11 @@ class FindOneAndDelete implements Executable
* *
* Supported options: * Supported options:
* *
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * maxTimeMS (integer): The maximum amount of time to allow the query to * * maxTimeMS (integer): The maximum amount of time to allow the query to
* run. * run.
* *
...@@ -68,6 +74,7 @@ class FindOneAndDelete implements Executable ...@@ -68,6 +74,7 @@ class FindOneAndDelete implements Executable
* @see Executable::execute() * @see Executable::execute()
* @param Server $server * @param Server $server
* @return object|null * @return object|null
* @throws UnsupportedException if collation is used and unsupported
*/ */
public function execute(Server $server) public function execute(Server $server)
{ {
......
...@@ -4,6 +4,7 @@ namespace MongoDB\Operation; ...@@ -4,6 +4,7 @@ namespace MongoDB\Operation;
use MongoDB\Driver\Server; use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/** /**
* Operation for replacing a document with the findAndModify command. * Operation for replacing a document with the findAndModify command.
...@@ -27,6 +28,11 @@ class FindOneAndReplace implements Executable ...@@ -27,6 +28,11 @@ class FindOneAndReplace implements Executable
* * bypassDocumentValidation (boolean): If true, allows the write to opt * * bypassDocumentValidation (boolean): If true, allows the write to opt
* out of document level validation. * out of document level validation.
* *
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * maxTimeMS (integer): The maximum amount of time to allow the query to * * maxTimeMS (integer): The maximum amount of time to allow the query to
* run. * run.
* *
...@@ -108,6 +114,7 @@ class FindOneAndReplace implements Executable ...@@ -108,6 +114,7 @@ class FindOneAndReplace implements Executable
* @see Executable::execute() * @see Executable::execute()
* @param Server $server * @param Server $server
* @return object|null * @return object|null
* @throws UnsupportedException if collation is used and unsupported
*/ */
public function execute(Server $server) public function execute(Server $server)
{ {
......
...@@ -4,6 +4,7 @@ namespace MongoDB\Operation; ...@@ -4,6 +4,7 @@ namespace MongoDB\Operation;
use MongoDB\Driver\Server; use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/** /**
* Operation for updating a document with the findAndModify command. * Operation for updating a document with the findAndModify command.
...@@ -27,6 +28,11 @@ class FindOneAndUpdate implements Executable ...@@ -27,6 +28,11 @@ class FindOneAndUpdate implements Executable
* * bypassDocumentValidation (boolean): If true, allows the write to opt * * bypassDocumentValidation (boolean): If true, allows the write to opt
* out of document level validation. * out of document level validation.
* *
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * maxTimeMS (integer): The maximum amount of time to allow the query to * * maxTimeMS (integer): The maximum amount of time to allow the query to
* run. * run.
* *
...@@ -108,6 +114,7 @@ class FindOneAndUpdate implements Executable ...@@ -108,6 +114,7 @@ class FindOneAndUpdate implements Executable
* @see Executable::execute() * @see Executable::execute()
* @param Server $server * @param Server $server
* @return object|null * @return object|null
* @throws UnsupportedException if collation is used and unsupported
*/ */
public function execute(Server $server) public function execute(Server $server)
{ {
......
...@@ -5,6 +5,7 @@ namespace MongoDB\Operation; ...@@ -5,6 +5,7 @@ namespace MongoDB\Operation;
use MongoDB\UpdateResult; use MongoDB\UpdateResult;
use MongoDB\Driver\Server; use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/** /**
* Operation for replacing a single document with the update command. * Operation for replacing a single document with the update command.
...@@ -25,6 +26,11 @@ class ReplaceOne implements Executable ...@@ -25,6 +26,11 @@ class ReplaceOne implements Executable
* * bypassDocumentValidation (boolean): If true, allows the write to opt * * bypassDocumentValidation (boolean): If true, allows the write to opt
* out of document level validation. * out of document level validation.
* *
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * 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.
* *
...@@ -62,6 +68,7 @@ class ReplaceOne implements Executable ...@@ -62,6 +68,7 @@ class ReplaceOne 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
*/ */
public function execute(Server $server) public function execute(Server $server)
{ {
......
...@@ -7,6 +7,7 @@ use MongoDB\Driver\BulkWrite as Bulk; ...@@ -7,6 +7,7 @@ use MongoDB\Driver\BulkWrite as Bulk;
use MongoDB\Driver\Server; use MongoDB\Driver\Server;
use MongoDB\Driver\WriteConcern; use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/** /**
* Operation for the update command. * Operation for the update command.
...@@ -19,6 +20,7 @@ use MongoDB\Exception\InvalidArgumentException; ...@@ -19,6 +20,7 @@ use MongoDB\Exception\InvalidArgumentException;
*/ */
class Update implements Executable class Update implements Executable
{ {
private static $wireVersionForCollation = 5;
private static $wireVersionForDocumentLevelValidation = 4; private static $wireVersionForDocumentLevelValidation = 4;
private $databaseName; private $databaseName;
...@@ -35,6 +37,11 @@ class Update implements Executable ...@@ -35,6 +37,11 @@ class Update implements Executable
* * bypassDocumentValidation (boolean): If true, allows the write to opt * * bypassDocumentValidation (boolean): If true, allows the write to opt
* out of document level validation. * out of document level validation.
* *
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * multi (boolean): When true, updates all documents matching the query. * * multi (boolean): When true, updates all documents matching the query.
* This option cannot be true if the $update argument is a replacement * This option cannot be true if the $update argument is a replacement
* document (i.e. contains no update operators). The default is false. * document (i.e. contains no update operators). The default is false.
...@@ -71,6 +78,10 @@ class Update implements Executable ...@@ -71,6 +78,10 @@ class Update implements Executable
throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean'); throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean');
} }
if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) {
throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object');
}
if ( ! is_bool($options['multi'])) { if ( ! is_bool($options['multi'])) {
throw InvalidArgumentException::invalidType('"multi" option', $options['multi'], 'boolean'); throw InvalidArgumentException::invalidType('"multi" option', $options['multi'], 'boolean');
} }
...@@ -100,14 +111,23 @@ class Update implements Executable ...@@ -100,14 +111,23 @@ 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
*/ */
public function execute(Server $server) public function execute(Server $server)
{ {
if (isset($this->options['collation']) && ! \MongoDB\server_supports_feature($server, self::$wireVersionForCollation)) {
throw UnsupportedException::collationNotSupported();
}
$updateOptions = [ $updateOptions = [
'multi' => $this->options['multi'], 'multi' => $this->options['multi'],
'upsert' => $this->options['upsert'], 'upsert' => $this->options['upsert'],
]; ];
if (isset($this->options['collation'])) {
$updateOptions['collation'] = (object) $this->options['collation'];
}
$bulkOptions = []; $bulkOptions = [];
if (isset($this->options['bypassDocumentValidation']) && \MongoDB\server_supports_feature($server, self::$wireVersionForDocumentLevelValidation)) { if (isset($this->options['bypassDocumentValidation']) && \MongoDB\server_supports_feature($server, self::$wireVersionForDocumentLevelValidation)) {
......
...@@ -5,6 +5,7 @@ namespace MongoDB\Operation; ...@@ -5,6 +5,7 @@ namespace MongoDB\Operation;
use MongoDB\UpdateResult; use MongoDB\UpdateResult;
use MongoDB\Driver\Server; use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/** /**
* Operation for updating multiple documents with the update command. * Operation for updating multiple documents with the update command.
...@@ -25,6 +26,11 @@ class UpdateMany implements Executable ...@@ -25,6 +26,11 @@ class UpdateMany implements Executable
* * bypassDocumentValidation (boolean): If true, allows the write to opt * * bypassDocumentValidation (boolean): If true, allows the write to opt
* out of document level validation. * out of document level validation.
* *
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * 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.
* *
...@@ -62,6 +68,7 @@ class UpdateMany implements Executable ...@@ -62,6 +68,7 @@ class UpdateMany 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
*/ */
public function execute(Server $server) public function execute(Server $server)
{ {
......
...@@ -5,6 +5,7 @@ namespace MongoDB\Operation; ...@@ -5,6 +5,7 @@ namespace MongoDB\Operation;
use MongoDB\UpdateResult; use MongoDB\UpdateResult;
use MongoDB\Driver\Server; use MongoDB\Driver\Server;
use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Exception\UnsupportedException;
/** /**
* Operation for updating a single document with the update command. * Operation for updating a single document with the update command.
...@@ -25,6 +26,11 @@ class UpdateOne implements Executable ...@@ -25,6 +26,11 @@ class UpdateOne implements Executable
* * bypassDocumentValidation (boolean): If true, allows the write to opt * * bypassDocumentValidation (boolean): If true, allows the write to opt
* out of document level validation. * out of document level validation.
* *
* * collation (document): Collation specification.
*
* This is not supported for server versions < 3.4 and will result in an
* exception at execution time if used.
*
* * 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.
* *
...@@ -62,6 +68,7 @@ class UpdateOne implements Executable ...@@ -62,6 +68,7 @@ class UpdateOne 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
*/ */
public function execute(Server $server) public function execute(Server $server)
{ {
......
...@@ -49,6 +49,10 @@ class AggregateTest extends TestCase ...@@ -49,6 +49,10 @@ class AggregateTest extends TestCase
$options[][] = ['bypassDocumentValidation' => $value]; $options[][] = ['bypassDocumentValidation' => $value];
} }
foreach ($this->getInvalidDocumentValues() as $value) {
$options[][] = ['collation' => $value];
}
foreach ($this->getInvalidIntegerValues() as $value) { foreach ($this->getInvalidIntegerValues() as $value) {
$options[][] = ['maxTimeMS' => $value]; $options[][] = ['maxTimeMS' => $value];
} }
......
...@@ -97,6 +97,23 @@ class BulkWriteTest extends TestCase ...@@ -97,6 +97,23 @@ class BulkWriteTest extends TestCase
]); ]);
} }
/**
* @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessageRegExp /Expected \$operations\[0\]\["deleteMany"\]\[1\]\["collation"\] to have type "array or object" but found "[\w ]+"/
* @dataProvider provideInvalidDocumentValues
*/
public function testDeleteManyCollationOptionTypeCheck($collation)
{
new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [
[BulkWrite::DELETE_MANY => [['x' => 1], ['collation' => $collation]]],
]);
}
public function provideInvalidDocumentValues()
{
return $this->wrapValuesForDataProvider($this->getInvalidDocumentValues());
}
/** /**
* @expectedException MongoDB\Exception\InvalidArgumentException * @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessage Missing first argument for $operations[0]["deleteOne"] * @expectedExceptionMessage Missing first argument for $operations[0]["deleteOne"]
...@@ -120,6 +137,18 @@ class BulkWriteTest extends TestCase ...@@ -120,6 +137,18 @@ class BulkWriteTest extends TestCase
]); ]);
} }
/**
* @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessageRegExp /Expected \$operations\[0\]\["deleteOne"\]\[1\]\["collation"\] to have type "array or object" but found "[\w ]+"/
* @dataProvider provideInvalidDocumentValues
*/
public function testDeleteOneCollationOptionTypeCheck($collation)
{
new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [
[BulkWrite::DELETE_ONE => [['x' => 1], ['collation' => $collation]]],
]);
}
/** /**
* @expectedException MongoDB\Exception\InvalidArgumentException * @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessage Missing first argument for $operations[0]["replaceOne"] * @expectedExceptionMessage Missing first argument for $operations[0]["replaceOne"]
...@@ -177,6 +206,18 @@ class BulkWriteTest extends TestCase ...@@ -177,6 +206,18 @@ class BulkWriteTest extends TestCase
]); ]);
} }
/**
* @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessageRegExp /Expected \$operations\[0\]\["replaceOne"\]\[2\]\["collation"\] to have type "array or object" but found "[\w ]+"/
* @dataProvider provideInvalidDocumentValues
*/
public function testReplaceOneCollationOptionTypeCheck($collation)
{
new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [
[BulkWrite::REPLACE_ONE => [['x' => 1], ['y' => 1], ['collation' => $collation]]],
]);
}
/** /**
* @expectedException MongoDB\Exception\InvalidArgumentException * @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessageRegExp /Expected \$operations\[0\]\["replaceOne"\]\[2\]\["upsert"\] to have type "boolean" but found "[\w ]+"/ * @expectedExceptionMessageRegExp /Expected \$operations\[0\]\["replaceOne"\]\[2\]\["upsert"\] to have type "boolean" but found "[\w ]+"/
...@@ -189,6 +230,11 @@ class BulkWriteTest extends TestCase ...@@ -189,6 +230,11 @@ class BulkWriteTest extends TestCase
]); ]);
} }
public function provideInvalidBooleanValues()
{
return $this->wrapValuesForDataProvider($this->getInvalidBooleanValues());
}
/** /**
* @expectedException MongoDB\Exception\InvalidArgumentException * @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessage Missing first argument for $operations[0]["updateMany"] * @expectedExceptionMessage Missing first argument for $operations[0]["updateMany"]
...@@ -246,6 +292,18 @@ class BulkWriteTest extends TestCase ...@@ -246,6 +292,18 @@ class BulkWriteTest extends TestCase
]); ]);
} }
/**
* @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessageRegExp /Expected \$operations\[0\]\["updateMany"\]\[2\]\["collation"\] to have type "array or object" but found "[\w ]+"/
* @dataProvider provideInvalidDocumentValues
*/
public function testUpdateManyCollationOptionTypeCheck($collation)
{
new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [
[BulkWrite::UPDATE_MANY => [['x' => 1], ['$set' => ['x' => 1]], ['collation' => $collation]]],
]);
}
/** /**
* @expectedException MongoDB\Exception\InvalidArgumentException * @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessageRegExp /Expected \$operations\[0\]\["updateMany"\]\[2\]\["upsert"\] to have type "boolean" but found "[\w ]+"/ * @expectedExceptionMessageRegExp /Expected \$operations\[0\]\["updateMany"\]\[2\]\["upsert"\] to have type "boolean" but found "[\w ]+"/
...@@ -315,6 +373,18 @@ class BulkWriteTest extends TestCase ...@@ -315,6 +373,18 @@ class BulkWriteTest extends TestCase
]); ]);
} }
/**
* @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessageRegExp /Expected \$operations\[0\]\["updateOne"\]\[2\]\["collation"\] to have type "array or object" but found "[\w ]+"/
* @dataProvider provideInvalidDocumentValues
*/
public function testUpdateOneCollationOptionTypeCheck($collation)
{
new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [
[BulkWrite::UPDATE_ONE => [['x' => 1], ['$set' => ['x' => 1]], ['collation' => $collation]]],
]);
}
/** /**
* @expectedException MongoDB\Exception\InvalidArgumentException * @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedExceptionMessageRegExp /Expected \$operations\[0\]\["updateOne"\]\[2\]\["upsert"\] to have type "boolean" but found "[\w ]+"/ * @expectedExceptionMessageRegExp /Expected \$operations\[0\]\["updateOne"\]\[2\]\["upsert"\] to have type "boolean" but found "[\w ]+"/
...@@ -341,11 +411,6 @@ class BulkWriteTest extends TestCase ...@@ -341,11 +411,6 @@ class BulkWriteTest extends TestCase
); );
} }
public function provideInvalidBooleanValues()
{
return $this->wrapValuesForDataProvider($this->getInvalidBooleanValues());
}
public function provideInvalidConstructorOptions() public function provideInvalidConstructorOptions()
{ {
$options = []; $options = [];
......
...@@ -28,6 +28,10 @@ class CountTest extends TestCase ...@@ -28,6 +28,10 @@ class CountTest extends TestCase
{ {
$options = []; $options = [];
foreach ($this->getInvalidDocumentValues() as $value) {
$options[][] = ['collation' => $value];
}
foreach ($this->getInvalidHintValues() as $value) { foreach ($this->getInvalidHintValues() as $value) {
$options[][] = ['hint' => $value]; $options[][] = ['hint' => $value];
} }
......
...@@ -27,6 +27,10 @@ class CreateCollectionTest extends TestCase ...@@ -27,6 +27,10 @@ class CreateCollectionTest extends TestCase
$options[][] = ['capped' => $value]; $options[][] = ['capped' => $value];
} }
foreach ($this->getInvalidDocumentValues() as $value) {
$options[][] = ['collation' => $value];
}
foreach ($this->getInvalidIntegerValues() as $value) { foreach ($this->getInvalidIntegerValues() as $value) {
$options[][] = ['flags' => $value]; $options[][] = ['flags' => $value];
} }
......
...@@ -43,6 +43,10 @@ class DeleteTest extends TestCase ...@@ -43,6 +43,10 @@ class DeleteTest extends TestCase
{ {
$options = []; $options = [];
foreach ($this->getInvalidDocumentValues() as $value) {
$options[][] = ['collation' => $value];
}
foreach ($this->getInvalidWriteConcernValues() as $value) { foreach ($this->getInvalidWriteConcernValues() as $value) {
$options[][] = ['writeConcern' => $value]; $options[][] = ['writeConcern' => $value];
} }
......
...@@ -28,6 +28,10 @@ class DistinctTest extends TestCase ...@@ -28,6 +28,10 @@ class DistinctTest extends TestCase
{ {
$options = []; $options = [];
foreach ($this->getInvalidDocumentValues() as $value) {
$options[][] = ['collation' => $value];
}
foreach ($this->getInvalidIntegerValues() as $value) { foreach ($this->getInvalidIntegerValues() as $value) {
$options[][] = ['maxTimeMS' => $value]; $options[][] = ['maxTimeMS' => $value];
} }
......
...@@ -23,6 +23,10 @@ class FindAndModifyTest extends TestCase ...@@ -23,6 +23,10 @@ class FindAndModifyTest extends TestCase
$options[][] = ['bypassDocumentValidation' => $value]; $options[][] = ['bypassDocumentValidation' => $value];
} }
foreach ($this->getInvalidDocumentValues() as $value) {
$options[][] = ['collation' => $value];
}
foreach ($this->getInvalidDocumentValues() as $value) { foreach ($this->getInvalidDocumentValues() as $value) {
$options[][] = ['fields' => $value]; $options[][] = ['fields' => $value];
} }
......
...@@ -36,6 +36,10 @@ class FindTest extends TestCase ...@@ -36,6 +36,10 @@ class FindTest extends TestCase
$options[][] = ['batchSize' => $value]; $options[][] = ['batchSize' => $value];
} }
foreach ($this->getInvalidDocumentValues() as $value) {
$options[][] = ['collation' => $value];
}
foreach ($this->getInvalidStringValues() as $value) { foreach ($this->getInvalidStringValues() as $value) {
$options[][] = ['comment' => $value]; $options[][] = ['comment' => $value];
} }
......
...@@ -43,6 +43,10 @@ class UpdateTest extends TestCase ...@@ -43,6 +43,10 @@ class UpdateTest extends TestCase
$options[][] = ['bypassDocumentValidation' => $value]; $options[][] = ['bypassDocumentValidation' => $value];
} }
foreach ($this->getInvalidDocumentValues() as $value) {
$options[][] = ['collation' => $value];
}
foreach ($this->getInvalidBooleanValues() as $value) { foreach ($this->getInvalidBooleanValues() as $value) {
$options[][] = ['multi' => $value]; $options[][] = ['multi' => $value];
} }
......
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