Skip to content

Commit b6ed827

Browse files
committed
PHPLIB-497: Allow passing hint to findAndModify update/replace ops
1 parent 239e9fa commit b6ed827

9 files changed

+744
-6
lines changed

src/Operation/FindAndModify.php

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
use function is_bool;
3131
use function is_integer;
3232
use function is_object;
33+
use function is_string;
3334
use function MongoDB\create_field_path_type_map;
3435
use function MongoDB\is_pipeline;
3536
use function MongoDB\server_supports_feature;
@@ -54,6 +55,9 @@ class FindAndModify implements Executable, Explainable
5455
/** @var integer */
5556
private static $wireVersionForDocumentLevelValidation = 4;
5657

58+
/** @var integer */
59+
private static $wireVersionForHintServerSideError = 8;
60+
5761
/** @var integer */
5862
private static $wireVersionForWriteConcern = 4;
5963

@@ -91,6 +95,14 @@ class FindAndModify implements Executable, Explainable
9195
* * fields (document): Limits the fields to return for the matching
9296
* document.
9397
*
98+
* * hint (string|document): The index to use. Specify either the index
99+
* name as a string or the index key pattern as a document. If specified,
100+
* then the query system will only consider plans using the hinted index.
101+
*
102+
* This is only supported for update and replace operations (i.e. remove
103+
* option is false) on server versions >= 4.4. Using this option in
104+
* other contexts will result in an exception at execution time.
105+
*
94106
* * maxTimeMS (integer): The maximum amount of time to allow the query to
95107
* run.
96108
*
@@ -153,6 +165,10 @@ public function __construct($databaseName, $collectionName, array $options)
153165
throw InvalidArgumentException::invalidType('"fields" option', $options['fields'], 'array or object');
154166
}
155167

168+
if (isset($options['hint']) && ! is_string($options['hint']) && ! is_array($options['hint']) && ! is_object($options['hint'])) {
169+
throw InvalidArgumentException::invalidType('"hint" option', $options['hint'], ['string', 'array', 'object']);
170+
}
171+
156172
if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) {
157173
throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer');
158174
}
@@ -226,6 +242,14 @@ public function execute(Server $server)
226242
throw UnsupportedException::collationNotSupported();
227243
}
228244

245+
/* Server versions >= 4.1.10 raise errors for unknown findAndModify
246+
* options (SERVER-40005), but the CRUD spec requires client-side errors
247+
* for server versions < 4.2. For later versions, we'll rely on the
248+
* server to either utilize the option or report its own error. */
249+
if (isset($this->options['hint']) && ! server_supports_feature($server, self::$wireVersionForHintServerSideError)) {
250+
throw UnsupportedException::hintNotSupported();
251+
}
252+
229253
if (isset($this->options['writeConcern']) && ! server_supports_feature($server, self::$wireVersionForWriteConcern)) {
230254
throw UnsupportedException::writeConcernNotSupported();
231255
}
@@ -280,12 +304,10 @@ private function createCommandDocument(Server $server)
280304
: (object) $this->options['update'];
281305
}
282306

