See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401]
1 <?php 2 /* 3 * Copyright 2015-present 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 * https://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 29 use function is_array; 30 use function is_bool; 31 use function is_object; 32 use function is_string; 33 use function MongoDB\is_first_key_operator; 34 use function MongoDB\is_pipeline; 35 use function MongoDB\is_write_concern_acknowledged; 36 use function MongoDB\server_supports_feature; 37 38 /** 39 * Operation for the update command. 40 * 41 * This class is used internally by the ReplaceOne, UpdateMany, and UpdateOne 42 * operation classes. 43 * 44 * @internal 45 * @see https://mongodb.com/docs/manual/reference/command/update/ 46 */ 47 class Update implements Executable, Explainable 48 { 49 /** @var integer */ 50 private static $wireVersionForHint = 8; 51 52 /** @var string */ 53 private $databaseName; 54 55 /** @var string */ 56 private $collectionName; 57 58 /** @var array|object */ 59 private $filter; 60 61 /** @var array|object */ 62 private $update; 63 64 /** @var array */ 65 private $options; 66 67 /** 68 * Constructs a update command. 69 * 70 * Supported options: 71 * 72 * * arrayFilters (document array): A set of filters specifying to which 73 * array elements an update should apply. 74 * 75 * * bypassDocumentValidation (boolean): If true, allows the write to 76 * circumvent document level validation. 77 * 78 * * collation (document): Collation specification. 79 * 80 * * comment (mixed): BSON value to attach as a comment to this command. 81 * 82 * This is not supported for servers versions < 4.4. 83 * 84 * * hint (string|document): The index to use. Specify either the index 85 * name as a string or the index key pattern as a document. If specified, 86 * then the query system will only consider plans using the hinted index. 87 * 88 * This is not supported for server versions < 4.2 and will result in an 89 * exception at execution time if used. 90 * 91 * * multi (boolean): When true, updates all documents matching the query. 92 * This option cannot be true if the $update argument is a replacement 93 * document (i.e. contains no update operators). The default is false. 94 * 95 * * session (MongoDB\Driver\Session): Client session. 96 * 97 * * upsert (boolean): When true, a new document is created if no document 98 * matches the query. The default is false. 99 * 100 * * let (document): Map of parameter names and values. Values must be 101 * constant or closed expressions that do not reference document fields. 102 * Parameters can then be accessed as variables in an aggregate 103 * expression context (e.g. "$$var"). 104 * 105 * * writeConcern (MongoDB\Driver\WriteConcern): Write concern. 106 * 107 * @param string $databaseName Database name 108 * @param string $collectionName Collection name 109 * @param array|object $filter Query by which to delete documents 110 * @param array|object $update Update to apply to the matched 111 * document(s) or a replacement document 112 * @param array $options Command options 113 * @throws InvalidArgumentException for parameter/option parsing errors 114 */ 115 public function __construct(string $databaseName, string $collectionName, $filter, $update, array $options = []) 116 { 117 if (! is_array($filter) && ! is_object($filter)) { 118 throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object'); 119 } 120 121 if (! is_array($update) && ! is_object($update)) { 122 throw InvalidArgumentException::invalidType('$update', $filter, 'array or object'); 123 } 124 125 $options += [ 126 'multi' => false, 127 'upsert' => false, 128 ]; 129 130 if (isset($options['arrayFilters']) && ! is_array($options['arrayFilters'])) { 131 throw InvalidArgumentException::invalidType('"arrayFilters" option', $options['arrayFilters'], 'array'); 132 } 133 134 if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) { 135 throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean'); 136 } 137 138 if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) { 139 throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object'); 140 } 141 142 if (isset($options['hint']) && ! is_string($options['hint']) && ! is_array($options['hint']) && ! is_object($options['hint'])) { 143 throw InvalidArgumentException::invalidType('"hint" option', $options['hint'], ['string', 'array', 'object']); 144 } 145 146 if (! is_bool($options['multi'])) { 147 throw InvalidArgumentException::invalidType('"multi" option', $options['multi'], 'boolean'); 148 } 149 150 if ($options['multi'] && ! is_first_key_operator($update) && ! is_pipeline($update)) { 151 throw new InvalidArgumentException('"multi" option cannot be true if $update is a replacement document'); 152 } 153 154 if (isset($options['session']) && ! $options['session'] instanceof Session) { 155 throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); 156 } 157 158 if (! is_bool($options['upsert'])) { 159 throw InvalidArgumentException::invalidType('"upsert" option', $options['upsert'], 'boolean'); 160 } 161 162 if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) { 163 throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); 164 } 165 166 if (isset($options['let']) && ! is_array($options['let']) && ! is_object($options['let'])) { 167 throw InvalidArgumentException::invalidType('"let" option', $options['let'], 'array or object'); 168 } 169 170 if (isset($options['bypassDocumentValidation']) && ! $options['bypassDocumentValidation']) { 171 unset($options['bypassDocumentValidation']); 172 } 173 174 if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) { 175 unset($options['writeConcern']); 176 } 177 178 $this->databaseName = $databaseName; 179 $this->collectionName = $collectionName; 180 $this->filter = $filter; 181 $this->update = $update; 182 $this->options = $options; 183 } 184 185 /** 186 * Execute the operation. 187 * 188 * @see Executable::execute() 189 * @return UpdateResult 190 * @throws UnsupportedException if hint or write concern is used and unsupported 191 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 192 */ 193 public function execute(Server $server) 194 { 195 /* CRUD spec requires a client-side error when using "hint" with an 196 * unacknowledged write concern on an unsupported server. */ 197 if ( 198 isset($this->options['writeConcern']) && ! is_write_concern_acknowledged($this->options['writeConcern']) && 199 isset($this->options['hint']) && ! server_supports_feature($server, self::$wireVersionForHint) 200 ) { 201 throw UnsupportedException::hintNotSupported(); 202 } 203 204 $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); 205 if ($inTransaction && isset($this->options['writeConcern'])) { 206 throw UnsupportedException::writeConcernNotSupportedInTransaction(); 207 } 208 209 $bulk = new Bulk($this->createBulkWriteOptions()); 210 $bulk->update($this->filter, $this->update, $this->createUpdateOptions()); 211 212 $writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $this->createExecuteOptions()); 213 214 return new UpdateResult($writeResult); 215 } 216 217 /** 218 * Returns the command document for this operation. 219 * 220 * @see Explainable::getCommandDocument() 221 * @return array 222 */ 223 public function getCommandDocument(Server $server) 224 { 225 $cmd = ['update' => $this->collectionName, 'updates' => [['q' => $this->filter, 'u' => $this->update] + $this->createUpdateOptions()]]; 226 227 if (isset($this->options['bypassDocumentValidation'])) { 228 $cmd['bypassDocumentValidation'] = $this->options['bypassDocumentValidation']; 229 } 230 231 if (isset($this->options['writeConcern'])) { 232 $cmd['writeConcern'] = $this->options['writeConcern']; 233 } 234 235 return $cmd; 236 } 237 238 /** 239 * Create options for constructing the bulk write. 240 * 241 * @see https://php.net/manual/en/mongodb-driver-bulkwrite.construct.php 242 */ 243 private function createBulkWriteOptions(): array 244 { 245 $options = []; 246 247 foreach (['bypassDocumentValidation', 'comment'] as $option) { 248 if (isset($this->options[$option])) { 249 $options[$option] = $this->options[$option]; 250 } 251 } 252 253 if (isset($this->options['let'])) { 254 $options['let'] = (object) $this->options['let']; 255 } 256 257 return $options; 258 } 259 260 /** 261 * Create options for executing the bulk write. 262 * 263 * @see https://php.net/manual/en/mongodb-driver-server.executebulkwrite.php 264 */ 265 private function createExecuteOptions(): array 266 { 267 $options = []; 268 269 if (isset($this->options['session'])) { 270 $options['session'] = $this->options['session']; 271 } 272 273 if (isset($this->options['writeConcern'])) { 274 $options['writeConcern'] = $this->options['writeConcern']; 275 } 276 277 return $options; 278 } 279 280 /** 281 * Create options for the update command. 282 * 283 * Note that these options are different from the bulk write options, which 284 * are created in createExecuteOptions(). 285 */ 286 private function createUpdateOptions(): array 287 { 288 $updateOptions = [ 289 'multi' => $this->options['multi'], 290 'upsert' => $this->options['upsert'], 291 ]; 292 293 foreach (['arrayFilters', 'hint'] as $option) { 294 if (isset($this->options[$option])) { 295 $updateOptions[$option] = $this->options[$option]; 296 } 297 } 298 299 if (isset($this->options['collation'])) { 300 $updateOptions['collation'] = (object) $this->options['collation']; 301 } 302 303 return $updateOptions; 304 } 305 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body