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 18namespace MongoDB; 19 20use MongoDB\BSON\JavascriptInterface; 21use MongoDB\Driver\Cursor; 22use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; 23use MongoDB\Driver\Manager; 24use MongoDB\Driver\ReadConcern; 25use MongoDB\Driver\ReadPreference; 26use MongoDB\Driver\WriteConcern; 27use MongoDB\Exception\InvalidArgumentException; 28use MongoDB\Exception\UnexpectedValueException; 29use MongoDB\Exception\UnsupportedException; 30use MongoDB\Model\BSONArray; 31use MongoDB\Model\BSONDocument; 32use MongoDB\Model\IndexInfo; 33use MongoDB\Model\IndexInfoIterator; 34use MongoDB\Operation\Aggregate; 35use MongoDB\Operation\BulkWrite; 36use MongoDB\Operation\Count; 37use MongoDB\Operation\CountDocuments; 38use MongoDB\Operation\CreateIndexes; 39use MongoDB\Operation\DeleteMany; 40use MongoDB\Operation\DeleteOne; 41use MongoDB\Operation\Distinct; 42use MongoDB\Operation\DropCollection; 43use MongoDB\Operation\DropIndexes; 44use MongoDB\Operation\EstimatedDocumentCount; 45use MongoDB\Operation\Explain; 46use MongoDB\Operation\Explainable; 47use MongoDB\Operation\Find; 48use MongoDB\Operation\FindOne; 49use MongoDB\Operation\FindOneAndDelete; 50use MongoDB\Operation\FindOneAndReplace; 51use MongoDB\Operation\FindOneAndUpdate; 52use MongoDB\Operation\InsertMany; 53use MongoDB\Operation\InsertOne; 54use MongoDB\Operation\ListIndexes; 55use MongoDB\Operation\MapReduce; 56use MongoDB\Operation\ReplaceOne; 57use MongoDB\Operation\UpdateMany; 58use MongoDB\Operation\UpdateOne; 59use MongoDB\Operation\Watch; 60use Traversable; 61use function array_diff_key; 62use function array_intersect_key; 63use function current; 64use function is_array; 65use function strlen; 66 67class Collection 68{ 69 /** @var array */ 70 private static $defaultTypeMap = [ 71 'array' => BSONArray::class, 72 'document' => BSONDocument::class, 73 'root' => BSONDocument::class, 74 ]; 75 76 /** @var integer */ 77 private static $wireVersionForFindAndModifyWriteConcern = 4; 78 79 /** @var integer */ 80 private static $wireVersionForReadConcern = 4; 81 82 /** @var integer */ 83 private static $wireVersionForWritableCommandWriteConcern = 5; 84 85 /** @var integer */ 86 private static $wireVersionForReadConcernWithWriteStage = 8; 87 88 /** @var string */ 89 private $collectionName; 90 91 /** @var string */ 92 private $databaseName; 93 94 /** @var Manager */ 95 private $manager; 96 97 /** @var ReadConcern */ 98 private $readConcern; 99 100 /** @var ReadPreference */ 101 private $readPreference; 102 103 /** @var array */ 104 private $typeMap; 105 106 /** @var WriteConcern */ 107 private $writeConcern; 108 109 /** 110 * Constructs new Collection instance. 111 * 112 * This class provides methods for collection-specific operations, such as 113 * CRUD (i.e. create, read, update, and delete) and index management. 114 * 115 * Supported options: 116 * 117 * * readConcern (MongoDB\Driver\ReadConcern): The default read concern to 118 * use for collection operations. Defaults to the Manager's read concern. 119 * 120 * * readPreference (MongoDB\Driver\ReadPreference): The default read 121 * preference to use for collection operations. Defaults to the Manager's 122 * read preference. 123 * 124 * * typeMap (array): Default type map for cursors and BSON documents. 125 * 126 * * writeConcern (MongoDB\Driver\WriteConcern): The default write concern 127 * to use for collection operations. Defaults to the Manager's write 128 * concern. 129 * 130 * @param Manager $manager Manager instance from the driver 131 * @param string $databaseName Database name 132 * @param string $collectionName Collection name 133 * @param array $options Collection options 134 * @throws InvalidArgumentException for parameter/option parsing errors 135 */ 136 public function __construct(Manager $manager, $databaseName, $collectionName, array $options = []) 137 { 138 if (strlen($databaseName) < 1) { 139 throw new InvalidArgumentException('$databaseName is invalid: ' . $databaseName); 140 } 141 142 if (strlen($collectionName) < 1) { 143 throw new InvalidArgumentException('$collectionName is invalid: ' . $collectionName); 144 } 145 146 if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) { 147 throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class); 148 } 149 150 if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) { 151 throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class); 152 } 153 154 if (isset($options['typeMap']) && ! is_array($options['typeMap'])) { 155 throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array'); 156 } 157 158 if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) { 159 throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); 160 } 161 162 $this->manager = $manager; 163 $this->databaseName = (string) $databaseName; 164 $this->collectionName = (string) $collectionName; 165 $this->readConcern = $options['readConcern'] ?? $this->manager->getReadConcern(); 166 $this->readPreference = $options['readPreference'] ?? $this->manager->getReadPreference(); 167 $this->typeMap = $options['typeMap'] ?? self::$defaultTypeMap; 168 $this->writeConcern = $options['writeConcern'] ?? $this->manager->getWriteConcern(); 169 } 170 171 /** 172 * Return internal properties for debugging purposes. 173 * 174 * @see http://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo 175 * @return array 176 */ 177 public function __debugInfo() 178 { 179 return [ 180 'collectionName' => $this->collectionName, 181 'databaseName' => $this->databaseName, 182 'manager' => $this->manager, 183 'readConcern' => $this->readConcern, 184 'readPreference' => $this->readPreference, 185 'typeMap' => $this->typeMap, 186 'writeConcern' => $this->writeConcern, 187 ]; 188 } 189 190 /** 191 * Return the collection namespace (e.g. "db.collection"). 192 * 193 * @see https://docs.mongodb.org/manual/faq/developers/#faq-dev-namespace 194 * @return string 195 */ 196 public function __toString() 197 { 198 return $this->databaseName . '.' . $this->collectionName; 199 } 200 201 /** 202 * Executes an aggregation framework pipeline on the collection. 203 * 204 * Note: this method's return value depends on the MongoDB server version 205 * and the "useCursor" option. If "useCursor" is true, a Cursor will be 206 * returned; otherwise, an ArrayIterator is returned, which wraps the 207 * "result" array from the command response document. 208 * 209 * @see Aggregate::__construct() for supported options 210 * @param array $pipeline List of pipeline operations 211 * @param array $options Command options 212 * @return Traversable 213 * @throws UnexpectedValueException if the command response was malformed 214 * @throws UnsupportedException if options are not supported by the selected server 215 * @throws InvalidArgumentException for parameter/option parsing errors 216 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 217 */ 218 public function aggregate(array $pipeline, array $options = []) 219 { 220 $hasWriteStage = is_last_pipeline_operator_write($pipeline); 221 222 if (! isset($options['readPreference']) && ! is_in_transaction($options)) { 223 $options['readPreference'] = $this->readPreference; 224 } 225 226 if ($hasWriteStage) { 227 $options['readPreference'] = new ReadPreference(ReadPreference::RP_PRIMARY); 228 } 229 230 $server = select_server($this->manager, $options); 231 232 /* MongoDB 4.2 and later supports a read concern when an $out stage is 233 * being used, but earlier versions do not. 234 * 235 * A read concern is also not compatible with transactions. 236 */ 237 if (! isset($options['readConcern']) && 238 server_supports_feature($server, self::$wireVersionForReadConcern) && 239 ! is_in_transaction($options) && 240 ( ! $hasWriteStage || server_supports_feature($server, self::$wireVersionForReadConcernWithWriteStage)) 241 ) { 242 $options['readConcern'] = $this->readConcern; 243 } 244 245 if (! isset($options['typeMap'])) { 246 $options['typeMap'] = $this->typeMap; 247 } 248 249 if ($hasWriteStage && 250 ! isset($options['writeConcern']) && 251 server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern) && 252 ! is_in_transaction($options)) { 253 $options['writeConcern'] = $this->writeConcern; 254 } 255 256 $operation = new Aggregate($this->databaseName, $this->collectionName, $pipeline, $options); 257 258 return $operation->execute($server); 259 } 260 261 /** 262 * Executes multiple write operations. 263 * 264 * @see BulkWrite::__construct() for supported options 265 * @param array[] $operations List of write operations 266 * @param array $options Command options 267 * @return BulkWriteResult 268 * @throws UnsupportedException if options are not supported by the selected server 269 * @throws InvalidArgumentException for parameter/option parsing errors 270 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 271 */ 272 public function bulkWrite(array $operations, array $options = []) 273 { 274 if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { 275 $options['writeConcern'] = $this->writeConcern; 276 } 277 278 $operation = new BulkWrite($this->databaseName, $this->collectionName, $operations, $options); 279 $server = select_server($this->manager, $options); 280 281 return $operation->execute($server); 282 } 283 284 /** 285 * Gets the number of documents matching the filter. 286 * 287 * @see Count::__construct() for supported options 288 * @param array|object $filter Query by which to filter documents 289 * @param array $options Command options 290 * @return integer 291 * @throws UnexpectedValueException if the command response was malformed 292 * @throws UnsupportedException if options are not supported by the selected server 293 * @throws InvalidArgumentException for parameter/option parsing errors 294 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 295 * 296 * @deprecated 1.4 297 */ 298 public function count($filter = [], array $options = []) 299 { 300 if (! isset($options['readPreference']) && ! is_in_transaction($options)) { 301 $options['readPreference'] = $this->readPreference; 302 } 303 304 $server = select_server($this->manager, $options); 305 306 if (! isset($options['readConcern']) && server_supports_feature($server, self::$wireVersionForReadConcern) && ! is_in_transaction($options)) { 307 $options['readConcern'] = $this->readConcern; 308 } 309 310 $operation = new Count($this->databaseName, $this->collectionName, $filter, $options); 311 312 return $operation->execute($server); 313 } 314 315 /** 316 * Gets the number of documents matching the filter. 317 * 318 * @see CountDocuments::__construct() for supported options 319 * @param array|object $filter Query by which to filter documents 320 * @param array $options Command options 321 * @return integer 322 * @throws UnexpectedValueException if the command response was malformed 323 * @throws UnsupportedException if options are not supported by the selected server 324 * @throws InvalidArgumentException for parameter/option parsing errors 325 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 326 */ 327 public function countDocuments($filter = [], array $options = []) 328 { 329 if (! isset($options['readPreference']) && ! is_in_transaction($options)) { 330 $options['readPreference'] = $this->readPreference; 331 } 332 333 $server = select_server($this->manager, $options); 334 335 if (! isset($options['readConcern']) && server_supports_feature($server, self::$wireVersionForReadConcern) && ! is_in_transaction($options)) { 336 $options['readConcern'] = $this->readConcern; 337 } 338 339 $operation = new CountDocuments($this->databaseName, $this->collectionName, $filter, $options); 340 341 return $operation->execute($server); 342 } 343 344 /** 345 * Create a single index for the collection. 346 * 347 * @see Collection::createIndexes() 348 * @see CreateIndexes::__construct() for supported command options 349 * @param array|object $key Document containing fields mapped to values, 350 * which denote order or an index type 351 * @param array $options Index and command options 352 * @return string The name of the created index 353 * @throws UnsupportedException if options are not supported by the selected server 354 * @throws InvalidArgumentException for parameter/option parsing errors 355 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 356 */ 357 public function createIndex($key, array $options = []) 358 { 359 $commandOptionKeys = ['commitQuorum' => 1, 'maxTimeMS' => 1, 'session' => 1, 'writeConcern' => 1]; 360 $indexOptions = array_diff_key($options, $commandOptionKeys); 361 $commandOptions = array_intersect_key($options, $commandOptionKeys); 362 363 return current($this->createIndexes([['key' => $key] + $indexOptions], $commandOptions)); 364 } 365 366 /** 367 * Create one or more indexes for the collection. 368 * 369 * Each element in the $indexes array must have a "key" document, which 370 * contains fields mapped to an order or type. Other options may follow. 371 * For example: 372 * 373 * $indexes = [ 374 * // Create a unique index on the "username" field 375 * [ 'key' => [ 'username' => 1 ], 'unique' => true ], 376 * // Create a 2dsphere index on the "loc" field with a custom name 377 * [ 'key' => [ 'loc' => '2dsphere' ], 'name' => 'geo' ], 378 * ]; 379 * 380 * If the "name" option is unspecified, a name will be generated from the 381 * "key" document. 382 * 383 * @see http://docs.mongodb.org/manual/reference/command/createIndexes/ 384 * @see http://docs.mongodb.org/manual/reference/method/db.collection.createIndex/ 385 * @see CreateIndexes::__construct() for supported command options 386 * @param array[] $indexes List of index specifications 387 * @param array $options Command options 388 * @return string[] The names of the created indexes 389 * @throws UnsupportedException if options are not supported by the selected server 390 * @throws InvalidArgumentException for parameter/option parsing errors 391 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 392 */ 393 public function createIndexes(array $indexes, array $options = []) 394 { 395 $server = select_server($this->manager, $options); 396 397 if (! isset($options['writeConcern']) && server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern) && ! is_in_transaction($options)) { 398 $options['writeConcern'] = $this->writeConcern; 399 } 400 401 $operation = new CreateIndexes($this->databaseName, $this->collectionName, $indexes, $options); 402 403 return $operation->execute($server); 404 } 405 406 /** 407 * Deletes all documents matching the filter. 408 * 409 * @see DeleteMany::__construct() for supported options 410 * @see http://docs.mongodb.org/manual/reference/command/delete/ 411 * @param array|object $filter Query by which to delete documents 412 * @param array $options Command options 413 * @return DeleteResult 414 * @throws UnsupportedException if options are not supported by the selected server 415 * @throws InvalidArgumentException for parameter/option parsing errors 416 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 417 */ 418 public function deleteMany($filter, array $options = []) 419 { 420 if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { 421 $options['writeConcern'] = $this->writeConcern; 422 } 423 424 $operation = new DeleteMany($this->databaseName, $this->collectionName, $filter, $options); 425 $server = select_server($this->manager, $options); 426 427 return $operation->execute($server); 428 } 429 430 /** 431 * Deletes at most one document matching the filter. 432 * 433 * @see DeleteOne::__construct() for supported options 434 * @see http://docs.mongodb.org/manual/reference/command/delete/ 435 * @param array|object $filter Query by which to delete documents 436 * @param array $options Command options 437 * @return DeleteResult 438 * @throws UnsupportedException if options are not supported by the selected server 439 * @throws InvalidArgumentException for parameter/option parsing errors 440 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 441 */ 442 public function deleteOne($filter, array $options = []) 443 { 444 if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { 445 $options['writeConcern'] = $this->writeConcern; 446 } 447 448 $operation = new DeleteOne($this->databaseName, $this->collectionName, $filter, $options); 449 $server = select_server($this->manager, $options); 450 451 return $operation->execute($server); 452 } 453 454 /** 455 * Finds the distinct values for a specified field across the collection. 456 * 457 * @see Distinct::__construct() for supported options 458 * @param string $fieldName Field for which to return distinct values 459 * @param array|object $filter Query by which to filter documents 460 * @param array $options Command options 461 * @return mixed[] 462 * @throws UnexpectedValueException if the command response was malformed 463 * @throws UnsupportedException if options are not supported by the selected server 464 * @throws InvalidArgumentException for parameter/option parsing errors 465 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 466 */ 467 public function distinct($fieldName, $filter = [], array $options = []) 468 { 469 if (! isset($options['readPreference']) && ! is_in_transaction($options)) { 470 $options['readPreference'] = $this->readPreference; 471 } 472 473 if (! isset($options['typeMap'])) { 474 $options['typeMap'] = $this->typeMap; 475 } 476 477 $server = select_server($this->manager, $options); 478 479 if (! isset($options['readConcern']) && server_supports_feature($server, self::$wireVersionForReadConcern) && ! is_in_transaction($options)) { 480 $options['readConcern'] = $this->readConcern; 481 } 482 483 $operation = new Distinct($this->databaseName, $this->collectionName, $fieldName, $filter, $options); 484 485 return $operation->execute($server); 486 } 487 488 /** 489 * Drop this collection. 490 * 491 * @see DropCollection::__construct() for supported options 492 * @param array $options Additional options 493 * @return array|object Command result document 494 * @throws UnsupportedException if options are not supported by the selected server 495 * @throws InvalidArgumentException for parameter/option parsing errors 496 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 497 */ 498 public function drop(array $options = []) 499 { 500 if (! isset($options['typeMap'])) { 501 $options['typeMap'] = $this->typeMap; 502 } 503 504 $server = select_server($this->manager, $options); 505 506 if (! isset($options['writeConcern']) && server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern) && ! is_in_transaction($options)) { 507 $options['writeConcern'] = $this->writeConcern; 508 } 509 510 $operation = new DropCollection($this->databaseName, $this->collectionName, $options); 511 512 return $operation->execute($server); 513 } 514 515 /** 516 * Drop a single index in the collection. 517 * 518 * @see DropIndexes::__construct() for supported options 519 * @param string|IndexInfo $indexName Index name or model object 520 * @param array $options Additional options 521 * @return array|object Command result document 522 * @throws UnsupportedException if options are not supported by the selected server 523 * @throws InvalidArgumentException for parameter/option parsing errors 524 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 525 */ 526 public function dropIndex($indexName, array $options = []) 527 { 528 $indexName = (string) $indexName; 529 530 if ($indexName === '*') { 531 throw new InvalidArgumentException('dropIndexes() must be used to drop multiple indexes'); 532 } 533 534 if (! isset($options['typeMap'])) { 535 $options['typeMap'] = $this->typeMap; 536 } 537 538 $server = select_server($this->manager, $options); 539 540 if (! isset($options['writeConcern']) && server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern) && ! is_in_transaction($options)) { 541 $options['writeConcern'] = $this->writeConcern; 542 } 543 544 $operation = new DropIndexes($this->databaseName, $this->collectionName, $indexName, $options); 545 546 return $operation->execute($server); 547 } 548 549 /** 550 * Drop all indexes in the collection. 551 * 552 * @see DropIndexes::__construct() for supported options 553 * @param array $options Additional options 554 * @return array|object Command result document 555 * @throws UnsupportedException if options are not supported by the selected server 556 * @throws InvalidArgumentException for parameter/option parsing errors 557 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 558 */ 559 public function dropIndexes(array $options = []) 560 { 561 if (! isset($options['typeMap'])) { 562 $options['typeMap'] = $this->typeMap; 563 } 564 565 $server = select_server($this->manager, $options); 566 567 if (! isset($options['writeConcern']) && server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern) && ! is_in_transaction($options)) { 568 $options['writeConcern'] = $this->writeConcern; 569 } 570 571 $operation = new DropIndexes($this->databaseName, $this->collectionName, '*', $options); 572 573 return $operation->execute($server); 574 } 575 576 /** 577 * Gets an estimated number of documents in the collection using the collection metadata. 578 * 579 * @see EstimatedDocumentCount::__construct() for supported options 580 * @param array $options Command options 581 * @return integer 582 * @throws UnexpectedValueException if the command response was malformed 583 * @throws UnsupportedException if options are not supported by the selected server 584 * @throws InvalidArgumentException for parameter/option parsing errors 585 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 586 */ 587 public function estimatedDocumentCount(array $options = []) 588 { 589 if (! isset($options['readPreference']) && ! is_in_transaction($options)) { 590 $options['readPreference'] = $this->readPreference; 591 } 592 593 $server = select_server($this->manager, $options); 594 595 if (! isset($options['readConcern']) && server_supports_feature($server, self::$wireVersionForReadConcern) && ! is_in_transaction($options)) { 596 $options['readConcern'] = $this->readConcern; 597 } 598 599 $operation = new EstimatedDocumentCount($this->databaseName, $this->collectionName, $options); 600 601 return $operation->execute($server); 602 } 603 604 /** 605 * Explains explainable commands. 606 * 607 * @see Explain::__construct() for supported options 608 * @see http://docs.mongodb.org/manual/reference/command/explain/ 609 * @param Explainable $explainable Command on which to run explain 610 * @param array $options Additional options 611 * @return array|object 612 * @throws UnsupportedException if explainable or options are not supported by the selected server 613 * @throws InvalidArgumentException for parameter/option parsing errors 614 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 615 */ 616 public function explain(Explainable $explainable, array $options = []) 617 { 618 if (! isset($options['readPreference']) && ! is_in_transaction($options)) { 619 $options['readPreference'] = $this->readPreference; 620 } 621 622 if (! isset($options['typeMap'])) { 623 $options['typeMap'] = $this->typeMap; 624 } 625 626 $server = select_server($this->manager, $options); 627 628 $operation = new Explain($this->databaseName, $explainable, $options); 629 630 return $operation->execute($server); 631 } 632 633 /** 634 * Finds documents matching the query. 635 * 636 * @see Find::__construct() for supported options 637 * @see http://docs.mongodb.org/manual/core/read-operations-introduction/ 638 * @param array|object $filter Query by which to filter documents 639 * @param array $options Additional options 640 * @return Cursor 641 * @throws UnsupportedException if options are not supported by the selected server 642 * @throws InvalidArgumentException for parameter/option parsing errors 643 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 644 */ 645 public function find($filter = [], array $options = []) 646 { 647 if (! isset($options['readPreference']) && ! is_in_transaction($options)) { 648 $options['readPreference'] = $this->readPreference; 649 } 650 651 $server = select_server($this->manager, $options); 652 653 if (! isset($options['readConcern']) && server_supports_feature($server, self::$wireVersionForReadConcern) && ! is_in_transaction($options)) { 654 $options['readConcern'] = $this->readConcern; 655 } 656 657 if (! isset($options['typeMap'])) { 658 $options['typeMap'] = $this->typeMap; 659 } 660 661 $operation = new Find($this->databaseName, $this->collectionName, $filter, $options); 662 663 return $operation->execute($server); 664 } 665 666 /** 667 * Finds a single document matching the query. 668 * 669 * @see FindOne::__construct() for supported options 670 * @see http://docs.mongodb.org/manual/core/read-operations-introduction/ 671 * @param array|object $filter Query by which to filter documents 672 * @param array $options Additional options 673 * @return array|object|null 674 * @throws UnsupportedException if options are not supported by the selected server 675 * @throws InvalidArgumentException for parameter/option parsing errors 676 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 677 */ 678 public function findOne($filter = [], array $options = []) 679 { 680 if (! isset($options['readPreference']) && ! is_in_transaction($options)) { 681 $options['readPreference'] = $this->readPreference; 682 } 683 684 $server = select_server($this->manager, $options); 685 686 if (! isset($options['readConcern']) && server_supports_feature($server, self::$wireVersionForReadConcern) && ! is_in_transaction($options)) { 687 $options['readConcern'] = $this->readConcern; 688 } 689 690 if (! isset($options['typeMap'])) { 691 $options['typeMap'] = $this->typeMap; 692 } 693 694 $operation = new FindOne($this->databaseName, $this->collectionName, $filter, $options); 695 696 return $operation->execute($server); 697 } 698 699 /** 700 * Finds a single document and deletes it, returning the original. 701 * 702 * The document to return may be null if no document matched the filter. 703 * 704 * @see FindOneAndDelete::__construct() for supported options 705 * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ 706 * @param array|object $filter Query by which to filter documents 707 * @param array $options Command options 708 * @return array|object|null 709 * @throws UnexpectedValueException if the command response was malformed 710 * @throws UnsupportedException if options are not supported by the selected server 711 * @throws InvalidArgumentException for parameter/option parsing errors 712 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 713 */ 714 public function findOneAndDelete($filter, array $options = []) 715 { 716 $server = select_server($this->manager, $options); 717 718 if (! isset($options['writeConcern']) && server_supports_feature($server, self::$wireVersionForFindAndModifyWriteConcern) && ! is_in_transaction($options)) { 719 $options['writeConcern'] = $this->writeConcern; 720 } 721 722 if (! isset($options['typeMap'])) { 723 $options['typeMap'] = $this->typeMap; 724 } 725 726 $operation = new FindOneAndDelete($this->databaseName, $this->collectionName, $filter, $options); 727 728 return $operation->execute($server); 729 } 730 731 /** 732 * Finds a single document and replaces it, returning either the original or 733 * the replaced document. 734 * 735 * The document to return may be null if no document matched the filter. By 736 * default, the original document is returned. Specify 737 * FindOneAndReplace::RETURN_DOCUMENT_AFTER for the "returnDocument" option 738 * to return the updated document. 739 * 740 * @see FindOneAndReplace::__construct() for supported options 741 * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ 742 * @param array|object $filter Query by which to filter documents 743 * @param array|object $replacement Replacement document 744 * @param array $options Command options 745 * @return array|object|null 746 * @throws UnexpectedValueException if the command response was malformed 747 * @throws UnsupportedException if options are not supported by the selected server 748 * @throws InvalidArgumentException for parameter/option parsing errors 749 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 750 */ 751 public function findOneAndReplace($filter, $replacement, array $options = []) 752 { 753 $server = select_server($this->manager, $options); 754 755 if (! isset($options['writeConcern']) && server_supports_feature($server, self::$wireVersionForFindAndModifyWriteConcern) && ! is_in_transaction($options)) { 756 $options['writeConcern'] = $this->writeConcern; 757 } 758 759 if (! isset($options['typeMap'])) { 760 $options['typeMap'] = $this->typeMap; 761 } 762 763 $operation = new FindOneAndReplace($this->databaseName, $this->collectionName, $filter, $replacement, $options); 764 765 return $operation->execute($server); 766 } 767 768 /** 769 * Finds a single document and updates it, returning either the original or 770 * the updated document. 771 * 772 * The document to return may be null if no document matched the filter. By 773 * default, the original document is returned. Specify 774 * FindOneAndUpdate::RETURN_DOCUMENT_AFTER for the "returnDocument" option 775 * to return the updated document. 776 * 777 * @see FindOneAndReplace::__construct() for supported options 778 * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ 779 * @param array|object $filter Query by which to filter documents 780 * @param array|object $update Update to apply to the matched document 781 * @param array $options Command options 782 * @return array|object|null 783 * @throws UnexpectedValueException if the command response was malformed 784 * @throws UnsupportedException if options are not supported by the selected server 785 * @throws InvalidArgumentException for parameter/option parsing errors 786 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 787 */ 788 public function findOneAndUpdate($filter, $update, array $options = []) 789 { 790 $server = select_server($this->manager, $options); 791 792 if (! isset($options['writeConcern']) && server_supports_feature($server, self::$wireVersionForFindAndModifyWriteConcern) && ! is_in_transaction($options)) { 793 $options['writeConcern'] = $this->writeConcern; 794 } 795 796 if (! isset($options['typeMap'])) { 797 $options['typeMap'] = $this->typeMap; 798 } 799 800 $operation = new FindOneAndUpdate($this->databaseName, $this->collectionName, $filter, $update, $options); 801 802 return $operation->execute($server); 803 } 804 805 /** 806 * Return the collection name. 807 * 808 * @return string 809 */ 810 public function getCollectionName() 811 { 812 return $this->collectionName; 813 } 814 815 /** 816 * Return the database name. 817 * 818 * @return string 819 */ 820 public function getDatabaseName() 821 { 822 return $this->databaseName; 823 } 824 825 /** 826 * Return the Manager. 827 * 828 * @return Manager 829 */ 830 public function getManager() 831 { 832 return $this->manager; 833 } 834 835 /** 836 * Return the collection namespace. 837 * 838 * @see https://docs.mongodb.org/manual/reference/glossary/#term-namespace 839 * @return string 840 */ 841 public function getNamespace() 842 { 843 return $this->databaseName . '.' . $this->collectionName; 844 } 845 846 /** 847 * Return the read concern for this collection. 848 * 849 * @see http://php.net/manual/en/mongodb-driver-readconcern.isdefault.php 850 * @return ReadConcern 851 */ 852 public function getReadConcern() 853 { 854 return $this->readConcern; 855 } 856 857 /** 858 * Return the read preference for this collection. 859 * 860 * @return ReadPreference 861 */ 862 public function getReadPreference() 863 { 864 return $this->readPreference; 865 } 866 867 /** 868 * Return the type map for this collection. 869 * 870 * @return array 871 */ 872 public function getTypeMap() 873 { 874 return $this->typeMap; 875 } 876 877 /** 878 * Return the write concern for this collection. 879 * 880 * @see http://php.net/manual/en/mongodb-driver-writeconcern.isdefault.php 881 * @return WriteConcern 882 */ 883 public function getWriteConcern() 884 { 885 return $this->writeConcern; 886 } 887 888 /** 889 * Inserts multiple documents. 890 * 891 * @see InsertMany::__construct() for supported options 892 * @see http://docs.mongodb.org/manual/reference/command/insert/ 893 * @param array[]|object[] $documents The documents to insert 894 * @param array $options Command options 895 * @return InsertManyResult 896 * @throws InvalidArgumentException for parameter/option parsing errors 897 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 898 */ 899 public function insertMany(array $documents, array $options = []) 900 { 901 if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { 902 $options['writeConcern'] = $this->writeConcern; 903 } 904 905 $operation = new InsertMany($this->databaseName, $this->collectionName, $documents, $options); 906 $server = select_server($this->manager, $options); 907 908 return $operation->execute($server); 909 } 910 911 /** 912 * Inserts one document. 913 * 914 * @see InsertOne::__construct() for supported options 915 * @see http://docs.mongodb.org/manual/reference/command/insert/ 916 * @param array|object $document The document to insert 917 * @param array $options Command options 918 * @return InsertOneResult 919 * @throws InvalidArgumentException for parameter/option parsing errors 920 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 921 */ 922 public function insertOne($document, array $options = []) 923 { 924 if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { 925 $options['writeConcern'] = $this->writeConcern; 926 } 927 928 $operation = new InsertOne($this->databaseName, $this->collectionName, $document, $options); 929 $server = select_server($this->manager, $options); 930 931 return $operation->execute($server); 932 } 933 934 /** 935 * Returns information for all indexes for the collection. 936 * 937 * @see ListIndexes::__construct() for supported options 938 * @param array $options 939 * @return IndexInfoIterator 940 * @throws InvalidArgumentException for parameter/option parsing errors 941 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 942 */ 943 public function listIndexes(array $options = []) 944 { 945 $operation = new ListIndexes($this->databaseName, $this->collectionName, $options); 946 $server = select_server($this->manager, $options); 947 948 return $operation->execute($server); 949 } 950 951 /** 952 * Executes a map-reduce aggregation on the collection. 953 * 954 * @see MapReduce::__construct() for supported options 955 * @see http://docs.mongodb.org/manual/reference/command/mapReduce/ 956 * @param JavascriptInterface $map Map function 957 * @param JavascriptInterface $reduce Reduce function 958 * @param string|array|object $out Output specification 959 * @param array $options Command options 960 * @return MapReduceResult 961 * @throws UnsupportedException if options are not supported by the selected server 962 * @throws InvalidArgumentException for parameter/option parsing errors 963 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 964 * @throws UnexpectedValueException if the command response was malformed 965 */ 966 public function mapReduce(JavascriptInterface $map, JavascriptInterface $reduce, $out, array $options = []) 967 { 968 $hasOutputCollection = ! is_mapreduce_output_inline($out); 969 970 if (! isset($options['readPreference']) && ! is_in_transaction($options)) { 971 $options['readPreference'] = $this->readPreference; 972 } 973 974 // Check if the out option is inline because we will want to coerce a primary read preference if not 975 if ($hasOutputCollection) { 976 $options['readPreference'] = new ReadPreference(ReadPreference::RP_PRIMARY); 977 } 978 979 $server = select_server($this->manager, $options); 980 981 /* A "majority" read concern is not compatible with inline output, so 982 * avoid providing the Collection's read concern if it would conflict. 983 * 984 * A read concern is also not compatible with transactions. 985 */ 986 if (! isset($options['readConcern']) && ! ($hasOutputCollection && $this->readConcern->getLevel() === ReadConcern::MAJORITY) && server_supports_feature($server, self::$wireVersionForReadConcern) && ! is_in_transaction($options)) { 987 $options['readConcern'] = $this->readConcern; 988 } 989 990 if (! isset($options['typeMap'])) { 991 $options['typeMap'] = $this->typeMap; 992 } 993 994 if (! isset($options['writeConcern']) && server_supports_feature($server, self::$wireVersionForWritableCommandWriteConcern) && ! is_in_transaction($options)) { 995 $options['writeConcern'] = $this->writeConcern; 996 } 997 998 $operation = new MapReduce($this->databaseName, $this->collectionName, $map, $reduce, $out, $options); 999 1000 return $operation->execute($server); 1001 } 1002 1003 /** 1004 * Replaces at most one document matching the filter. 1005 * 1006 * @see ReplaceOne::__construct() for supported options 1007 * @see http://docs.mongodb.org/manual/reference/command/update/ 1008 * @param array|object $filter Query by which to filter documents 1009 * @param array|object $replacement Replacement document 1010 * @param array $options Command options 1011 * @return UpdateResult 1012 * @throws UnsupportedException if options are not supported by the selected server 1013 * @throws InvalidArgumentException for parameter/option parsing errors 1014 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 1015 */ 1016 public function replaceOne($filter, $replacement, array $options = []) 1017 { 1018 if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { 1019 $options['writeConcern'] = $this->writeConcern; 1020 } 1021 1022 $operation = new ReplaceOne($this->databaseName, $this->collectionName, $filter, $replacement, $options); 1023 $server = select_server($this->manager, $options); 1024 1025 return $operation->execute($server); 1026 } 1027 1028 /** 1029 * Updates all documents matching the filter. 1030 * 1031 * @see UpdateMany::__construct() for supported options 1032 * @see http://docs.mongodb.org/manual/reference/command/update/ 1033 * @param array|object $filter Query by which to filter documents 1034 * @param array|object $update Update to apply to the matched documents 1035 * @param array $options Command options 1036 * @return UpdateResult 1037 * @throws UnsupportedException if options are not supported by the selected server 1038 * @throws InvalidArgumentException for parameter/option parsing errors 1039 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 1040 */ 1041 public function updateMany($filter, $update, array $options = []) 1042 { 1043 if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { 1044 $options['writeConcern'] = $this->writeConcern; 1045 } 1046 1047 $operation = new UpdateMany($this->databaseName, $this->collectionName, $filter, $update, $options); 1048 $server = select_server($this->manager, $options); 1049 1050 return $operation->execute($server); 1051 } 1052 1053 /** 1054 * Updates at most one document matching the filter. 1055 * 1056 * @see UpdateOne::__construct() for supported options 1057 * @see http://docs.mongodb.org/manual/reference/command/update/ 1058 * @param array|object $filter Query by which to filter documents 1059 * @param array|object $update Update to apply to the matched document 1060 * @param array $options Command options 1061 * @return UpdateResult 1062 * @throws UnsupportedException if options are not supported by the selected server 1063 * @throws InvalidArgumentException for parameter/option parsing errors 1064 * @throws DriverRuntimeException for other driver errors (e.g. connection errors) 1065 */ 1066 public function updateOne($filter, $update, array $options = []) 1067 { 1068 if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { 1069 $options['writeConcern'] = $this->writeConcern; 1070 } 1071 1072 $operation = new UpdateOne($this->databaseName, $this->collectionName, $filter, $update, $options); 1073 $server = select_server($this->manager, $options); 1074 1075 return $operation->execute($server); 1076 } 1077 1078 /** 1079 * Create a change stream for watching changes to the collection. 1080 * 1081 * @see Watch::__construct() for supported options 1082 * @param array $pipeline List of pipeline operations 1083 * @param array $options Command options 1084 * @return ChangeStream 1085 * @throws InvalidArgumentException for parameter/option parsing errors 1086 */ 1087 public function watch(array $pipeline = [], array $options = []) 1088 { 1089 if (! isset($options['readPreference']) && ! is_in_transaction($options)) { 1090 $options['readPreference'] = $this->readPreference; 1091 } 1092 1093 $server = select_server($this->manager, $options); 1094 1095 /* Although change streams require a newer version of the server than 1096 * read concerns, perform the usual wire version check before inheriting 1097 * the collection's read concern. In the event that the server is too 1098 * old, this makes it more likely that users will encounter an error 1099 * related to change streams being unsupported instead of an 1100 * UnsupportedException regarding use of the "readConcern" option from 1101 * the Aggregate operation class. */ 1102 if (! isset($options['readConcern']) && server_supports_feature($server, self::$wireVersionForReadConcern) && ! is_in_transaction($options)) { 1103 $options['readConcern'] = $this->readConcern; 1104 } 1105 1106 if (! isset($options['typeMap'])) { 1107 $options['typeMap'] = $this->typeMap; 1108 } 1109 1110 $operation = new Watch($this->manager, $this->databaseName, $this->collectionName, $pipeline, $options); 1111 1112 return $operation->execute($server); 1113 } 1114 1115 /** 1116 * Get a clone of this collection with different options. 1117 * 1118 * @see Collection::__construct() for supported options 1119 * @param array $options Collection constructor options 1120 * @return Collection 1121 * @throws InvalidArgumentException for parameter/option parsing errors 1122 */ 1123 public function withOptions(array $options = []) 1124 { 1125 $options += [ 1126 'readConcern' => $this->readConcern, 1127 'readPreference' => $this->readPreference, 1128 'typeMap' => $this->typeMap, 1129 'writeConcern' => $this->writeConcern, 1130 ]; 1131 1132 return new Collection($this->manager, $this->databaseName, $this->collectionName, $options); 1133 } 1134} 1135