Differences Between: [Versions 310 and 400] [Versions 39 and 400] [Versions 400 and 401]
1 <?php 2 /* 3 * Copyright 2015-2017 MongoDB, Inc. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 namespace MongoDB\Operation; 19 20 use MongoDB\Driver\BulkWrite as Bulk; 21 use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; 22 use MongoDB\Driver\Server; 23 use MongoDB\Driver\Session; 24 use MongoDB\Driver\WriteConcern; 25 use MongoDB\Exception\InvalidArgumentException; 26 use MongoDB\Exception\UnsupportedException; 27 use MongoDB\UpdateResult; 28 use function is_array; 29 use function is_bool; 30 use function is_object; 31 use function is_string; 32 use function MongoDB\is_first_key_operator; 33 use function MongoDB\is_pipeline; 34 use function MongoDB\server_supports_feature; 35 36 /** 37 * Operation for the update command. 38 * 39 * This class is used internally by the ReplaceOne, UpdateMany, and UpdateOne 40 * operation classes. 41 * 42 * @internal 43 * @see http://docs.mongodb.org/manual/reference/command/update/ 44 */ 45 class Update implements Executable, Explainable 46 { 47 /** @var integer */ 48 private static $wireVersionForArrayFilters = 6; 49 50 /** @var integer */ 51 private static $wireVersionForCollation = 5; 52 53 /** @var integer */ 54 private static $wireVersionForDocumentLevelValidation = 4; 55 56 /** @var integer */ 57 private static $wireVersionForHintServerSideError = 5; 58 59 /** @var string */ 60 private $databaseName; 61 62 /** @var string */ 63 private $collectionName; 64 65 /** @var array|object */ 66 private $filter; 67 68 /** @var array|object */ 69 private $update; 70 71 /** @var array */ 72 private $options; 73 74 /** 75 * Constructs a update command. 76 * 77 * Supported options: 78 * 79 * * arrayFilters (document array): A set of filters specifying to which 80 * array elements an update should apply. 81 * 82 * This is not supported for server versions < 3.6 and will result in an 83 * exception at execution time if used. 84 * 85 * * bypassDocumentValidation (boolean): If true, allows the write to 86 * circumvent document level validation. 87 * 88 * For servers < 3.2, this option is ignored as document level validation 89 * is not available. 90 * 91 * * collation (document): Collation specification. 92 * 93 * This is not supported for server versions < 3.4 and will result in an 94 * exception at execution time if used. 95 * 96 * * hint (string|document): The index to use. Specify either the index 97 * name as a string or the index key pattern as a document. If specified, 98 * then the query system will only consider plans using the hinted index. 99 * 100 * This is not supported for server versions < 4.2 and will result in an 101 * exception at execution time if used. 102 * 103 * * multi (boolean): When true, updates all documents matching the query. 104 * This option cannot be true if the $update argument is a replacement 105 * document (i.e. contains no update operators). The default is false. 106 * 107 * * session (MongoDB\Driver\Session): Client session. 108 * 109 * Sessions are not supported for server versions < 3.6. 110 * 111 * * upsert (boolean): When true, a new document is created if no document 112 * matches the query. The default is false. 113 * 114 * * writeConcern (MongoDB\Driver\WriteConcern): Write concern. 115 * 116 * @param string $databaseName Database name 117 * @param string $collectionName Collection name 118 * @param array|object $filter Query by which to delete documents 119 * @param array|object $update Update to apply to the matched 120 * document(s) or a replacement document 121 * @param array $options Command options 122 * @throws InvalidArgumentException for parameter/option parsing errors 123 */ 124 public function __construct($databaseName, $collectionName, $filter, $update, array $options = []) 125 { 126 if (! is_array($filter) && ! is_object($filter)) { 127 throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object'); 128 } 129 130 if (! is_array($update) && ! is_object($update)) { 131 throw InvalidArgumentException::invalidType('$update', $filter, 'array or object'); 132 } 133 134 $options += [ 135 'multi' => false, 136 'upsert' => false, 137 ]; 138 139 if (isset($options['arrayFilters']) && ! is_array($options['arrayFilters'])) { 140 throw InvalidArgumentException::invalidType('"arrayFilters" option', $options['arrayFilters'], 'array'); 141 } 142 143 if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) { 144 throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean'); 145 } 146 147 if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) { 148 throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object'); 149 } 150 151 if (isset($options['hint']) && ! is_string($options['hint']) && ! is_array($options['hint']) && ! is_object($options['hint'])) { 152 throw InvalidArgumentException::invalidType('"hint" option', $options['hint'], ['string', 'array', 'object']); 153 } 154 155 if (! is_bool($options['multi'])) { 156 throw InvalidArgumentException::invalidType('"multi" option', $options['multi'], 'boolean'); 157 } 158 159 if ($options['multi'] && ! is_first_key_operator($update) && ! is_pipeline($update)) { 160 throw new InvalidArgumentException('"multi" option cannot be true if $update is a replacement document'); 161 } 162 163 if (isset($options['session']) && ! $options['session'] instanceof Session) { 164 throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); 165 } 166 167 if (! is_bool($options['upsert'])) { 168 throw InvalidArgumentException::invalidType('"upsert" option', $options['upsert'], 'boolean'); 169 } 170 171 if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) { 172 throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); 173 } 174 175 if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) { 176 unset($options['writeConcern']); 177 } 178 179 $this->databaseName = (string) $databaseName; 180 $this->collectionName = (string) $collectionName; 181 $this->filter = $filter; 182 $this->update = $update; 183 $this->options = $options; 184 } 185 186 /** 187 * Execute the operation. 188 * 189 * @see Executable::execute() 190 * @param Server $server 191 * @return UpdateResult 192 * @throws UnsupportedException if array filters or collation is used and unsupported 193 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 194 */ 195 public function execute(Server $server) 196 { 197 if (isset($this->options['arrayFilters']) && ! server_supports_feature($server, self::$wireVersionForArrayFilters)) { 198 throw UnsupportedException::arrayFiltersNotSupported(); 199 } 200 201 if (isset($this->options['collation']) && ! server_supports_feature($server, self::$wireVersionForCollation)) { 202 throw UnsupportedException::collationNotSupported(); 203 } 204 205 /* Server versions >= 3.4.0 raise errors for unknown update 206 * options. For previous versions, the CRUD spec requires a client-side 207 * error. */ 208 if (isset($this->options['hint']) && ! server_supports_feature($server, self::$wireVersionForHintServerSideError)) { 209 throw UnsupportedException::hintNotSupported(); 210 } 211 212 $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); 213 if ($inTransaction && isset($this->options['writeConcern'])) { 214 throw UnsupportedException::writeConcernNotSupportedInTransaction(); 215 } 216 217 $bulkOptions = []; 218 219 if (! empty($this->options['bypassDocumentValidation']) && 220 server_supports_feature($server, self::$wireVersionForDocumentLevelValidation) 221 ) { 222 $bulkOptions['bypassDocumentValidation'] = $this->options['bypassDocumentValidation']; 223 } 224 225 $bulk = new Bulk($bulkOptions); 226 $bulk->update($this->filter, $this->update, $this->createUpdateOptions()); 227 228 $writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $this->createExecuteOptions()); 229 230 return new UpdateResult($writeResult); 231 } 232 233 public function getCommandDocument(Server $server) 234 { 235 $cmd = ['update' => $this->collectionName, 'updates' => [['q' => $this->filter, 'u' => $this->update] + $this->createUpdateOptions()]]; 236 237 if (isset($this->options['writeConcern'])) { 238 $cmd['writeConcern'] = $this->options['writeConcern']; 239 } 240 241 if (! empty($this->options['bypassDocumentValidation']) && 242 server_supports_feature($server, self::$wireVersionForDocumentLevelValidation) 243 ) { 244 $cmd['bypassDocumentValidation'] = $this->options['bypassDocumentValidation']; 245 } 246 247 return $cmd; 248 } 249 250 /** 251 * Create options for executing the bulk write. 252 * 253 * @see http://php.net/manual/en/mongodb-driver-server.executebulkwrite.php 254 * @return array 255 */ 256 private function createExecuteOptions() 257 { 258 $options = []; 259 260 if (isset($this->options['session'])) { 261 $options['session'] = $this->options['session']; 262 } 263 264 if (isset($this->options['writeConcern'])) { 265 $options['writeConcern'] = $this->options['writeConcern']; 266 } 267 268 return $options; 269 } 270 271 /** 272 * Create options for the update command. 273 * 274 * Note that these options are different from the bulk write options, which 275 * are created in createExecuteOptions(). 276 * 277 * @return array 278 */ 279 private function createUpdateOptions() 280 { 281 $updateOptions = [ 282 'multi' => $this->options['multi'], 283 'upsert' => $this->options['upsert'], 284 ]; 285 286 foreach (['arrayFilters', 'hint'] as $option) { 287 if (isset($this->options[$option])) { 288 $updateOptions[$option] = $this->options[$option]; 289 } 290 } 291 292 if (isset($this->options['collation'])) { 293 $updateOptions['collation'] = (object) $this->options['collation']; 294 } 295 296 return $updateOptions; 297 } 298 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body