283-
if (isset($this->options['arrayFilters'])) {
284-
$cmd['arrayFilters'] = $this->options['arrayFilters'];
285-
}
286-
287-
if (isset($this->options['maxTimeMS'])) {
288-
$cmd['maxTimeMS'] = $this->options['maxTimeMS'];
307+
foreach (['arrayFilters', 'hint', 'maxTimeMS'] as $option) {
308+
if (isset($this->options[$option])) {
309+
$cmd[$option] = $this->options[$option];
310+
}
289311
}
290312

291313
if (! empty($this->options['bypassDocumentValidation']) &&

src/Operation/FindOneAndReplace.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ class FindOneAndReplace implements Executable, Explainable
5757
* This is not supported for server versions < 3.4 and will result in an
5858
* exception at execution time if used.
5959
*
60+
* * hint (string|document): The index to use. Specify either the index
61+
* name as a string or the index key pattern as a document. If specified,
62+
* then the query system will only consider plans using the hinted index.
63+
*
64+
* This is not supported for server versions < 4.4 and will result in an
65+
* exception at execution time if used.
66+
*
6067
* * maxTimeMS (integer): The maximum amount of time to allow the query to
6168
* run.
6269
*

src/Operation/FindOneAndUpdate.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ class FindOneAndUpdate implements Executable, Explainable
6161
* This is not supported for server versions < 3.4 and will result in an
6262
* exception at execution time if used.
6363
*
64+
* * hint (string|document): The index to use. Specify either the index
65+
* name as a string or the index key pattern as a document. If specified,
66+
* then the query system will only consider plans using the hinted index.
67+
*
68+
* This is not supported for server versions < 4.4 and will result in an
69+
* exception at execution time if used.
70+
*
6471
* * maxTimeMS (integer): The maximum amount of time to allow the query to
6572
* run.
6673
*
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
{
2+
"runOn": [
3+
{
4+
"maxServerVersion": "4.0.99"
5+
}
6+
],
7+
"data": [
8+
{
9+
"_id": 1,
10+
"x": 11
11+
},
12+
{
13+
"_id": 2,
14+
"x": 22
15+
}
16+
],
17+
"collection_name": "findOneAndReplace_hint",
18+
"tests": [
19+
{
20+
"description": "FindOneAndReplace with hint string unsupported (client-side error)",
21+
"operations": [
22+
{
23+
"object": "collection",
24+
"name": "findOneAndReplace",
25+
"arguments": {
26+
"filter": {
27+
"_id": 1
28+
},
29+
"replacement": {
30+
"x": 33
31+
},
32+
"hint": "_id_"
33+
},
34+
"error": true
35+
}
36+
],
37+
"expectations": [],
38+
"outcome": {
39+
"collection": {
40+
"data": [
41+
{
42+
"_id": 1,
43+
"x": 11
44+
},
45+
{
46+
"_id": 2,
47+
"x": 22
48+
}
49+
]
50+
}
51+
}
52+
},
53+
{
54+
"description": "FindOneAndReplace with hint document unsupported (client-side error)",
55+
"operations": [
56+
{
57+
"object": "collection",
58+
"name": "findOneAndReplace",
59+
"arguments": {
60+
"filter": {
61+
"_id": 1
62+
},
63+
"replacement": {
64+
"x": 33
65+
},
66+
"hint": {
67+
"_id": 1
68+
}
69+
},
70+
"error": true
71+
}
72+
],
73+
"expectations": [],
74+
"outcome": {
75+
"collection": {
76+
"data": [
77+
{
78+
"_id": 1,
79+
"x": 11
80+
},
81+
{
82+
"_id": 2,
83+
"x": 22
84+
}
85+
]
86+
}
87+
}
88+
}
89+
]
90+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
{
2+
"runOn": [
3+
{
4+
"minServerVersion": "4.2.0",
5+
"maxServerVersion": "4.3.0"
6+
}
7+
],
8+
"data": [
9+
{
10+
"_id": 1,
11+
"x": 11
12+
},
13+
{
14+
"_id": 2,
15+
"x": 22
16+
}
17+
],
18+
"collection_name": "findOneAndReplace_hint",
19+
"tests": [
20+
{
21+
"description": "FindOneAndReplace with hint string unsupported (server-side error)",
22+
"operations": [
23+
{
24+
"object": "collection",
25+
"name": "findOneAndReplace",
26+
"arguments": {
27+
"filter": {
28+
"_id": 1
29+
},
30+
"replacement": {
31+
"x": 33
32+
},
33+
"hint": "_id_"
34+
},
35+
"error": true
36+
}
37+
],
38+
"expectations": [
39+
{
40+
"command_started_event": {
41+
"command": {
42+
"findAndModify": "findOneAndReplace_hint",
43+
"query": {
44+
"_id": 1
45+
},
46+
"update": {
47+
"x": 33
48+
},
49+
"hint": "_id_"
50+
}
51+
}
52+
}
53+
],
54+
"outcome": {
55+
"collection": {
56+
"data": [
57+
{
58+
"_id": 1,
59+
"x": 11
60+
},
61+
{
62+
"_id": 2,
63+
"x": 22
64+
}
65+
]
66+
}
67+
}
68+
},
69+
{
70+
"description": "FindOneAndReplace with hint document unsupported (server-side error)",
71+
"operations": [
72+
{
73+
"object": "collection",
74+
"name": "findOneAndReplace",
75+
"arguments": {
76+
"filter": {
77+
"_id": 1
78+
},
79+
"replacement": {
80+
"x": 33
81+
},
82+
"hint": {
83+
"_id": 1
84+
}
85+
},
86+
"error": true
87+
}
88+
],
89+
"expectations": [
90+
{
91+
"command_started_event": {
92+
"command": {
93+
"findAndModify": "findOneAndReplace_hint",
94+
"query": {
95+
"_id": 1
96+
},
97+
"update": {
98+
"x": 33
99+
},
100+
"hint": {
101+
"_id": 1
102+
}
103+
}
104+
}
105+
}
106+
],
107+
"outcome": {
108+
"collection": {
109+
"data": [
110+
{
111+
"_id": 1,
112+
"x": 11
113+
},
114+
{
115+
"_id": 2,
116+
"x": 22
117+
}
118+
]
119+
}
120+
}
121+
}
122+
]
123+
}

0 commit comments

Comments
 (0)