Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 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\Cursor; 21 use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; 22 use MongoDB\Driver\Query; 23 use MongoDB\Driver\ReadConcern; 24 use MongoDB\Driver\ReadPreference; 25 use MongoDB\Driver\Server; 26 use MongoDB\Driver\Session; 27 use MongoDB\Exception\InvalidArgumentException; 28 use MongoDB\Exception\UnsupportedException; 29 use function is_array; 30 use function is_bool; 31 use function is_integer; 32 use function is_object; 33 use function is_string; 34 use function MongoDB\server_supports_feature; 35 use function trigger_error; 36 use const E_USER_DEPRECATED; 37 38 /** 39 * Operation for the find command. 40 * 41 * @api 42 * @see \MongoDB\Collection::find() 43 * @see http://docs.mongodb.org/manual/tutorial/query-documents/ 44 * @see http://docs.mongodb.org/manual/reference/operator/query-modifier/ 45 */ 46 class Find implements Executable, Explainable 47 { 48 const NON_TAILABLE = 1; 49 const TAILABLE = 2; 50 const TAILABLE_AWAIT = 3; 51 52 /** @var integer */ 53 private static $wireVersionForCollation = 5; 54 55 /** @var integer */ 56 private static $wireVersionForReadConcern = 4; 57 58 /** @var string */ 59 private $databaseName; 60 61 /** @var string */ 62 private $collectionName; 63 64 /** @var array|object */ 65 private $filter; 66 67 /** @var array */ 68 private $options; 69 70 /** 71 * Constructs a find command. 72 * 73 * Supported options: 74 * 75 * * allowPartialResults (boolean): Get partial results from a mongos if 76 * some shards are inaccessible (instead of throwing an error). 77 * 78 * * batchSize (integer): The number of documents to return per batch. 79 * 80 * * collation (document): Collation specification. 81 * 82 * This is not supported for server versions < 3.4 and will result in an 83 * exception at execution time if used. 84 * 85 * * comment (string): Attaches a comment to the query. If "$comment" also 86 * exists in the modifiers document, this option will take precedence. 87 * 88 * * cursorType (enum): Indicates the type of cursor to use. Must be either 89 * NON_TAILABLE, TAILABLE, or TAILABLE_AWAIT. The default is 90 * NON_TAILABLE. 91 * 92 * * hint (string|document): The index to use. Specify either the index 93 * name as a string or the index key pattern as a document. If specified, 94 * then the query system will only consider plans using the hinted index. 95 * 96 * * limit (integer): The maximum number of documents to return. 97 * 98 * * max (document): The exclusive upper bound for a specific index. 99 * 100 * * maxAwaitTimeMS (integer): The maxium amount of time for the server to wait 101 * on new documents to satisfy a query, if cursorType is TAILABLE_AWAIT. 102 * 103 * * maxScan (integer): Maximum number of documents or index keys to scan 104 * when executing the query. 105 * 106 * This option has been deprecated since version 1.4. 107 * 108 * * maxTimeMS (integer): The maximum amount of time to allow the query to 109 * run. If "$maxTimeMS" also exists in the modifiers document, this 110 * option will take precedence. 111 * 112 * * min (document): The inclusive upper bound for a specific index. 113 * 114 * * modifiers (document): Meta operators that modify the output or 115 * behavior of a query. Use of these operators is deprecated in favor of 116 * named options. 117 * 118 * * noCursorTimeout (boolean): The server normally times out idle cursors 119 * after an inactivity period (10 minutes) to prevent excess memory use. 120 * Set this option to prevent that. 121 * 122 * * oplogReplay (boolean): Internal replication use only. The driver 123 * should not set this. 124 * 125 * * projection (document): Limits the fields to return for the matching 126 * document. 127 * 128 * * readConcern (MongoDB\Driver\ReadConcern): Read concern. 129 * 130 * This is not supported for server versions < 3.2 and will result in an 131 * exception at execution time if used. 132 * 133 * * readPreference (MongoDB\Driver\ReadPreference): Read preference. 134 * 135 * * returnKey (boolean): If true, returns only the index keys in the 136 * resulting documents. 137 * 138 * * session (MongoDB\Driver\Session): Client session. 139 * 140 * Sessions are not supported for server versions < 3.6. 141 * 142 * * showRecordId (boolean): Determines whether to return the record 143 * identifier for each document. If true, adds a field $recordId to the 144 * returned documents. 145 * 146 * * skip (integer): The number of documents to skip before returning. 147 * 148 * * snapshot (boolean): Prevents the cursor from returning a document more 149 * than once because of an intervening write operation. 150 * 151 * This options has been deprecated since version 1.4. 152 * 153 * * sort (document): The order in which to return matching documents. If 154 * "$orderby" also exists in the modifiers document, this option will 155 * take precedence. 156 * 157 * * typeMap (array): Type map for BSON deserialization. This will be 158 * applied to the returned Cursor (it is not sent to the server). 159 * 160 * @param string $databaseName Database name 161 * @param string $collectionName Collection name 162 * @param array|object $filter Query by which to filter documents 163 * @param array $options Command options 164 * @throws InvalidArgumentException for parameter/option parsing errors 165 */ 166 public function __construct($databaseName, $collectionName, $filter, array $options = []) 167 { 168 if (! is_array($filter) && ! is_object($filter)) { 169 throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object'); 170 } 171 172 if (isset($options['allowPartialResults']) && ! is_bool($options['allowPartialResults'])) { 173 throw InvalidArgumentException::invalidType('"allowPartialResults" option', $options['allowPartialResults'], 'boolean'); 174 } 175 176 if (isset($options['batchSize']) && ! is_integer($options['batchSize'])) { 177 throw InvalidArgumentException::invalidType('"batchSize" option', $options['batchSize'], 'integer'); 178 } 179 180 if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) { 181 throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object'); 182 } 183 184 if (isset($options['comment']) && ! is_string($options['comment'])) { 185 throw InvalidArgumentException::invalidType('"comment" option', $options['comment'], 'comment'); 186 } 187 188 if (isset($options['cursorType'])) { 189 if (! is_integer($options['cursorType'])) { 190 throw InvalidArgumentException::invalidType('"cursorType" option', $options['cursorType'], 'integer'); 191 } 192 193 if ($options['cursorType'] !== self::NON_TAILABLE && 194 $options['cursorType'] !== self::TAILABLE && 195 $options['cursorType'] !== self::TAILABLE_AWAIT) { 196 throw new InvalidArgumentException('Invalid value for "cursorType" option: ' . $options['cursorType']); 197 } 198 } 199 200 if (isset($options['hint']) && ! is_string($options['hint']) && ! is_array($options['hint']) && ! is_object($options['hint'])) { 201 throw InvalidArgumentException::invalidType('"hint" option', $options['hint'], 'string or array or object'); 202 } 203 204 if (isset($options['limit']) && ! is_integer($options['limit'])) { 205 throw InvalidArgumentException::invalidType('"limit" option', $options['limit'], 'integer'); 206 } 207 208 if (isset($options['max']) && ! is_array($options['max']) && ! is_object($options['max'])) { 209 throw InvalidArgumentException::invalidType('"max" option', $options['max'], 'array or object'); 210 } 211 212 if (isset($options['maxAwaitTimeMS']) && ! is_integer($options['maxAwaitTimeMS'])) { 213 throw InvalidArgumentException::invalidType('"maxAwaitTimeMS" option', $options['maxAwaitTimeMS'], 'integer'); 214 } 215 216 if (isset($options['maxScan']) && ! is_integer($options['maxScan'])) { 217 throw InvalidArgumentException::invalidType('"maxScan" option', $options['maxScan'], 'integer'); 218 } 219 220 if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) { 221 throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer'); 222 } 223 224 if (isset($options['min']) && ! is_array($options['min']) && ! is_object($options['min'])) { 225 throw InvalidArgumentException::invalidType('"min" option', $options['min'], 'array or object'); 226 } 227 228 if (isset($options['modifiers']) && ! is_array($options['modifiers']) && ! is_object($options['modifiers'])) { 229 throw InvalidArgumentException::invalidType('"modifiers" option', $options['modifiers'], 'array or object'); 230 } 231 232 if (isset($options['noCursorTimeout']) && ! is_bool($options['noCursorTimeout'])) { 233 throw InvalidArgumentException::invalidType('"noCursorTimeout" option', $options['noCursorTimeout'], 'boolean'); 234 } 235 236 if (isset($options['oplogReplay']) && ! is_bool($options['oplogReplay'])) { 237 throw InvalidArgumentException::invalidType('"oplogReplay" option', $options['oplogReplay'], 'boolean'); 238 } 239 240 if (isset($options['projection']) && ! is_array($options['projection']) && ! is_object($options['projection'])) { 241 throw InvalidArgumentException::invalidType('"projection" option', $options['projection'], 'array or object'); 242 } 243 244 if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) { 245 throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class); 246 } 247 248 if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) { 249 throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class); 250 } 251 252 if (isset($options['returnKey']) && ! is_bool($options['returnKey'])) { 253 throw InvalidArgumentException::invalidType('"returnKey" option', $options['returnKey'], 'boolean'); 254 } 255 256 if (isset($options['session']) && ! $options['session'] instanceof Session) { 257 throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); 258 } 259 260 if (isset($options['showRecordId']) && ! is_bool($options['showRecordId'])) { 261 throw InvalidArgumentException::invalidType('"showRecordId" option', $options['showRecordId'], 'boolean'); 262 } 263 264 if (isset($options['skip']) && ! is_integer($options['skip'])) { 265 throw InvalidArgumentException::invalidType('"skip" option', $options['skip'], 'integer'); 266 } 267 268 if (isset($options['snapshot']) && ! is_bool($options['snapshot'])) { 269 throw InvalidArgumentException::invalidType('"snapshot" option', $options['snapshot'], 'boolean'); 270 } 271 272 if (isset($options['sort']) && ! is_array($options['sort']) && ! is_object($options['sort'])) { 273 throw InvalidArgumentException::invalidType('"sort" option', $options['sort'], 'array or object'); 274 } 275 276 if (isset($options['typeMap']) && ! is_array($options['typeMap'])) { 277 throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array'); 278 } 279 280 if (isset($options['readConcern']) && $options['readConcern']->isDefault()) { 281 unset($options['readConcern']); 282 } 283 284 if (isset($options['snapshot'])) { 285 trigger_error('The "snapshot" option is deprecated and will be removed in a future release', E_USER_DEPRECATED); 286 } 287 288 if (isset($options['maxScan'])) { 289 trigger_error('The "maxScan" option is deprecated and will be removed in a future release', E_USER_DEPRECATED); 290 } 291 292 $this->databaseName = (string) $databaseName; 293 $this->collectionName = (string) $collectionName; 294 $this->filter = $filter; 295 $this->options = $options; 296 } 297 298 /** 299 * Execute the operation. 300 * 301 * @see Executable::execute() 302 * @param Server $server 303 * @return Cursor 304 * @throws UnsupportedException if collation or read concern is used and unsupported 305 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 306 */ 307 public function execute(Server $server) 308 { 309 if (isset($this->options['collation']) && ! server_supports_feature($server, self::$wireVersionForCollation)) { 310 throw UnsupportedException::collationNotSupported(); 311 } 312 313 if (isset($this->options['readConcern']) && ! server_supports_feature($server, self::$wireVersionForReadConcern)) { 314 throw UnsupportedException::readConcernNotSupported(); 315 } 316 317 $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); 318 if ($inTransaction && isset($this->options['readConcern'])) { 319 throw UnsupportedException::readConcernNotSupportedInTransaction(); 320 } 321 322 $cursor = $server->executeQuery($this->databaseName . '.' . $this->collectionName, new Query($this->filter, $this->createQueryOptions()), $this->createExecuteOptions()); 323 324 if (isset($this->options['typeMap'])) { 325 $cursor->setTypeMap($this->options['typeMap']); 326 } 327 328 return $cursor; 329 } 330 331 public function getCommandDocument(Server $server) 332 { 333 return $this->createCommandDocument(); 334 } 335 336 /** 337 * Construct a command document for Find 338 */ 339 private function createCommandDocument() 340 { 341 $cmd = ['find' => $this->collectionName, 'filter' => (object) $this->filter]; 342 343 $options = $this->createQueryOptions(); 344 345 if (empty($options)) { 346 return $cmd; 347 } 348 349 // maxAwaitTimeMS is a Query level option so should not be considered here 350 unset($options['maxAwaitTimeMS']); 351 352 $modifierFallback = [ 353 ['allowPartialResults', 'partial'], 354 ['comment', '$comment'], 355 ['hint', '$hint'], 356 ['maxScan', '$maxScan'], 357 ['max', '$max'], 358 ['maxTimeMS', '$maxTimeMS'], 359 ['min', '$min'], 360 ['returnKey', '$returnKey'], 361 ['showRecordId', '$showDiskLoc'], 362 ['sort', '$orderby'], 363 ['snapshot', '$snapshot'], 364 ]; 365 366 foreach ($modifierFallback as $modifier) { 367 if (! isset($options[$modifier[0]]) && isset($options['modifiers'][$modifier[1]])) { 368 $options[$modifier[0]] = $options['modifiers'][$modifier[1]]; 369 } 370 } 371 unset($options['modifiers']); 372 373 return $cmd + $options; 374 } 375 376 /** 377 * Create options for executing the command. 378 * 379 * @see http://php.net/manual/en/mongodb-driver-server.executequery.php 380 * @return array 381 */ 382 private function createExecuteOptions() 383 { 384 $options = []; 385 386 if (isset($this->options['readPreference'])) { 387 $options['readPreference'] = $this->options['readPreference']; 388 } 389 390 if (isset($this->options['session'])) { 391 $options['session'] = $this->options['session']; 392 } 393 394 return $options; 395 } 396 397 /** 398 * Create options for the find query. 399 * 400 * Note that these are separate from the options for executing the command, 401 * which are created in createExecuteOptions(). 402 * 403 * @return array 404 */ 405 private function createQueryOptions() 406 { 407 $options = []; 408 409 if (isset($this->options['cursorType'])) { 410 if ($this->options['cursorType'] === self::TAILABLE) { 411 $options['tailable'] = true; 412 } 413 if ($this->options['cursorType'] === self::TAILABLE_AWAIT) { 414 $options['tailable'] = true; 415 $options['awaitData'] = true; 416 } 417 } 418 419 foreach (['allowPartialResults', 'batchSize', 'comment', 'hint', 'limit', 'maxAwaitTimeMS', 'maxScan', 'maxTimeMS', 'noCursorTimeout', 'oplogReplay', 'projection', 'readConcern', 'returnKey', 'showRecordId', 'skip', 'snapshot', 'sort'] as $option) { 420 if (isset($this->options[$option])) { 421 $options[$option] = $this->options[$option]; 422 } 423 } 424 425 foreach (['collation', 'max', 'min'] as $option) { 426 if (isset($this->options[$option])) { 427 $options[$option] = (object) $this->options[$option]; 428 } 429 } 430 431 $modifiers = empty($this->options['modifiers']) ? [] : (array) $this->options['modifiers']; 432 433 if (! empty($modifiers)) { 434 $options['modifiers'] = $modifiers; 435 } 436 437 return $options; 438 } 439 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body