Commit 324a20e4 authored by Jeremy Mikola's avatar Jeremy Mikola

PHPLIB-491: Support hint for update command

Spec tests from mongodb/specifications@60adafcffa07174ac54c6e8658bd37a3faf350c3
parent ee833334
...@@ -10,6 +10,15 @@ source: ...@@ -10,6 +10,15 @@ source:
file: apiargs-MongoDBCollection-common-option.yaml file: apiargs-MongoDBCollection-common-option.yaml
ref: collation ref: collation
--- ---
source:
file: apiargs-common-option.yaml
ref: hint
post: |
This option is available in MongoDB 4.2+ and will result in an exception at
execution time if specified for an older server version.
.. versionadded:: 1.6
---
source: source:
file: apiargs-common-option.yaml file: apiargs-common-option.yaml
ref: session ref: session
......
...@@ -16,6 +16,15 @@ source: ...@@ -16,6 +16,15 @@ source:
file: apiargs-MongoDBCollection-common-option.yaml file: apiargs-MongoDBCollection-common-option.yaml
ref: collation ref: collation
--- ---
source:
file: apiargs-common-option.yaml
ref: hint
post: |
This option is available in MongoDB 4.2+ and will result in an exception at
execution time if specified for an older server version.
.. versionadded:: 1.6
---
source: source:
file: apiargs-common-option.yaml file: apiargs-common-option.yaml
ref: session ref: session
......
...@@ -16,6 +16,15 @@ source: ...@@ -16,6 +16,15 @@ source:
file: apiargs-MongoDBCollection-common-option.yaml file: apiargs-MongoDBCollection-common-option.yaml
ref: collation ref: collation
--- ---
source:
file: apiargs-common-option.yaml
ref: hint
post: |
This option is available in MongoDB 4.2+ and will result in an exception at
execution time if specified for an older server version.
.. versionadded:: 1.6
---
source: source:
file: apiargs-common-option.yaml file: apiargs-common-option.yaml
ref: session ref: session
......
...@@ -49,6 +49,16 @@ class UnsupportedException extends RuntimeException ...@@ -49,6 +49,16 @@ class UnsupportedException extends RuntimeException
return new static('Explain is not supported by the server executing this operation'); return new static('Explain is not supported by the server executing this operation');
} }
/**
* Thrown when a command's hint option is not supported by a server.
*
* @return self
*/
public static function hintNotSupported()
{
return new static('Hint is not supported by the server executing this operation');
}
/** /**
* Thrown when a command's readConcern option is not supported by a server. * Thrown when a command's readConcern option is not supported by a server.
* *
......
...@@ -55,6 +55,13 @@ class ReplaceOne implements Executable ...@@ -55,6 +55,13 @@ class ReplaceOne implements Executable
* 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
* exception at execution time if used. * exception at execution time if used.
* *
* * hint (string|document): The index to use. Specify either the index
* name as a string or the index key pattern as a document. If specified,
* then the query system will only consider plans using the hinted index.
*
* This is not supported for server versions < 4.2 and will result in an
* exception at execution time if used.
*
* * session (MongoDB\Driver\Session): Client session. * * session (MongoDB\Driver\Session): Client session.
* *
* Sessions are not supported for server versions < 3.6. * Sessions are not supported for server versions < 3.6.
......
...@@ -28,6 +28,7 @@ use MongoDB\UpdateResult; ...@@ -28,6 +28,7 @@ use MongoDB\UpdateResult;
use function is_array; use function is_array;
use function is_bool; use function is_bool;
use function is_object; use function is_object;
use function is_string;
use function MongoDB\is_first_key_operator; use function MongoDB\is_first_key_operator;
use function MongoDB\is_pipeline; use function MongoDB\is_pipeline;
use function MongoDB\server_supports_feature; use function MongoDB\server_supports_feature;
...@@ -52,6 +53,9 @@ class Update implements Executable, Explainable ...@@ -52,6 +53,9 @@ class Update implements Executable, Explainable
/** @var integer */ /** @var integer */
private static $wireVersionForDocumentLevelValidation = 4; private static $wireVersionForDocumentLevelValidation = 4;
/** @var integer */
private static $wireVersionForHint = 8;
/** @var string */ /** @var string */
private $databaseName; private $databaseName;
...@@ -89,6 +93,13 @@ class Update implements Executable, Explainable ...@@ -89,6 +93,13 @@ class Update implements Executable, Explainable
* 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
* exception at execution time if used. * exception at execution time if used.
* *
* * hint (string|document): The index to use. Specify either the index
* name as a string or the index key pattern as a document. If specified,
* then the query system will only consider plans using the hinted index.
*
* This is not supported for server versions < 4.2 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.
...@@ -137,6 +148,10 @@ class Update implements Executable, Explainable ...@@ -137,6 +148,10 @@ class Update implements Executable, Explainable
throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object'); throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object');
} }
if (isset($options['hint']) && ! is_string($options['hint']) && ! is_array($options['hint']) && ! is_object($options['hint'])) {
throw InvalidArgumentException::invalidType('"hint" option', $options['hint'], ['string', 'array', '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');
} }
...@@ -187,6 +202,10 @@ class Update implements Executable, Explainable ...@@ -187,6 +202,10 @@ class Update implements Executable, Explainable
throw UnsupportedException::collationNotSupported(); throw UnsupportedException::collationNotSupported();
} }
if (isset($this->options['hint']) && ! server_supports_feature($server, self::$wireVersionForHint)) {
throw UnsupportedException::hintNotSupported();
}
$inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction();
if ($inTransaction && isset($this->options['writeConcern'])) { if ($inTransaction && isset($this->options['writeConcern'])) {
throw UnsupportedException::writeConcernNotSupportedInTransaction(); throw UnsupportedException::writeConcernNotSupportedInTransaction();
...@@ -261,8 +280,10 @@ class Update implements Executable, Explainable ...@@ -261,8 +280,10 @@ class Update implements Executable, Explainable
'upsert' => $this->options['upsert'], 'upsert' => $this->options['upsert'],
]; ];
if (isset($this->options['arrayFilters'])) { foreach (['arrayFilters', 'hint'] as $option) {
$updateOptions['arrayFilters'] = $this->options['arrayFilters']; if (isset($this->options[$option])) {
$updateOptions[$option] = $this->options[$option];
}
} }
if (isset($this->options['collation'])) { if (isset($this->options['collation'])) {
......
...@@ -61,6 +61,13 @@ class UpdateMany implements Executable, Explainable ...@@ -61,6 +61,13 @@ class UpdateMany implements Executable, Explainable
* 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
* exception at execution time if used. * exception at execution time if used.
* *
* * hint (string|document): The index to use. Specify either the index
* name as a string or the index key pattern as a document. If specified,
* then the query system will only consider plans using the hinted index.
*
* This is not supported for server versions < 4.2 and will result in an
* exception at execution time if used.
*
* * session (MongoDB\Driver\Session): Client session. * * session (MongoDB\Driver\Session): Client session.
* *
* Sessions are not supported for server versions < 3.6. * Sessions are not supported for server versions < 3.6.
......
...@@ -61,6 +61,13 @@ class UpdateOne implements Executable, Explainable ...@@ -61,6 +61,13 @@ class UpdateOne implements Executable, Explainable
* 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
* exception at execution time if used. * exception at execution time if used.
* *
* * hint (string|document): The index to use. Specify either the index
* name as a string or the index key pattern as a document. If specified,
* then the query system will only consider plans using the hinted index.
*
* This is not supported for server versions < 4.2 and will result in an
* exception at execution time if used.
*
* * session (MongoDB\Driver\Session): Client session. * * session (MongoDB\Driver\Session): Client session.
* *
* Sessions are not supported for server versions < 3.6. * Sessions are not supported for server versions < 3.6.
......
{
"runOn": [
{
"minServerVersion": "4.2.0"
}
],
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
},
{
"_id": 4,
"x": 44
}
],
"collection_name": "test_bulkwrite_update_hint",
"tests": [
{
"description": "BulkWrite with update hints",
"operations": [
{
"name": "bulkWrite",
"arguments": {
"requests": [
{
"name": "updateOne",
"arguments": {
"filter": {
"_id": 1
},
"update": {
"$inc": {
"x": 1
}
},
"hint": "_id_"
}
},
{
"name": "updateOne",
"arguments": {
"filter": {
"_id": 1
},
"update": {
"$inc": {
"x": 1
}
},
"hint": {
"_id": 1
}
}
},
{
"name": "updateMany",
"arguments": {
"filter": {
"_id": {
"$lt": 3
}
},
"update": {
"$inc": {
"x": 1
}
},
"hint": "_id_"
}
},
{
"name": "updateMany",
"arguments": {
"filter": {
"_id": {
"$lt": 3
}
},
"update": {
"$inc": {
"x": 1
}
},
"hint": {
"_id": 1
}
}
},
{
"name": "replaceOne",
"arguments": {
"filter": {
"_id": 3
},
"replacement": {
"x": 333
},
"hint": "_id_"
}
},
{
"name": "replaceOne",
"arguments": {
"filter": {
"_id": 4
},
"replacement": {
"x": 444
},
"hint": {
"_id": 1
}
}
}
],
"options": {
"ordered": true
}
},
"result": {
"deletedCount": 0,
"insertedCount": 0,
"insertedIds": {},
"matchedCount": 8,
"modifiedCount": 8,
"upsertedCount": 0,
"upsertedIds": {}
}
}
],
"expectations": [
{
"command_started_event": {
"command": {
"update": "test_bulkwrite_update_hint",
"updates": [
{
"q": {
"_id": 1
},
"u": {
"$inc": {
"x": 1
}
},
"hint": "_id_"
},
{
"q": {
"_id": 1
},
"u": {
"$inc": {
"x": 1
}
},
"hint": {
"_id": 1
}
},
{
"q": {
"_id": {
"$lt": 3
}
},
"u": {
"$inc": {
"x": 1
}
},
"multi": true,
"hint": "_id_"
},
{
"q": {
"_id": {
"$lt": 3
}
},
"u": {
"$inc": {
"x": 1
}
},
"multi": true,
"hint": {
"_id": 1
}
},
{
"q": {
"_id": 3
},
"u": {
"x": 333
},
"hint": "_id_"
},
{
"q": {
"_id": 4
},
"u": {
"x": 444
},
"hint": {
"_id": 1
}
}
],
"ordered": true
}
}
}
],
"outcome": {
"collection": {
"data": [
{
"_id": 1,
"x": 15
},
{
"_id": 2,
"x": 24
},
{
"_id": 3,
"x": 333
},
{
"_id": 4,
"x": 444
}
]
}
}
}
]
}
{
"runOn": [
{
"minServerVersion": "4.3.1"
}
],
"collection_name": "test_find_allowdiskuse",
"tests": [
{
"description": "Find does not send allowDiskuse when value is not specified",
"operations": [
{
"object": "collection",
"name": "find",
"arguments": {
"filter": {}
}
}
],
"expectations": [
{
"command_started_event": {
"command": {
"find": "test_find_allowdiskuse",
"allowDiskUse": null
}
}
}
]
},
{
"description": "Find sends allowDiskuse false when false is specified",
"operations": [
{
"object": "collection",
"name": "find",
"arguments": {
"filter": {},
"allowDiskUse": false
}
}
],
"expectations": [
{
"command_started_event": {
"command": {
"find": "test_find_allowdiskuse",
"allowDiskUse": false
}
}
}
]
},
{
"description": "Find sends allowDiskUse true when true is specified",
"operations": [
{
"object": "collection",
"name": "find",
"arguments": {
"filter": {},
"allowDiskUse": true
}
}
],
"expectations": [
{
"command_started_event": {
"command": {
"find": "test_find_allowdiskuse",
"allowDiskUse": true
}
}
}
]
}
]
}
\ No newline at end of file
{
"runOn": [
{
"minServerVersion": "4.2.0"
}
],
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
}
],
"collection_name": "test_replaceone_hint",
"tests": [
{
"description": "ReplaceOne with hint string",
"operations": [
{
"object": "collection",
"name": "replaceOne",
"arguments": {
"filter": {
"_id": {
"$gt": 1
}
},
"replacement": {
"x": 111
},
"hint": "_id_"
},
"result": {
"matchedCount": 1,
"modifiedCount": 1,
"upsertedCount": 0
}
}
],
"expectations": [
{
"command_started_event": {
"command": {
"update": "test_replaceone_hint",
"updates": [
{
"q": {
"_id": {
"$gt": 1
}
},
"u": {
"x": 111
},
"hint": "_id_"
}
]
}
}
}
],
"outcome": {
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 111
}
]
}
}
},
{
"description": "ReplaceOne with hint document",
"operations": [
{
"object": "collection",
"name": "replaceOne",
"arguments": {
"filter": {
"_id": {
"$gt": 1
}
},
"replacement": {
"x": 111
},
"hint": {
"_id": 1
}
},
"result": {
"matchedCount": 1,
"modifiedCount": 1,
"upsertedCount": 0
}
}
],
"expectations": [
{
"command_started_event": {
"command": {
"update": "test_replaceone_hint",
"updates": [
{
"q": {
"_id": {
"$gt": 1
}
},
"u": {
"x": 111
},
"hint": {
"_id": 1
}
}
]
}
}
}
],
"outcome": {
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 111
}
]
}
}
}
]
}
{
"runOn": [
{
"minServerVersion": "4.2.0"
}
],
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
],
"collection_name": "test_updatemany_hint",
"tests": [
{
"description": "UpdateMany with hint string",
"operations": [
{
"object": "collection",
"name": "updateMany",
"arguments": {
"filter": {
"_id": {
"$gt": 1
}
},
"update": {
"$inc": {
"x": 1
}
},
"hint": "_id_"
},
"result": {
"matchedCount": 2,
"modifiedCount": 2,
"upsertedCount": 0
}
}
],
"expectations": [
{
"command_started_event": {
"command": {
"update": "test_updatemany_hint",
"updates": [
{
"q": {
"_id": {
"$gt": 1
}
},
"u": {
"$inc": {
"x": 1
}
},
"multi": true,
"hint": "_id_"
}
]
}
}
}
],
"outcome": {
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 23
},
{
"_id": 3,
"x": 34
}
]
}
}
},
{
"description": "UpdateMany with hint document",
"operations": [
{
"object": "collection",
"name": "updateMany",
"arguments": {
"filter": {
"_id": {
"$gt": 1
}
},
"update": {
"$inc": {
"x": 1
}
},
"hint": {
"_id": 1
}
},
"result": {
"matchedCount": 2,
"modifiedCount": 2,
"upsertedCount": 0
}
}
],
"expectations": [
{
"command_started_event": {
"command": {
"update": "test_updatemany_hint",
"updates": [
{
"q": {
"_id": {
"$gt": 1
}
},
"u": {
"$inc": {
"x": 1
}
},
"multi": true,
"hint": {
"_id": 1
}
}
]
}
}
}
],
"outcome": {
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 23
},
{
"_id": 3,
"x": 34
}
]
}
}
}
]
}
{
"runOn": [
{
"minServerVersion": "4.2.0"
}
],
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
}
],
"collection_name": "test_updateone_hint",
"tests": [
{
"description": "UpdateOne with hint string",
"operations": [
{
"object": "collection",
"name": "updateOne",
"arguments": {
"filter": {
"_id": {
"$gt": 1
}
},
"update": {
"$inc": {
"x": 1
}
},
"hint": "_id_"
},
"result": {
"matchedCount": 1,
"modifiedCount": 1,
"upsertedCount": 0
}
}
],
"expectations": [
{
"command_started_event": {
"command": {
"update": "test_updateone_hint",
"updates": [
{
"q": {
"_id": {
"$gt": 1
}
},
"u": {
"$inc": {
"x": 1
}
},
"hint": "_id_"
}
]
}
}
}
],
"outcome": {
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 23
}
]
}
}
},
{
"description": "UpdateOne with hint document",
"operations": [
{
"object": "collection",
"name": "updateOne",
"arguments": {
"filter": {
"_id": {
"$gt": 1
}
},
"update": {
"$inc": {
"x": 1
}
},
"hint": {
"_id": 1
}
},
"result": {
"matchedCount": 1,
"modifiedCount": 1,
"upsertedCount": 0
}
}
],
"expectations": [
{
"command_started_event": {
"command": {
"update": "test_updateone_hint",
"updates": [
{
"q": {
"_id": {
"$gt": 1
}
},
"u": {
"$inc": {
"x": 1
}
},
"hint": {
"_id": 1
}
}
]
}
}
}
],
"outcome": {
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 23
}
]
}
}
}
]
}
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