diff --git a/UPGRADE-2.0.md b/UPGRADE-2.0.md index 8dabbdd59..8b3562013 100644 --- a/UPGRADE-2.0.md +++ b/UPGRADE-2.0.md @@ -3,6 +3,25 @@ UPGRADE FROM 1.x to 2.0 * Classes in the namespace `MongoDB\Operation\` are `final`. * All methods in interfaces and classes now define a return type. + * The `MongoDB\ChangeStream::CURSOR_NOT_FOUND` constant is now private. + * The `MongoDB\Operation\Watch::FULL_DOCUMENT_DEFAULT` constant has been + removed. + * The `getNamespace` and `isGeoHaystack` methods have been removed from the + `MongoDB\Model\IndexInfo` class. + * The `maxScan`, `modifiers`, `oplogReplay`, and `snapshot` options for `find` + and `findOne` operations have been removed. + * The `MongoDB\Collection::mapReduce` method has been removed. Use + [aggregation pipeline](https://www.mongodb.com/docs/manual/reference/map-reduce-to-aggregation-pipeline/) + instead. + * The following classes and interfaces have been removed without replacement: + * `MongoDB\MapReduceResult` + * `MongoDB\Model\CollectionInfoCommandIterator` + * `MongoDB\Model\CollectionInfoIterator` + * `MongoDB\Model\DatabaseInfoIterator` + * `MongoDB\Model\DatabaseInfoLegacyIterator` + * `MongoDB\Model\IndexInfoIterator` + * `MongoDB\Model\IndexInfoIteratorIterator` + * `MongoDB\Operation\Executable` GridFS ------ diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 5ea414eb3..541a5380f 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -179,18 +179,7 @@ count($queriesOrArrayOfQueries[0]) > 0]]> - - - - - - - - - - ]]> - @@ -198,9 +187,6 @@ - - ]]> - @@ -232,19 +218,10 @@ - - - - - ]]> - - - ]]> - @@ -271,19 +248,10 @@ - - - - - ]]> - - - ]]> - @@ -388,34 +356,6 @@ current()]]> - - - - - - - - - - - - - - - - - databases)]]> - - - - databases)]]> - - - - - - - index]]> @@ -713,11 +653,7 @@ options['codec']]]> options['typeMap']]]> - - - - @@ -802,64 +738,11 @@ - - - - - - ]]> - - - ]]> - - - - - - - - ]]> - - - ]]> - - - - - databaseName . '.' . $this->collectionName)]]> - - - - - - - ]]> - - - - - - - ]]> - - - - - result->collection]]> - result->db]]> - options['typeMap']]]> - - - - - - - diff --git a/src/ChangeStream.php b/src/ChangeStream.php index a1a732666..c48cd1483 100644 --- a/src/ChangeStream.php +++ b/src/ChangeStream.php @@ -43,11 +43,7 @@ */ class ChangeStream implements Iterator { - /** - * @deprecated 1.4 - * @todo make this constant private in 2.0 (see: PHPLIB-360) - */ - public const CURSOR_NOT_FOUND = 43; + private const CURSOR_NOT_FOUND = 43; private const RESUMABLE_ERROR_CODES = [ 6, // HostUnreachable diff --git a/src/Client.php b/src/Client.php index 00a1a6ed3..38803fe52 100644 --- a/src/Client.php +++ b/src/Client.php @@ -39,7 +39,6 @@ use MongoDB\Model\BSONArray; use MongoDB\Model\BSONDocument; use MongoDB\Model\DatabaseInfo; -use MongoDB\Model\DatabaseInfoIterator; use MongoDB\Operation\DropDatabase; use MongoDB\Operation\ListDatabaseNames; use MongoDB\Operation\ListDatabases; @@ -284,6 +283,7 @@ public function getWriteConcern(): WriteConcern * List database names. * * @see ListDatabaseNames::__construct() for supported options + * @return Iterator * @throws UnexpectedValueException if the command response was malformed * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) @@ -305,7 +305,7 @@ public function listDatabaseNames(array $options = []): Iterator * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function listDatabases(array $options = []): DatabaseInfoIterator + public function listDatabases(array $options = []): Iterator { $operation = new ListDatabases($options); $server = select_server($this->manager, $options); diff --git a/src/Collection.php b/src/Collection.php index ec87cb928..88606e449 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -20,7 +20,6 @@ use Countable; use Iterator; use MongoDB\BSON\Document; -use MongoDB\BSON\JavascriptInterface; use MongoDB\BSON\PackedArray; use MongoDB\Builder\BuilderEncoder; use MongoDB\Builder\Pipeline; @@ -38,7 +37,6 @@ use MongoDB\Model\BSONArray; use MongoDB\Model\BSONDocument; use MongoDB\Model\IndexInfo; -use MongoDB\Model\IndexInfoIterator; use MongoDB\Operation\Aggregate; use MongoDB\Operation\BulkWrite; use MongoDB\Operation\Count; @@ -64,7 +62,6 @@ use MongoDB\Operation\InsertOne; use MongoDB\Operation\ListIndexes; use MongoDB\Operation\ListSearchIndexes; -use MongoDB\Operation\MapReduce; use MongoDB\Operation\RenameCollection; use MongoDB\Operation\ReplaceOne; use MongoDB\Operation\UpdateMany; @@ -78,11 +75,7 @@ use function array_key_exists; use function current; use function is_array; -use function sprintf; use function strlen; -use function trigger_error; - -use const E_USER_DEPRECATED; class Collection { @@ -884,7 +877,7 @@ public function insertOne(array|object $document, array $options = []): InsertOn * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function listIndexes(array $options = []): IndexInfoIterator + public function listIndexes(array $options = []): Iterator { $operation = new ListIndexes($this->databaseName, $this->collectionName, $options); @@ -909,48 +902,6 @@ public function listSearchIndexes(array $options = []): Iterator return $operation->execute($server); } - /** - * Executes a map-reduce aggregation on the collection. - * - * @see MapReduce::__construct() for supported options - * @see https://mongodb.com/docs/manual/reference/command/mapReduce/ - * @param JavascriptInterface $map Map function - * @param JavascriptInterface $reduce Reduce function - * @param string|array|object $out Output specification - * @param array $options Command options - * @throws UnsupportedException if options are not supported by the selected server - * @throws InvalidArgumentException for parameter/option parsing errors - * @throws DriverRuntimeException for other driver errors (e.g. connection errors) - * @throws UnexpectedValueException if the command response was malformed - */ - public function mapReduce(JavascriptInterface $map, JavascriptInterface $reduce, string|array|object $out, array $options = []): MapReduceResult - { - @trigger_error(sprintf('The %s method is deprecated and will be removed in a version 2.0.', __METHOD__), E_USER_DEPRECATED); - - $hasOutputCollection = ! is_mapreduce_output_inline($out); - - // Check if the out option is inline because we will want to coerce a primary read preference if not - if ($hasOutputCollection) { - $options['readPreference'] = new ReadPreference(ReadPreference::PRIMARY); - } else { - $options = $this->inheritReadPreference($options); - } - - /* A "majority" read concern is not compatible with inline output, so - * avoid providing the Collection's read concern if it would conflict. - */ - if (! $hasOutputCollection || $this->readConcern->getLevel() !== ReadConcern::MAJORITY) { - $options = $this->inheritReadConcern($options); - } - - $options = $this->inheritWriteOptions($options); - $options = $this->inheritTypeMap($options); - - $operation = new MapReduce($this->databaseName, $this->collectionName, $map, $reduce, $out, $options); - - return $operation->execute(select_server_for_write($this->manager, $options)); - } - /** * Renames the collection. * diff --git a/src/Command/ListCollections.php b/src/Command/ListCollections.php index f2fb2410f..d42250cd3 100644 --- a/src/Command/ListCollections.php +++ b/src/Command/ListCollections.php @@ -18,13 +18,11 @@ namespace MongoDB\Command; use MongoDB\Driver\Command; -use MongoDB\Driver\Cursor; +use MongoDB\Driver\CursorInterface; use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Server; use MongoDB\Driver\Session; use MongoDB\Exception\InvalidArgumentException; -use MongoDB\Model\CachingIterator; -use MongoDB\Operation\Executable; use function is_bool; use function is_integer; @@ -36,7 +34,7 @@ * @internal * @see https://mongodb.com/docs/manual/reference/command/listCollections/ */ -final class ListCollections implements Executable +final class ListCollections { /** * Constructs a listCollections command. @@ -93,17 +91,16 @@ public function __construct(private string $databaseName, private array $options /** * Execute the operation. * - * @return CachingIterator - * @see Executable::execute() + * @return CursorInterface * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server): CachingIterator + public function execute(Server $server): CursorInterface { - /** @var Cursor $cursor */ + /** @var CursorInterface $cursor */ $cursor = $server->executeReadCommand($this->databaseName, $this->createCommand(), $this->createOptions()); $cursor->setTypeMap(['root' => 'array', 'document' => 'array']); - return new CachingIterator($cursor); + return $cursor; } /** diff --git a/src/Command/ListDatabases.php b/src/Command/ListDatabases.php index 7ceeb4206..31ca5f741 100644 --- a/src/Command/ListDatabases.php +++ b/src/Command/ListDatabases.php @@ -23,7 +23,6 @@ use MongoDB\Driver\Session; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\UnexpectedValueException; -use MongoDB\Operation\Executable; use function current; use function is_array; @@ -37,7 +36,7 @@ * @internal * @see https://mongodb.com/docs/manual/reference/command/listDatabases/ */ -final class ListDatabases implements Executable +final class ListDatabases { /** * Constructs a listDatabases command. @@ -93,7 +92,6 @@ public function __construct(private array $options = []) /** * Execute the operation. * - * @see Executable::execute() * @return array An array of database info structures * @throws UnexpectedValueException if the command response was malformed * @throws DriverRuntimeException for other driver errors (e.g. connection errors) @@ -102,6 +100,7 @@ public function execute(Server $server): array { $cursor = $server->executeReadCommand('admin', $this->createCommand(), $this->createOptions()); $cursor->setTypeMap(['root' => 'array', 'document' => 'array']); + $result = current($cursor->toArray()); if (! isset($result['databases']) || ! is_array($result['databases'])) { diff --git a/src/Database.php b/src/Database.php index e8e256c49..b3282d7af 100644 --- a/src/Database.php +++ b/src/Database.php @@ -38,7 +38,6 @@ use MongoDB\Model\BSONArray; use MongoDB\Model\BSONDocument; use MongoDB\Model\CollectionInfo; -use MongoDB\Model\CollectionInfoIterator; use MongoDB\Operation\Aggregate; use MongoDB\Operation\CreateCollection; use MongoDB\Operation\CreateEncryptedCollection; @@ -460,6 +459,7 @@ public function getWriteConcern(): WriteConcern * Returns the names of all collections in this database * * @see ListCollectionNames::__construct() for supported options + * @return Iterator * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ @@ -479,7 +479,7 @@ public function listCollectionNames(array $options = []): Iterator * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function listCollections(array $options = []): CollectionInfoIterator + public function listCollections(array $options = []): Iterator { $operation = new ListCollections($this->databaseName, $options); $server = select_server($this->manager, $options); diff --git a/src/MapReduceResult.php b/src/MapReduceResult.php deleted file mode 100644 index dbe0e1dda..000000000 --- a/src/MapReduceResult.php +++ /dev/null @@ -1,103 +0,0 @@ - - * @psalm-type MapReduceCallable = callable(): Traversable - */ -class MapReduceResult implements IteratorAggregate -{ - /** - * @var callable - * @psalm-var MapReduceCallable - */ - private $getIterator; - - private int $executionTimeMS; - - private array $counts; - - private array $timing; - - /** - * Returns various count statistics from the mapReduce command. - */ - public function getCounts(): array - { - return $this->counts; - } - - /** - * Return the command execution time in milliseconds. - */ - public function getExecutionTimeMS(): int - { - return $this->executionTimeMS; - } - - /** - * Return the mapReduce results as a Traversable. - * - * @see https://php.net/iteratoraggregate.getiterator - * @return Traversable - */ - public function getIterator(): Traversable - { - return call_user_func($this->getIterator); - } - - /** - * Returns various timing statistics from the mapReduce command. - * - * Note: timing statistics are only available if the mapReduce command's - * "verbose" option was true; otherwise, an empty array will be returned. - */ - public function getTiming(): array - { - return $this->timing; - } - - /** - * @internal - * @param callable $getIterator Callback that returns a Traversable for mapReduce results - * @param stdClass $result Result document from the mapReduce command - * @psalm-param MapReduceCallable $getIterator - */ - public function __construct(callable $getIterator, stdClass $result) - { - $this->getIterator = $getIterator; - $this->executionTimeMS = isset($result->timeMillis) ? (integer) $result->timeMillis : 0; - $this->counts = isset($result->counts) ? (array) $result->counts : []; - $this->timing = isset($result->timing) ? (array) $result->timing : []; - } -} diff --git a/src/Model/CallbackIterator.php b/src/Model/CallbackIterator.php index 161224925..4a8e5dda9 100644 --- a/src/Model/CallbackIterator.php +++ b/src/Model/CallbackIterator.php @@ -28,7 +28,7 @@ * * @internal * - * @template TKey + * @template TKey of array-key * @template TValue * @template TCallbackValue * @template-implements Iterator diff --git a/src/Model/CollectionInfoCommandIterator.php b/src/Model/CollectionInfoCommandIterator.php deleted file mode 100644 index 37c28b051..000000000 --- a/src/Model/CollectionInfoCommandIterator.php +++ /dev/null @@ -1,60 +0,0 @@ -> - */ -final class CollectionInfoCommandIterator extends IteratorIterator implements CollectionInfoIterator -{ - /** @param Traversable $iterator */ - public function __construct(Traversable $iterator, private ?string $databaseName = null) - { - parent::__construct($iterator); - } - - /** - * Return the current element as a CollectionInfo instance. - * - * @see CollectionInfoIterator::current() - * @see https://php.net/iterator.current - */ - public function current(): CollectionInfo - { - $info = parent::current(); - - if ($this->databaseName !== null && isset($info['idIndex']) && ! isset($info['idIndex']['ns'])) { - $info['idIndex']['ns'] = $this->databaseName . '.' . $info['name']; - } - - return new CollectionInfo($info); - } -} diff --git a/src/Model/CollectionInfoIterator.php b/src/Model/CollectionInfoIterator.php deleted file mode 100644 index 6ce22c551..000000000 --- a/src/Model/CollectionInfoIterator.php +++ /dev/null @@ -1,37 +0,0 @@ - - */ -interface CollectionInfoIterator extends Iterator -{ - /** - * Return the current element as a CollectionInfo instance. - */ - public function current(): CollectionInfo; -} diff --git a/src/Model/DatabaseInfoIterator.php b/src/Model/DatabaseInfoIterator.php deleted file mode 100644 index fc00fb79b..000000000 --- a/src/Model/DatabaseInfoIterator.php +++ /dev/null @@ -1,37 +0,0 @@ - - */ -interface DatabaseInfoIterator extends Iterator -{ - /** - * Return the current element as a DatabaseInfo instance. - */ - public function current(): DatabaseInfo; -} diff --git a/src/Model/DatabaseInfoLegacyIterator.php b/src/Model/DatabaseInfoLegacyIterator.php deleted file mode 100644 index e6a73ab75..000000000 --- a/src/Model/DatabaseInfoLegacyIterator.php +++ /dev/null @@ -1,92 +0,0 @@ -databases)); - } - - /** - * Return the key of the current element. - * - * @see https://php.net/iterator.key - */ - public function key(): int - { - return key($this->databases); - } - - /** - * Move forward to next element. - * - * @see https://php.net/iterator.next - */ - public function next(): void - { - next($this->databases); - } - - /** - * Rewind the Iterator to the first element. - * - * @see https://php.net/iterator.rewind - */ - public function rewind(): void - { - reset($this->databases); - } - - /** - * Checks if current position is valid. - * - * @see https://php.net/iterator.valid - */ - public function valid(): bool - { - return key($this->databases) !== null; - } -} diff --git a/src/Model/IndexInfo.php b/src/Model/IndexInfo.php index 4e2177765..ce6e04be5 100644 --- a/src/Model/IndexInfo.php +++ b/src/Model/IndexInfo.php @@ -22,9 +22,6 @@ use function array_key_exists; use function array_search; -use function trigger_error; - -use const E_USER_DEPRECATED; /** * Index information model class. @@ -82,18 +79,6 @@ public function getName(): string return (string) $this->info['name']; } - /** - * Return the index namespace (e.g. "db.collection"). - * - * @deprecated - */ - public function getNamespace(): string - { - @trigger_error('MongoDB 4.4 drops support for the namespace in indexes, the method "IndexInfo::getNamespace()" will be removed in version 2.0', E_USER_DEPRECATED); - - return (string) $this->info['ns']; - } - /** * Return the index version. */ @@ -110,18 +95,6 @@ public function is2dSphere(): bool return array_search('2dsphere', $this->getKey(), true) !== false; } - /** - * Return whether or not this index is of type geoHaystack. - * - * @deprecated Since 1.16: MongoDB 5.0 removes support for geoHaystack indexes. - */ - public function isGeoHaystack(): bool - { - @trigger_error('MongoDB 5.0 removes support for "geoHaystack" indexes, the method "IndexInfo::isGeoHaystack()" will be removed in version 2.0', E_USER_DEPRECATED); - - return array_search('geoHaystack', $this->getKey(), true) !== false; - } - /** * Return whether this is a sparse index. * diff --git a/src/Model/IndexInfoIterator.php b/src/Model/IndexInfoIterator.php deleted file mode 100644 index f52e8dbfa..000000000 --- a/src/Model/IndexInfoIterator.php +++ /dev/null @@ -1,37 +0,0 @@ - - */ -interface IndexInfoIterator extends Iterator -{ - /** - * Return the current element as a IndexInfo instance. - */ - public function current(): IndexInfo; -} diff --git a/src/Model/IndexInfoIteratorIterator.php b/src/Model/IndexInfoIteratorIterator.php deleted file mode 100644 index 04a95528b..000000000 --- a/src/Model/IndexInfoIteratorIterator.php +++ /dev/null @@ -1,64 +0,0 @@ -> - */ -final class IndexInfoIteratorIterator extends IteratorIterator implements IndexInfoIterator -{ - /** @param Traversable $iterator */ - public function __construct(Traversable $iterator, private ?string $ns = null) - { - parent::__construct($iterator); - } - - /** - * Return the current element as an IndexInfo instance. - * - * @see IndexInfoIterator::current() - * @see https://php.net/iterator.current - */ - public function current(): IndexInfo - { - $info = parent::current(); - - if (! array_key_exists('ns', $info) && $this->ns !== null) { - $info['ns'] = $this->ns; - } - - return new IndexInfo($info); - } -} diff --git a/src/Operation/Aggregate.php b/src/Operation/Aggregate.php index b7f109589..0588df188 100644 --- a/src/Operation/Aggregate.php +++ b/src/Operation/Aggregate.php @@ -47,7 +47,7 @@ * @see \MongoDB\Collection::aggregate() * @see https://mongodb.com/docs/manual/reference/command/aggregate/ */ -final class Aggregate implements Executable, Explainable +final class Aggregate implements Explainable { private bool $isWrite; @@ -212,7 +212,6 @@ public function __construct(private string $databaseName, private ?string $colle /** * Execute the operation. * - * @see Executable::execute() * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if read concern or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) diff --git a/src/Operation/BulkWrite.php b/src/Operation/BulkWrite.php index 178ff1847..411c9d24d 100644 --- a/src/Operation/BulkWrite.php +++ b/src/Operation/BulkWrite.php @@ -46,7 +46,7 @@ * * @see \MongoDB\Collection::bulkWrite() */ -final class BulkWrite implements Executable +final class BulkWrite { public const DELETE_MANY = 'deleteMany'; public const DELETE_ONE = 'deleteOne'; @@ -185,7 +185,6 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ diff --git a/src/Operation/Count.php b/src/Operation/Count.php index df91214bd..d79d49447 100644 --- a/src/Operation/Count.php +++ b/src/Operation/Count.php @@ -41,7 +41,7 @@ * @see \MongoDB\Collection::count() * @see https://mongodb.com/docs/manual/reference/command/count/ */ -final class Count implements Executable, Explainable +final class Count implements Explainable { /** * Constructs a count command. @@ -124,7 +124,6 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if read concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) diff --git a/src/Operation/CountDocuments.php b/src/Operation/CountDocuments.php index 4badaaf3b..7d00e8bed 100644 --- a/src/Operation/CountDocuments.php +++ b/src/Operation/CountDocuments.php @@ -37,7 +37,7 @@ * @see \MongoDB\Collection::countDocuments() * @see https://github.com/mongodb/specifications/blob/master/source/crud/crud.rst#countdocuments */ -final class CountDocuments implements Executable +final class CountDocuments { private array $aggregateOptions; @@ -103,7 +103,6 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if collation or read concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) diff --git a/src/Operation/CreateCollection.php b/src/Operation/CreateCollection.php index 6411d1d2e..0e7918f37 100644 --- a/src/Operation/CreateCollection.php +++ b/src/Operation/CreateCollection.php @@ -41,7 +41,7 @@ * @see \MongoDB\Database::createCollection() * @see https://mongodb.com/docs/manual/reference/command/create/ */ -final class CreateCollection implements Executable +final class CreateCollection { public const USE_POWER_OF_2_SIZES = 1; public const NO_PADDING = 2; @@ -239,7 +239,6 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() * @return array|object Command result document * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ diff --git a/src/Operation/CreateEncryptedCollection.php b/src/Operation/CreateEncryptedCollection.php index fec5f2d18..7c1075c29 100644 --- a/src/Operation/CreateEncryptedCollection.php +++ b/src/Operation/CreateEncryptedCollection.php @@ -48,7 +48,7 @@ * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/client-side-encryption.rst#create-encrypted-collection-helper * @see https://www.mongodb.com/docs/manual/core/queryable-encryption/fundamentals/manage-collections/ */ -final class CreateEncryptedCollection implements Executable +final class CreateEncryptedCollection { private const WIRE_VERSION_FOR_QUERYABLE_ENCRYPTION_V2 = 21; @@ -155,7 +155,6 @@ public function createDataKeys(ClientEncryption $clientEncryption, string $kmsPr } /** - * @see Executable::execute() * @return array|object Command result document from creating the encrypted collection * @throws DriverRuntimeException for other driver errors (e.g. connection errors) * @throws UnsupportedException if the server does not support Queryable Encryption diff --git a/src/Operation/CreateIndexes.php b/src/Operation/CreateIndexes.php index 0b164c48c..6591270b7 100644 --- a/src/Operation/CreateIndexes.php +++ b/src/Operation/CreateIndexes.php @@ -41,7 +41,7 @@ * @see \MongoDB\Collection::createIndexes() * @see https://mongodb.com/docs/manual/reference/command/createIndexes/ */ -final class CreateIndexes implements Executable +final class CreateIndexes { private const WIRE_VERSION_FOR_COMMIT_QUORUM = 9; @@ -116,7 +116,6 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() * @return string[] The names of the created indexes * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) diff --git a/src/Operation/CreateSearchIndexes.php b/src/Operation/CreateSearchIndexes.php index b96ca0f41..d21ed9428 100644 --- a/src/Operation/CreateSearchIndexes.php +++ b/src/Operation/CreateSearchIndexes.php @@ -37,7 +37,7 @@ * @see \MongoDB\Collection::createSearchIndexes() * @see https://mongodb.com/docs/manual/reference/command/createSearchIndexes/ */ -final class CreateSearchIndexes implements Executable +final class CreateSearchIndexes { private array $indexes = []; @@ -68,7 +68,6 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() * @return string[] The names of the created indexes * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) diff --git a/src/Operation/DatabaseCommand.php b/src/Operation/DatabaseCommand.php index 97cb7a500..567bbff9e 100644 --- a/src/Operation/DatabaseCommand.php +++ b/src/Operation/DatabaseCommand.php @@ -32,7 +32,7 @@ * * @see \MongoDB\Database::command() */ -final class DatabaseCommand implements Executable +final class DatabaseCommand { private Command $command; @@ -78,11 +78,6 @@ public function __construct(private string $databaseName, array|object $command, $this->command = $command instanceof Command ? $command : new Command($command); } - /** - * Execute the operation. - * - * @see Executable::execute() - */ public function execute(Server $server): CursorInterface { $cursor = $server->executeCommand($this->databaseName, $this->command, $this->createOptions()); diff --git a/src/Operation/Delete.php b/src/Operation/Delete.php index ae2a46306..7e264152f 100644 --- a/src/Operation/Delete.php +++ b/src/Operation/Delete.php @@ -42,7 +42,7 @@ * @internal * @see https://mongodb.com/docs/manual/reference/command/delete/ */ -final class Delete implements Executable, Explainable +final class Delete implements Explainable { private const WIRE_VERSION_FOR_HINT = 9; @@ -120,7 +120,6 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() * @throws UnsupportedException if hint or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ diff --git a/src/Operation/DeleteMany.php b/src/Operation/DeleteMany.php index 78090c464..5278ee08a 100644 --- a/src/Operation/DeleteMany.php +++ b/src/Operation/DeleteMany.php @@ -29,7 +29,7 @@ * @see \MongoDB\Collection::deleteOne() * @see https://mongodb.com/docs/manual/reference/command/delete/ */ -final class DeleteMany implements Executable, Explainable +final class DeleteMany implements Explainable { private Delete $delete; @@ -74,7 +74,6 @@ public function __construct(string $databaseName, string $collectionName, array| /** * Execute the operation. * - * @see Executable::execute() * @throws UnsupportedException if collation is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ diff --git a/src/Operation/DeleteOne.php b/src/Operation/DeleteOne.php index 157bb748a..44dd47ba5 100644 --- a/src/Operation/DeleteOne.php +++ b/src/Operation/DeleteOne.php @@ -29,7 +29,7 @@ * @see \MongoDB\Collection::deleteOne() * @see https://mongodb.com/docs/manual/reference/command/delete/ */ -final class DeleteOne implements Executable, Explainable +final class DeleteOne implements Explainable { private Delete $delete; @@ -74,7 +74,6 @@ public function __construct(string $databaseName, string $collectionName, array| /** * Execute the operation. * - * @see Executable::execute() * @throws UnsupportedException if collation is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ diff --git a/src/Operation/Distinct.php b/src/Operation/Distinct.php index 1f06c0ff2..09ae1f00d 100644 --- a/src/Operation/Distinct.php +++ b/src/Operation/Distinct.php @@ -40,7 +40,7 @@ * @see \MongoDB\Collection::distinct() * @see https://mongodb.com/docs/manual/reference/command/distinct/ */ -final class Distinct implements Executable, Explainable +final class Distinct implements Explainable { /** * Constructs a distinct command. @@ -109,7 +109,6 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if read concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) diff --git a/src/Operation/DropCollection.php b/src/Operation/DropCollection.php index d1a0b65a8..c5878359e 100644 --- a/src/Operation/DropCollection.php +++ b/src/Operation/DropCollection.php @@ -36,7 +36,7 @@ * @see \MongoDB\Database::dropCollection() * @see https://mongodb.com/docs/manual/reference/command/drop/ */ -final class DropCollection implements Executable +final class DropCollection { private const ERROR_CODE_NAMESPACE_NOT_FOUND = 26; @@ -83,7 +83,6 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() * @return array|object Command result document * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) diff --git a/src/Operation/DropDatabase.php b/src/Operation/DropDatabase.php index c60015e5c..3f9d7756f 100644 --- a/src/Operation/DropDatabase.php +++ b/src/Operation/DropDatabase.php @@ -34,7 +34,7 @@ * @see \MongoDB\Database::drop() * @see https://mongodb.com/docs/manual/reference/command/dropDatabase/ */ -final class DropDatabase implements Executable +final class DropDatabase { /** * Constructs a dropDatabase command. @@ -78,7 +78,6 @@ public function __construct(private string $databaseName, private array $options /** * Execute the operation. * - * @see Executable::execute() * @return array|object Command result document * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ diff --git a/src/Operation/DropEncryptedCollection.php b/src/Operation/DropEncryptedCollection.php index 32f143ee5..52fa1b8e3 100644 --- a/src/Operation/DropEncryptedCollection.php +++ b/src/Operation/DropEncryptedCollection.php @@ -37,7 +37,7 @@ * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/client-side-encryption.rst#drop-collection-helper * @see https://www.mongodb.com/docs/manual/core/queryable-encryption/fundamentals/manage-collections/ */ -final class DropEncryptedCollection implements Executable +final class DropEncryptedCollection { private DropCollection $dropCollection; @@ -85,7 +85,6 @@ public function __construct(string $databaseName, string $collectionName, array } /** - * @see Executable::execute() * @return array|object Command result document from dropping the encrypted collection * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ diff --git a/src/Operation/DropIndexes.php b/src/Operation/DropIndexes.php index d6b1beb59..152977445 100644 --- a/src/Operation/DropIndexes.php +++ b/src/Operation/DropIndexes.php @@ -35,7 +35,7 @@ * @see \MongoDB\Collection::dropIndexes() * @see https://mongodb.com/docs/manual/reference/command/dropIndexes/ */ -final class DropIndexes implements Executable +final class DropIndexes { /** * Constructs a dropIndexes command. @@ -92,7 +92,6 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() * @return array|object Command result document * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) diff --git a/src/Operation/DropSearchIndex.php b/src/Operation/DropSearchIndex.php index 73b9c6e17..c79025fbb 100644 --- a/src/Operation/DropSearchIndex.php +++ b/src/Operation/DropSearchIndex.php @@ -30,7 +30,7 @@ * @see \MongoDB\Collection::dropSearchIndexes() * @see https://mongodb.com/docs/manual/reference/command/dropSearchIndexes/ */ -final class DropSearchIndex implements Executable +final class DropSearchIndex { private const ERROR_CODE_NAMESPACE_NOT_FOUND = 26; @@ -53,7 +53,6 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ diff --git a/src/Operation/EstimatedDocumentCount.php b/src/Operation/EstimatedDocumentCount.php index 2a5aada61..716996541 100644 --- a/src/Operation/EstimatedDocumentCount.php +++ b/src/Operation/EstimatedDocumentCount.php @@ -35,7 +35,7 @@ * @see \MongoDB\Collection::estimatedDocumentCount() * @see https://mongodb.com/docs/manual/reference/command/count/ */ -final class EstimatedDocumentCount implements Executable, Explainable +final class EstimatedDocumentCount implements Explainable { private array $options; @@ -87,7 +87,6 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if collation or read concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) diff --git a/src/Operation/Executable.php b/src/Operation/Executable.php deleted file mode 100644 index 2c8dbc9f2..000000000 --- a/src/Operation/Executable.php +++ /dev/null @@ -1,39 +0,0 @@ -options['maxAwaitTimeMS'], 'integer'); } - if (isset($this->options['maxScan']) && ! is_integer($this->options['maxScan'])) { - throw InvalidArgumentException::invalidType('"maxScan" option', $this->options['maxScan'], 'integer'); - } - if (isset($this->options['maxTimeMS']) && ! is_integer($this->options['maxTimeMS'])) { throw InvalidArgumentException::invalidType('"maxTimeMS" option', $this->options['maxTimeMS'], 'integer'); } @@ -219,18 +194,10 @@ public function __construct(private string $databaseName, private string $collec throw InvalidArgumentException::expectedDocumentType('"min" option', $this->options['min']); } - if (isset($this->options['modifiers']) && ! is_document($this->options['modifiers'])) { - throw InvalidArgumentException::expectedDocumentType('"modifiers" option', $this->options['modifiers']); - } - if (isset($this->options['noCursorTimeout']) && ! is_bool($this->options['noCursorTimeout'])) { throw InvalidArgumentException::invalidType('"noCursorTimeout" option', $this->options['noCursorTimeout'], 'boolean'); } - if (isset($this->options['oplogReplay']) && ! is_bool($this->options['oplogReplay'])) { - throw InvalidArgumentException::invalidType('"oplogReplay" option', $this->options['oplogReplay'], 'boolean'); - } - if (isset($this->options['projection']) && ! is_document($this->options['projection'])) { throw InvalidArgumentException::expectedDocumentType('"projection" option', $this->options['projection']); } @@ -259,10 +226,6 @@ public function __construct(private string $databaseName, private string $collec throw InvalidArgumentException::invalidType('"skip" option', $this->options['skip'], 'integer'); } - if (isset($this->options['snapshot']) && ! is_bool($this->options['snapshot'])) { - throw InvalidArgumentException::invalidType('"snapshot" option', $this->options['snapshot'], 'boolean'); - } - if (isset($this->options['sort']) && ! is_document($this->options['sort'])) { throw InvalidArgumentException::expectedDocumentType('"sort" option', $this->options['sort']); } @@ -287,7 +250,6 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() * @throws UnsupportedException if read concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ @@ -329,28 +291,6 @@ public function getCommandDocument(): array // maxAwaitTimeMS is a Query level option so should not be considered here unset($options['maxAwaitTimeMS']); - $modifierFallback = [ - ['allowPartialResults', 'partial'], - ['comment', '$comment'], - ['hint', '$hint'], - ['maxScan', '$maxScan'], - ['max', '$max'], - ['maxTimeMS', '$maxTimeMS'], - ['min', '$min'], - ['returnKey', '$returnKey'], - ['showRecordId', '$showDiskLoc'], - ['sort', '$orderby'], - ['snapshot', '$snapshot'], - ]; - - foreach ($modifierFallback as $modifier) { - if (! isset($options[$modifier[0]]) && isset($options['modifiers'][$modifier[1]])) { - $options[$modifier[0]] = $options['modifiers'][$modifier[1]]; - } - } - - unset($options['modifiers']); - return $cmd + $options; } @@ -395,7 +335,7 @@ private function createQueryOptions(): array } } - foreach (['allowDiskUse', 'allowPartialResults', 'batchSize', 'comment', 'hint', 'limit', 'maxAwaitTimeMS', 'maxScan', 'maxTimeMS', 'noCursorTimeout', 'oplogReplay', 'projection', 'readConcern', 'returnKey', 'showRecordId', 'skip', 'snapshot', 'sort'] as $option) { + foreach (['allowDiskUse', 'allowPartialResults', 'batchSize', 'comment', 'hint', 'limit', 'maxAwaitTimeMS', 'maxTimeMS', 'noCursorTimeout', 'projection', 'readConcern', 'returnKey', 'showRecordId', 'skip', 'sort'] as $option) { if (isset($this->options[$option])) { $options[$option] = $this->options[$option]; } @@ -407,12 +347,6 @@ private function createQueryOptions(): array } } - if (! empty($this->options['modifiers'])) { - /** @psalm-var array|object */ - $modifiers = $this->options['modifiers']; - $options['modifiers'] = is_object($modifiers) ? document_to_array($modifiers) : $modifiers; - } - return $options; } } diff --git a/src/Operation/FindAndModify.php b/src/Operation/FindAndModify.php index 8b1f14f2e..27a888bc6 100644 --- a/src/Operation/FindAndModify.php +++ b/src/Operation/FindAndModify.php @@ -51,7 +51,7 @@ * @internal * @see https://mongodb.com/docs/manual/reference/command/findAndModify/ */ -final class FindAndModify implements Executable, Explainable +final class FindAndModify implements Explainable { private const WIRE_VERSION_FOR_HINT = 9; @@ -221,7 +221,6 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if hint or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) diff --git a/src/Operation/FindOne.php b/src/Operation/FindOne.php index 0290356ee..f685de5ae 100644 --- a/src/Operation/FindOne.php +++ b/src/Operation/FindOne.php @@ -31,7 +31,7 @@ * @see https://mongodb.com/docs/manual/tutorial/query-documents/ * @see https://mongodb.com/docs/manual/reference/operator/query-modifier/ */ -final class FindOne implements Executable, Explainable +final class FindOne implements Explainable { private Find $find; @@ -55,20 +55,11 @@ final class FindOne implements Executable, Explainable * * * max (document): The exclusive upper bound for a specific index. * - * * maxScan (integer): Maximum number of documents or index keys to scan - * when executing the query. - * - * This option has been deprecated since version 1.4. - * * * maxTimeMS (integer): The maximum amount of time to allow the query to - * run. If "$maxTimeMS" also exists in the modifiers document, this - * option will take precedence. + * run. * * * min (document): The inclusive upper bound for a specific index. * - * * modifiers (document): Meta-operators modifying the output or behavior - * of a query. - * * * projection (document): Limits the fields to return for the matching * document. * @@ -87,9 +78,7 @@ final class FindOne implements Executable, Explainable * * * skip (integer): The number of documents to skip before returning. * - * * sort (document): The order in which to return matching documents. If - * "$orderby" also exists in the modifiers document, this option will - * take precedence. + * * sort (document): The order in which to return matching documents. * * * let (document): Map of parameter names and values. Values must be * constant or closed expressions that do not reference document fields. @@ -117,7 +106,6 @@ public function __construct(string $databaseName, string $collectionName, array| /** * Execute the operation. * - * @see Executable::execute() * @throws UnsupportedException if collation or read concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ diff --git a/src/Operation/FindOneAndDelete.php b/src/Operation/FindOneAndDelete.php index bf70bfc4a..7c0f22ad0 100644 --- a/src/Operation/FindOneAndDelete.php +++ b/src/Operation/FindOneAndDelete.php @@ -30,7 +30,7 @@ * @see \MongoDB\Collection::findOneAndDelete() * @see https://mongodb.com/docs/manual/reference/command/findAndModify/ */ -final class FindOneAndDelete implements Executable, Explainable +final class FindOneAndDelete implements Explainable { private FindAndModify $findAndModify; @@ -107,7 +107,6 @@ public function __construct(string $databaseName, string $collectionName, array| /** * Execute the operation. * - * @see Executable::execute() * @throws UnsupportedException if collation or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ diff --git a/src/Operation/FindOneAndReplace.php b/src/Operation/FindOneAndReplace.php index b9a5bdf7d..5ea0c5a34 100644 --- a/src/Operation/FindOneAndReplace.php +++ b/src/Operation/FindOneAndReplace.php @@ -35,7 +35,7 @@ * @see \MongoDB\Collection::findOneAndReplace() * @see https://mongodb.com/docs/manual/reference/command/findAndModify/ */ -final class FindOneAndReplace implements Executable, Explainable +final class FindOneAndReplace implements Explainable { public const RETURN_DOCUMENT_BEFORE = 1; public const RETURN_DOCUMENT_AFTER = 2; @@ -150,7 +150,6 @@ public function __construct(string $databaseName, string $collectionName, array| /** * Execute the operation. * - * @see Executable::execute() * @throws UnsupportedException if collation or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ diff --git a/src/Operation/FindOneAndUpdate.php b/src/Operation/FindOneAndUpdate.php index 56db8cb8e..00e97972b 100644 --- a/src/Operation/FindOneAndUpdate.php +++ b/src/Operation/FindOneAndUpdate.php @@ -34,7 +34,7 @@ * @see \MongoDB\Collection::findOneAndUpdate() * @see https://mongodb.com/docs/manual/reference/command/findAndModify/ */ -final class FindOneAndUpdate implements Executable, Explainable +final class FindOneAndUpdate implements Explainable { public const RETURN_DOCUMENT_BEFORE = 1; public const RETURN_DOCUMENT_AFTER = 2; @@ -150,7 +150,6 @@ public function __construct(string $databaseName, string $collectionName, array| /** * Execute the operation. * - * @see Executable::execute() * @throws UnsupportedException if collation or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ diff --git a/src/Operation/InsertMany.php b/src/Operation/InsertMany.php index 9de46a1f0..70e149076 100644 --- a/src/Operation/InsertMany.php +++ b/src/Operation/InsertMany.php @@ -38,7 +38,7 @@ * @see \MongoDB\Collection::insertMany() * @see https://mongodb.com/docs/manual/reference/command/insert/ */ -final class InsertMany implements Executable +final class InsertMany { /** @var list */ private array $documents; @@ -114,7 +114,6 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ diff --git a/src/Operation/InsertOne.php b/src/Operation/InsertOne.php index 2c08d8db2..dff9f79f6 100644 --- a/src/Operation/InsertOne.php +++ b/src/Operation/InsertOne.php @@ -36,7 +36,7 @@ * @see \MongoDB\Collection::insertOne() * @see https://mongodb.com/docs/manual/reference/command/insert/ */ -final class InsertOne implements Executable +final class InsertOne { private array|object $document; @@ -97,7 +97,6 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ diff --git a/src/Operation/ListCollectionNames.php b/src/Operation/ListCollectionNames.php index fefa464af..3b32cd589 100644 --- a/src/Operation/ListCollectionNames.php +++ b/src/Operation/ListCollectionNames.php @@ -22,6 +22,7 @@ use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Server; use MongoDB\Exception\InvalidArgumentException; +use MongoDB\Model\CachingIterator; use MongoDB\Model\CallbackIterator; /** @@ -30,7 +31,7 @@ * @see \MongoDB\Database::listCollectionNames() * @see https://mongodb.com/docs/manual/reference/command/listCollections/ */ -final class ListCollectionNames implements Executable +final class ListCollectionNames { private ListCollectionsCommand $listCollections; @@ -67,15 +68,16 @@ public function __construct(string $databaseName, array $options = []) /** * Execute the operation. * - * @see Executable::execute() - * @return Iterator + * @return Iterator * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ public function execute(Server $server): Iterator { - return new CallbackIterator( - $this->listCollections->execute($server), - fn (array $collectionInfo): string => (string) $collectionInfo['name'], + return new CachingIterator( + new CallbackIterator( + $this->listCollections->execute($server), + fn (array $collectionInfo): string => (string) $collectionInfo['name'], + ), ); } } diff --git a/src/Operation/ListCollections.php b/src/Operation/ListCollections.php index efce4da48..a86b0a1e5 100644 --- a/src/Operation/ListCollections.php +++ b/src/Operation/ListCollections.php @@ -22,9 +22,9 @@ use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Server; use MongoDB\Exception\InvalidArgumentException; +use MongoDB\Model\CachingIterator; +use MongoDB\Model\CallbackIterator; use MongoDB\Model\CollectionInfo; -use MongoDB\Model\CollectionInfoCommandIterator; -use MongoDB\Model\CollectionInfoIterator; /** * Operation for the listCollections command. @@ -32,7 +32,7 @@ * @see \MongoDB\Database::listCollections() * @see https://mongodb.com/docs/manual/reference/command/listCollections/ */ -final class ListCollections implements Executable +final class ListCollections { private ListCollectionsCommand $listCollections; @@ -61,7 +61,7 @@ final class ListCollections implements Executable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(private string $databaseName, array $options = []) + public function __construct(string $databaseName, array $options = []) { $this->listCollections = new ListCollectionsCommand($databaseName, ['nameOnly' => false] + $options); } @@ -69,12 +69,19 @@ public function __construct(private string $databaseName, array $options = []) /** * Execute the operation. * - * @see Executable::execute() * @return Iterator * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server): CollectionInfoIterator + public function execute(Server $server): Iterator { - return new CollectionInfoCommandIterator($this->listCollections->execute($server), $this->databaseName); + /** @var Iterator $collections */ + $collections = $this->listCollections->execute($server); + + return new CachingIterator( + new CallbackIterator( + $collections, + fn (array $collectionInfo, int $key): CollectionInfo => new CollectionInfo($collectionInfo), + ), + ); } } diff --git a/src/Operation/ListDatabaseNames.php b/src/Operation/ListDatabaseNames.php index 0c6ee5437..74263fb53 100644 --- a/src/Operation/ListDatabaseNames.php +++ b/src/Operation/ListDatabaseNames.php @@ -33,7 +33,7 @@ * @see \MongoDB\Client::listDatabaseNames() * @see https://mongodb.com/docs/manual/reference/command/listDatabases/#mongodb-dbcommand-dbcmd.listDatabases */ -final class ListDatabaseNames implements Executable +final class ListDatabaseNames { private ListDatabasesCommand $listDatabases; @@ -69,7 +69,7 @@ public function __construct(array $options = []) /** * Execute the operation. * - * @see Executable::execute() + * @return Iterator * @throws UnexpectedValueException if the command response was malformed * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ diff --git a/src/Operation/ListDatabases.php b/src/Operation/ListDatabases.php index 1e67daafd..ab32ef7e6 100644 --- a/src/Operation/ListDatabases.php +++ b/src/Operation/ListDatabases.php @@ -17,15 +17,15 @@ namespace MongoDB\Operation; +use ArrayIterator; use Iterator; use MongoDB\Command\ListDatabases as ListDatabasesCommand; use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Server; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\UnexpectedValueException; +use MongoDB\Model\CallbackIterator; use MongoDB\Model\DatabaseInfo; -use MongoDB\Model\DatabaseInfoIterator; -use MongoDB\Model\DatabaseInfoLegacyIterator; /** * Operation for the ListDatabases command. @@ -33,7 +33,7 @@ * @see \MongoDB\Client::listDatabases() * @see https://mongodb.com/docs/manual/reference/command/listDatabases/#mongodb-dbcommand-dbcmd.listDatabases` */ -final class ListDatabases implements Executable +final class ListDatabases { private ListDatabasesCommand $listDatabases; @@ -69,13 +69,18 @@ public function __construct(array $options = []) /** * Execute the operation. * - * @see Executable::execute() * @return Iterator * @throws UnexpectedValueException if the command response was malformed * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server): DatabaseInfoIterator + public function execute(Server $server): Iterator { - return new DatabaseInfoLegacyIterator($this->listDatabases->execute($server)); + /** @var list $databases */ + $databases = $this->listDatabases->execute($server); + + return new CallbackIterator( + new ArrayIterator($databases), + fn (array $databaseInfo): DatabaseInfo => new DatabaseInfo($databaseInfo), + ); } } diff --git a/src/Operation/ListIndexes.php b/src/Operation/ListIndexes.php index a32b96b97..8fab516fc 100644 --- a/src/Operation/ListIndexes.php +++ b/src/Operation/ListIndexes.php @@ -20,15 +20,15 @@ use EmptyIterator; use Iterator; use MongoDB\Driver\Command; +use MongoDB\Driver\CursorInterface; use MongoDB\Driver\Exception\CommandException; use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Server; use MongoDB\Driver\Session; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Model\CachingIterator; +use MongoDB\Model\CallbackIterator; use MongoDB\Model\IndexInfo; -use MongoDB\Model\IndexInfoIterator; -use MongoDB\Model\IndexInfoIteratorIterator; use function is_integer; @@ -38,7 +38,7 @@ * @see \MongoDB\Collection::listIndexes() * @see https://mongodb.com/docs/manual/reference/command/listIndexes/ */ -final class ListIndexes implements Executable +final class ListIndexes { private const ERROR_CODE_DATABASE_NOT_FOUND = 60; private const ERROR_CODE_NAMESPACE_NOT_FOUND = 26; @@ -76,41 +76,10 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() * @return Iterator * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server): IndexInfoIterator - { - return $this->executeCommand($server); - } - - /** - * Create options for executing the command. - * - * Note: read preference is intentionally omitted, as the spec requires that - * the command be executed on the primary. - * - * @see https://php.net/manual/en/mongodb-driver-server.executecommand.php - */ - private function createOptions(): array - { - $options = []; - - if (isset($this->options['session'])) { - $options['session'] = $this->options['session']; - } - - return $options; - } - - /** - * Returns information for all indexes for this collection using the - * listIndexes command. - * - * @throws DriverRuntimeException for other driver errors (e.g. connection errors) - */ - private function executeCommand(Server $server): IndexInfoIteratorIterator + public function execute(Server $server): Iterator { $cmd = ['listIndexes' => $this->collectionName]; @@ -121,24 +90,45 @@ private function executeCommand(Server $server): IndexInfoIteratorIterator } try { + /** @var CursorInterface $cursor */ $cursor = $server->executeReadCommand($this->databaseName, new Command($cmd), $this->createOptions()); + $cursor->setTypeMap(['root' => 'array', 'document' => 'array']); } catch (CommandException $e) { /* The server may return an error if the collection does not exist. * Check for possible error codes (see: SERVER-20463) and return an * empty iterator instead of throwing. */ if ($e->getCode() === self::ERROR_CODE_NAMESPACE_NOT_FOUND || $e->getCode() === self::ERROR_CODE_DATABASE_NOT_FOUND) { - return new IndexInfoIteratorIterator(new EmptyIterator()); + return new EmptyIterator(); } throw $e; } - $cursor->setTypeMap(['root' => 'array', 'document' => 'array']); + return new CachingIterator( + new CallbackIterator( + $cursor, + fn (array $indexInfo): IndexInfo => new IndexInfo($indexInfo), + ), + ); + } - /** @var CachingIterator $iterator */ - $iterator = new CachingIterator($cursor); + /** + * Create options for executing the command. + * + * Note: read preference is intentionally omitted, as the spec requires that + * the command be executed on the primary. + * + * @see https://php.net/manual/en/mongodb-driver-server.executecommand.php + */ + private function createOptions(): array + { + $options = []; - return new IndexInfoIteratorIterator($iterator, $this->databaseName . '.' . $this->collectionName); + if (isset($this->options['session'])) { + $options['session'] = $this->options['session']; + } + + return $options; } } diff --git a/src/Operation/ListSearchIndexes.php b/src/Operation/ListSearchIndexes.php index 8a3850968..bd6810e98 100644 --- a/src/Operation/ListSearchIndexes.php +++ b/src/Operation/ListSearchIndexes.php @@ -35,7 +35,7 @@ * @see \MongoDB\Collection::listSearchIndexes() * @see https://mongodb.com/docs/manual/reference/command/listSearchIndexes/ */ -final class ListSearchIndexes implements Executable +final class ListSearchIndexes { private array $listSearchIndexesOptions; private array $aggregateOptions; @@ -68,7 +68,6 @@ public function __construct(private string $databaseName, private string $collec * Execute the operation. * * @return Iterator&Countable - * @see Executable::execute() * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if collation or read concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) diff --git a/src/Operation/MapReduce.php b/src/Operation/MapReduce.php deleted file mode 100644 index 197f340fa..000000000 --- a/src/Operation/MapReduce.php +++ /dev/null @@ -1,388 +0,0 @@ -options['bypassDocumentValidation']) && ! is_bool($this->options['bypassDocumentValidation'])) { - throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $this->options['bypassDocumentValidation'], 'boolean'); - } - - if (isset($this->options['collation']) && ! is_document($this->options['collation'])) { - throw InvalidArgumentException::expectedDocumentType('"collation" option', $this->options['collation']); - } - - if (isset($this->options['finalize']) && ! $this->options['finalize'] instanceof JavascriptInterface) { - throw InvalidArgumentException::invalidType('"finalize" option', $this->options['finalize'], JavascriptInterface::class); - } - - if (isset($this->options['jsMode']) && ! is_bool($this->options['jsMode'])) { - throw InvalidArgumentException::invalidType('"jsMode" option', $this->options['jsMode'], 'boolean'); - } - - if (isset($this->options['limit']) && ! is_integer($this->options['limit'])) { - throw InvalidArgumentException::invalidType('"limit" option', $this->options['limit'], 'integer'); - } - - if (isset($this->options['maxTimeMS']) && ! is_integer($this->options['maxTimeMS'])) { - throw InvalidArgumentException::invalidType('"maxTimeMS" option', $this->options['maxTimeMS'], 'integer'); - } - - if (isset($this->options['query']) && ! is_document($this->options['query'])) { - throw InvalidArgumentException::expectedDocumentType('"query" option', $this->options['query']); - } - - if (isset($this->options['readConcern']) && ! $this->options['readConcern'] instanceof ReadConcern) { - throw InvalidArgumentException::invalidType('"readConcern" option', $this->options['readConcern'], ReadConcern::class); - } - - if (isset($this->options['readPreference']) && ! $this->options['readPreference'] instanceof ReadPreference) { - throw InvalidArgumentException::invalidType('"readPreference" option', $this->options['readPreference'], ReadPreference::class); - } - - if (isset($this->options['scope']) && ! is_document($this->options['scope'])) { - throw InvalidArgumentException::expectedDocumentType('"scope" option', $this->options['scope']); - } - - if (isset($this->options['session']) && ! $this->options['session'] instanceof Session) { - throw InvalidArgumentException::invalidType('"session" option', $this->options['session'], Session::class); - } - - if (isset($this->options['sort']) && ! is_document($this->options['sort'])) { - throw InvalidArgumentException::expectedDocumentType('"sort" option', $this->options['sort']); - } - - if (isset($this->options['typeMap']) && ! is_array($this->options['typeMap'])) { - throw InvalidArgumentException::invalidType('"typeMap" option', $this->options['typeMap'], 'array'); - } - - if (isset($this->options['verbose']) && ! is_bool($this->options['verbose'])) { - throw InvalidArgumentException::invalidType('"verbose" option', $this->options['verbose'], 'boolean'); - } - - if (isset($this->options['writeConcern']) && ! $this->options['writeConcern'] instanceof WriteConcern) { - throw InvalidArgumentException::invalidType('"writeConcern" option', $this->options['writeConcern'], WriteConcern::class); - } - - if (isset($this->options['bypassDocumentValidation']) && ! $this->options['bypassDocumentValidation']) { - unset($this->options['bypassDocumentValidation']); - } - - if (isset($this->options['readConcern']) && $this->options['readConcern']->isDefault()) { - unset($this->options['readConcern']); - } - - if (isset($this->options['writeConcern']) && $this->options['writeConcern']->isDefault()) { - unset($this->options['writeConcern']); - } - - // Handle deprecation of CodeWScope - if ($map->getScope() !== null) { - @trigger_error('Use of Javascript with scope in "$map" argument for MapReduce is deprecated. Put all scope variables in the "scope" option of the MapReduce operation.', E_USER_DEPRECATED); - } - - if ($reduce->getScope() !== null) { - @trigger_error('Use of Javascript with scope in "$reduce" argument for MapReduce is deprecated. Put all scope variables in the "scope" option of the MapReduce operation.', E_USER_DEPRECATED); - } - - if (isset($this->options['finalize']) && $this->options['finalize']->getScope() !== null) { - @trigger_error('Use of Javascript with scope in "finalize" option for MapReduce is deprecated. Put all scope variables in the "scope" option of the MapReduce operation.', E_USER_DEPRECATED); - } - - $this->checkOutDeprecations($out); - - $this->out = $out; - } - - /** - * Execute the operation. - * - * @see Executable::execute() - * @throws UnexpectedValueException if the command response was malformed - * @throws UnsupportedException if read concern or write concern is used and unsupported - * @throws DriverRuntimeException for other driver errors (e.g. connection errors) - */ - public function execute(Server $server): MapReduceResult - { - $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); - if ($inTransaction) { - if (isset($this->options['readConcern'])) { - throw UnsupportedException::readConcernNotSupportedInTransaction(); - } - - if (isset($this->options['writeConcern'])) { - throw UnsupportedException::writeConcernNotSupportedInTransaction(); - } - } - - $hasOutputCollection = ! is_mapreduce_output_inline($this->out); - - $command = $this->createCommand(); - $options = $this->createOptions($hasOutputCollection); - - /* If the mapReduce operation results in a write, use - * executeReadWriteCommand to ensure we're handling the writeConcern - * option. - * In other cases, we use executeCommand as this will prevent the - * mapReduce operation from being retried when retryReads is enabled. - * See https://github.com/mongodb/specifications/blob/master/source/retryable-reads/retryable-reads.rst#unsupported-read-operations. */ - $cursor = $hasOutputCollection - ? $server->executeReadWriteCommand($this->databaseName, $command, $options) - : $server->executeCommand($this->databaseName, $command, $options); - - if (isset($this->options['typeMap']) && ! $hasOutputCollection) { - $cursor->setTypeMap(create_field_path_type_map($this->options['typeMap'], 'results.$')); - } - - $result = current($cursor->toArray()); - assert($result instanceof stdClass); - - $getIterator = $this->createGetIteratorCallable($result, $server); - - return new MapReduceResult($getIterator, $result); - } - - private function checkOutDeprecations(string|array|object $out): void - { - if (is_string($out)) { - return; - } - - $out = document_to_array($out); - - if (isset($out['nonAtomic']) && ! $out['nonAtomic']) { - @trigger_error('Specifying false for "out.nonAtomic" is deprecated.', E_USER_DEPRECATED); - } - - if (isset($out['sharded']) && ! $out['sharded']) { - @trigger_error('Specifying false for "out.sharded" is deprecated.', E_USER_DEPRECATED); - } - } - - /** - * Create the mapReduce command. - */ - private function createCommand(): Command - { - $cmd = [ - 'mapReduce' => $this->collectionName, - 'map' => $this->map, - 'reduce' => $this->reduce, - 'out' => $this->out, - ]; - - foreach (['bypassDocumentValidation', 'comment', 'finalize', 'jsMode', 'limit', 'maxTimeMS', 'verbose'] as $option) { - if (isset($this->options[$option])) { - $cmd[$option] = $this->options[$option]; - } - } - - foreach (['collation', 'query', 'scope', 'sort'] as $option) { - if (isset($this->options[$option])) { - $cmd[$option] = (object) $this->options[$option]; - } - } - - return new Command($cmd); - } - - /** - * Creates a callable for MapReduceResult::getIterator(). - * - * @psalm-return MapReduceCallable - * @throws UnexpectedValueException if the command response was malformed - */ - private function createGetIteratorCallable(stdClass $result, Server $server): callable - { - // Inline results can be wrapped with an ArrayIterator - if (isset($result->results) && is_array($result->results)) { - $results = $result->results; - - return fn () => new ArrayIterator($results); - } - - if (isset($result->result) && (is_string($result->result) || is_object($result->result))) { - $options = isset($this->options['typeMap']) ? ['typeMap' => $this->options['typeMap']] : []; - - $find = is_string($result->result) - ? new Find($this->databaseName, $result->result, [], $options) - : new Find($result->result->db, $result->result->collection, [], $options); - - return fn () => $find->execute($server); - } - - throw new UnexpectedValueException('mapReduce command did not return inline results or an output collection'); - } - - /** - * Create options for executing the command. - * - * @see https://php.net/manual/en/mongodb-driver-server.executereadcommand.php - * @see https://php.net/manual/en/mongodb-driver-server.executereadwritecommand.php - */ - private function createOptions(bool $hasOutputCollection): array - { - $options = []; - - if (isset($this->options['readConcern'])) { - $options['readConcern'] = $this->options['readConcern']; - } - - if (! $hasOutputCollection && isset($this->options['readPreference'])) { - $options['readPreference'] = $this->options['readPreference']; - } - - if (isset($this->options['session'])) { - $options['session'] = $this->options['session']; - } - - if ($hasOutputCollection && isset($this->options['writeConcern'])) { - $options['writeConcern'] = $this->options['writeConcern']; - } - - return $options; - } -} diff --git a/src/Operation/ModifyCollection.php b/src/Operation/ModifyCollection.php index 45a5f1c4b..7b40f2f8c 100644 --- a/src/Operation/ModifyCollection.php +++ b/src/Operation/ModifyCollection.php @@ -33,7 +33,7 @@ * @see \MongoDB\Database::modifyCollection() * @see https://mongodb.com/docs/manual/reference/command/collMod/ */ -final class ModifyCollection implements Executable +final class ModifyCollection { /** * Constructs a collMod command. @@ -83,7 +83,6 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() * @return array|object Command result document * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ diff --git a/src/Operation/RenameCollection.php b/src/Operation/RenameCollection.php index 078bf487f..5804bb474 100644 --- a/src/Operation/RenameCollection.php +++ b/src/Operation/RenameCollection.php @@ -36,7 +36,7 @@ * @see \MongoDB\Database::renameCollection() * @see https://mongodb.com/docs/manual/reference/command/renameCollection/ */ -final class RenameCollection implements Executable +final class RenameCollection { private string $fromNamespace; @@ -97,7 +97,6 @@ public function __construct(string $fromDatabaseName, string $fromCollectionName /** * Execute the operation. * - * @see Executable::execute() * @return array|object Command result document * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) diff --git a/src/Operation/ReplaceOne.php b/src/Operation/ReplaceOne.php index ab4944b27..3133ea034 100644 --- a/src/Operation/ReplaceOne.php +++ b/src/Operation/ReplaceOne.php @@ -34,7 +34,7 @@ * @see \MongoDB\Collection::replaceOne() * @see https://mongodb.com/docs/manual/reference/command/update/ */ -final class ReplaceOne implements Executable +final class ReplaceOne { private Update $update; @@ -103,7 +103,6 @@ public function __construct(string $databaseName, string $collectionName, array| /** * Execute the operation. * - * @see Executable::execute() * @throws UnsupportedException if collation is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ diff --git a/src/Operation/Update.php b/src/Operation/Update.php index 79f64fecb..3e2da46af 100644 --- a/src/Operation/Update.php +++ b/src/Operation/Update.php @@ -45,7 +45,7 @@ * @internal * @see https://mongodb.com/docs/manual/reference/command/update/ */ -final class Update implements Executable, Explainable +final class Update implements Explainable { private const WIRE_VERSION_FOR_HINT = 8; @@ -164,7 +164,6 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() * @throws UnsupportedException if hint or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ diff --git a/src/Operation/UpdateMany.php b/src/Operation/UpdateMany.php index ac1f932a7..97865288c 100644 --- a/src/Operation/UpdateMany.php +++ b/src/Operation/UpdateMany.php @@ -32,7 +32,7 @@ * @see \MongoDB\Collection::updateMany() * @see https://mongodb.com/docs/manual/reference/command/update/ */ -final class UpdateMany implements Executable, Explainable +final class UpdateMany implements Explainable { private Update $update; @@ -97,7 +97,6 @@ public function __construct(string $databaseName, string $collectionName, array| /** * Execute the operation. * - * @see Executable::execute() * @throws UnsupportedException if collation is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ diff --git a/src/Operation/UpdateOne.php b/src/Operation/UpdateOne.php index d1026e65f..794d80058 100644 --- a/src/Operation/UpdateOne.php +++ b/src/Operation/UpdateOne.php @@ -32,7 +32,7 @@ * @see \MongoDB\Collection::updateOne() * @see https://mongodb.com/docs/manual/reference/command/update/ */ -final class UpdateOne implements Executable, Explainable +final class UpdateOne implements Explainable { private Update $update; @@ -97,7 +97,6 @@ public function __construct(string $databaseName, string $collectionName, array| /** * Execute the operation. * - * @see Executable::execute() * @throws UnsupportedException if collation is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ diff --git a/src/Operation/UpdateSearchIndex.php b/src/Operation/UpdateSearchIndex.php index 9aff80662..4543914bb 100644 --- a/src/Operation/UpdateSearchIndex.php +++ b/src/Operation/UpdateSearchIndex.php @@ -31,7 +31,7 @@ * @see \MongoDB\Collection::updateSearchIndexes() * @see https://mongodb.com/docs/manual/reference/command/updateSearchIndexes/ */ -final class UpdateSearchIndex implements Executable +final class UpdateSearchIndex { private object $definition; @@ -61,7 +61,6 @@ public function __construct(private string $databaseName, private string $collec /** * Execute the operation. * - * @see Executable::execute() * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ diff --git a/src/Operation/Watch.php b/src/Operation/Watch.php index 45074b2ca..3a09fd0a1 100644 --- a/src/Operation/Watch.php +++ b/src/Operation/Watch.php @@ -56,9 +56,8 @@ * @see \MongoDB\Collection::watch() * @see https://mongodb.com/docs/manual/changeStreams/ */ -final class Watch implements Executable, /* @internal */ CommandSubscriber +final class Watch implements /* @internal */ CommandSubscriber { - public const FULL_DOCUMENT_DEFAULT = 'default'; public const FULL_DOCUMENT_UPDATE_LOOKUP = 'updateLookup'; public const FULL_DOCUMENT_WHEN_AVAILABLE = 'whenAvailable'; public const FULL_DOCUMENT_REQUIRED = 'required'; @@ -264,7 +263,6 @@ public function __construct(private Manager $manager, ?string $databaseName, pri /** * Execute the operation. * - * @see Executable::execute() * @throws UnsupportedException if collation or read concern is used and unsupported * @throws RuntimeException for other driver errors (e.g. connection errors) */ diff --git a/src/functions.php b/src/functions.php index 5d66bd13a..aca396723 100644 --- a/src/functions.php +++ b/src/functions.php @@ -387,24 +387,6 @@ function is_last_pipeline_operator_write(array $pipeline): bool return $key === '$merge' || $key === '$out'; } -/** - * Return whether the "out" option for a mapReduce operation is "inline". - * - * This is used to determine if a mapReduce command requires a primary. - * - * @internal - * @see https://mongodb.com/docs/manual/reference/command/mapReduce/#output-inline - * @param string|array|object $out Output specification - */ -function is_mapreduce_output_inline(string|array|object $out): bool -{ - if (! is_array($out) && ! is_object($out)) { - return false; - } - - return array_key_first(document_to_array($out)) === 'inline'; -} - /** * Return whether the write concern is acknowledged. * diff --git a/tests/ClientFunctionalTest.php b/tests/ClientFunctionalTest.php index ca0ea2722..aa10d4a19 100644 --- a/tests/ClientFunctionalTest.php +++ b/tests/ClientFunctionalTest.php @@ -2,6 +2,7 @@ namespace MongoDB\Tests; +use Iterator; use MongoDB\Builder\Pipeline; use MongoDB\Builder\Query; use MongoDB\Builder\Stage; @@ -12,7 +13,6 @@ use MongoDB\Driver\Monitoring\CommandSubscriber; use MongoDB\Driver\Session; use MongoDB\Model\DatabaseInfo; -use MongoDB\Model\DatabaseInfoIterator; use function call_user_func; use function is_callable; @@ -62,7 +62,7 @@ public function testListDatabases(): void $databases = $this->client->listDatabases(); - $this->assertInstanceOf(DatabaseInfoIterator::class, $databases); + $this->assertInstanceOf(Iterator::class, $databases); foreach ($databases as $database) { $this->assertInstanceOf(DatabaseInfo::class, $database); diff --git a/tests/Collection/CollectionFunctionalTest.php b/tests/Collection/CollectionFunctionalTest.php index a919e38d2..4a75c8525 100644 --- a/tests/Collection/CollectionFunctionalTest.php +++ b/tests/Collection/CollectionFunctionalTest.php @@ -3,7 +3,6 @@ namespace MongoDB\Tests\Collection; use Closure; -use MongoDB\BSON\Javascript; use MongoDB\Codec\Encoder; use MongoDB\Collection; use MongoDB\Database; @@ -13,11 +12,9 @@ use MongoDB\Driver\WriteConcern; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\UnsupportedException; -use MongoDB\MapReduceResult; use MongoDB\Operation\Count; use MongoDB\Tests\CommandObserver; use PHPUnit\Framework\Attributes\DataProvider; -use PHPUnit\Framework\Attributes\Group; use TypeError; use function array_filter; @@ -26,7 +23,6 @@ use function json_encode; use function str_contains; use function usort; -use function version_compare; use const JSON_THROW_ON_ERROR; @@ -421,35 +417,6 @@ public function testWithOptionsPassesOptions(): void $this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW()); } - #[Group('matrix-testing-exclude-server-4.4-driver-4.0')] - #[Group('matrix-testing-exclude-server-4.4-driver-4.2')] - #[Group('matrix-testing-exclude-server-5.0-driver-4.0')] - #[Group('matrix-testing-exclude-server-5.0-driver-4.2')] - public function testMapReduce(): void - { - $this->createFixtures(3); - - $map = new Javascript('function() { emit(1, this.x); }'); - $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); - $out = ['inline' => 1]; - - $result = $this->assertDeprecated( - fn () => $this->collection->mapReduce($map, $reduce, $out), - ); - - $this->assertInstanceOf(MapReduceResult::class, $result); - $expected = [ - [ '_id' => 1.0, 'value' => 66.0 ], - ]; - - $this->assertSameDocuments($expected, $result); - - if (version_compare($this->getServerVersion(), '4.3.0', '<')) { - $this->assertGreaterThanOrEqual(0, $result->getExecutionTimeMS()); - $this->assertNotEmpty($result->getCounts()); - } - } - public static function collectionMethodClosures() { return [ @@ -662,19 +629,6 @@ function($collection, $session, $options = []) { ], */ - /* Disabled, as it's illegal to use mapReduce command in transactions - 'mapReduce' => [ - function($collection, $session, $options = []) { - $collection->mapReduce( - new \MongoDB\BSON\Javascript('function() { emit(this.state, this.pop); }'), - new \MongoDB\BSON\Javascript('function(key, values) { return Array.sum(values) }'), - ['inline' => 1], - ['session' => $session] + $options - ); - }, 'rw' - ], - */ - 'replaceOne' => [ function ($collection, $session, $options = []): void { $collection->replaceOne( diff --git a/tests/Database/CollectionManagementFunctionalTest.php b/tests/Database/CollectionManagementFunctionalTest.php index 485bcd8e5..e3a3ca5fa 100644 --- a/tests/Database/CollectionManagementFunctionalTest.php +++ b/tests/Database/CollectionManagementFunctionalTest.php @@ -2,9 +2,9 @@ namespace MongoDB\Tests\Database; +use Iterator; use MongoDB\Driver\BulkWrite; use MongoDB\Model\CollectionInfo; -use MongoDB\Model\CollectionInfoIterator; /** * Functional tests for collection management methods. @@ -57,7 +57,7 @@ public function testListCollections(): void $this->assertCommandSucceeded($commandResult); $collections = $this->database->listCollections(); - $this->assertInstanceOf(CollectionInfoIterator::class, $collections); + $this->assertInstanceOf(Iterator::class, $collections); foreach ($collections as $collection) { $this->assertInstanceOf(CollectionInfo::class, $collection); @@ -73,7 +73,7 @@ public function testListCollectionsWithFilter(): void $options = ['filter' => ['name' => $collectionName]]; $collections = $this->database->listCollections($options); - $this->assertInstanceOf(CollectionInfoIterator::class, $collections); + $this->assertInstanceOf(Iterator::class, $collections); foreach ($collections as $collection) { $this->assertInstanceOf(CollectionInfo::class, $collection); diff --git a/tests/FunctionsTest.php b/tests/FunctionsTest.php index 999c33247..bf64b03ee 100644 --- a/tests/FunctionsTest.php +++ b/tests/FunctionsTest.php @@ -18,7 +18,6 @@ use function MongoDB\is_builder_pipeline; use function MongoDB\is_first_key_operator; use function MongoDB\is_last_pipeline_operator_write; -use function MongoDB\is_mapreduce_output_inline; use function MongoDB\is_pipeline; use function MongoDB\is_write_concern_acknowledged; @@ -161,20 +160,6 @@ public function testIsFirstKeyOperatorArgumentTypeCheck($document): void is_first_key_operator($document); } - #[DataProvider('provideDocumentCasts')] - public function testIsMapReduceOutputInlineWithDocumentValues(callable $cast): void - { - $this->assertTrue(is_mapreduce_output_inline($cast(['inline' => 1]))); - // Note: only the key is significant - $this->assertTrue(is_mapreduce_output_inline($cast(['inline' => 0]))); - $this->assertFalse(is_mapreduce_output_inline($cast(['replace' => 'collectionName']))); - } - - public function testIsMapReduceOutputInlineWithStringValue(): void - { - $this->assertFalse(is_mapreduce_output_inline('collectionName')); - } - #[DataProvider('provideTypeMapValues')] public function testCreateFieldPathTypeMap(array $expected, array $typeMap, $fieldPath = 'field'): void { diff --git a/tests/Model/IndexInfoFunctionalTest.php b/tests/Model/IndexInfoFunctionalTest.php index 33537382f..824507ec6 100644 --- a/tests/Model/IndexInfoFunctionalTest.php +++ b/tests/Model/IndexInfoFunctionalTest.php @@ -4,7 +4,6 @@ use MongoDB\Collection; use MongoDB\Tests\FunctionalTestCase; -use PHPUnit\Framework\Attributes\Group; class IndexInfoFunctionalTest extends FunctionalTestCase { @@ -33,27 +32,6 @@ public function testIs2dSphere(): void $this->assertEquals(3, $index['2dsphereIndexVersion']); } - #[Group('matrix-testing-exclude-server-5.0-driver-4.0')] - #[Group('matrix-testing-exclude-server-5.0-driver-4.2')] - #[Group('matrix-testing-exclude-server-5.0-driver-4.4')] - public function testIsGeoHaystack(): void - { - $this->skipIfGeoHaystackIndexIsNotSupported(); - - $indexName = $this->collection->createIndex(['pos' => 'geoHaystack', 'x' => 1], ['bucketSize' => 5]); - $result = $this->collection->listIndexes(); - - $result->rewind(); - $result->next(); - $index = $result->current(); - - $this->assertEquals($indexName, $index->getName()); - $this->assertDeprecated(function () use ($index): void { - $this->assertTrue($index->isGeoHaystack()); - }); - $this->assertEquals(5, $index['bucketSize']); - } - public function testIsText(): void { $indexName = $this->collection->createIndex(['x' => 'text']); diff --git a/tests/Model/IndexInfoTest.php b/tests/Model/IndexInfoTest.php index 30cb26aea..189b522bd 100644 --- a/tests/Model/IndexInfoTest.php +++ b/tests/Model/IndexInfoTest.php @@ -14,17 +14,12 @@ public function testBasicIndex(): void 'v' => 1, 'key' => ['x' => 1], 'name' => 'x_1', - 'ns' => 'foo.bar', ]); $this->assertSame(1, $info->getVersion()); $this->assertSame(['x' => 1], $info->getKey()); $this->assertSame('x_1', $info->getName()); - $this->assertSame('foo.bar', $info->getNamespace()); $this->assertFalse($info->is2dSphere()); - $this->assertDeprecated(function () use ($info): void { - $this->assertFalse($info->isGeoHaystack()); - }); $this->assertFalse($info->isSparse()); $this->assertFalse($info->isText()); $this->assertFalse($info->isTtl()); @@ -37,18 +32,13 @@ public function testSparseIndex(): void 'v' => 1, 'key' => ['y' => 1], 'name' => 'y_sparse', - 'ns' => 'foo.bar', 'sparse' => true, ]); $this->assertSame(1, $info->getVersion()); $this->assertSame(['y' => 1], $info->getKey()); $this->assertSame('y_sparse', $info->getName()); - $this->assertSame('foo.bar', $info->getNamespace()); $this->assertFalse($info->is2dSphere()); - $this->assertDeprecated(function () use ($info): void { - $this->assertFalse($info->isGeoHaystack()); - }); $this->assertTrue($info->isSparse()); $this->assertFalse($info->isText()); $this->assertFalse($info->isTtl()); @@ -61,18 +51,13 @@ public function testUniqueIndex(): void 'v' => 1, 'key' => ['z' => 1], 'name' => 'z_unique', - 'ns' => 'foo.bar', 'unique' => true, ]); $this->assertSame(1, $info->getVersion()); $this->assertSame(['z' => 1], $info->getKey()); $this->assertSame('z_unique', $info->getName()); - $this->assertSame('foo.bar', $info->getNamespace()); $this->assertFalse($info->is2dSphere()); - $this->assertDeprecated(function () use ($info): void { - $this->assertFalse($info->isGeoHaystack()); - }); $this->assertFalse($info->isSparse()); $this->assertFalse($info->isText()); $this->assertFalse($info->isTtl()); @@ -85,18 +70,13 @@ public function testTtlIndex(): void 'v' => 1, 'key' => ['z' => 1], 'name' => 'z_unique', - 'ns' => 'foo.bar', 'expireAfterSeconds' => 100, ]); $this->assertSame(1, $info->getVersion()); $this->assertSame(['z' => 1], $info->getKey()); $this->assertSame('z_unique', $info->getName()); - $this->assertSame('foo.bar', $info->getNamespace()); $this->assertFalse($info->is2dSphere()); - $this->assertDeprecated(function () use ($info): void { - $this->assertFalse($info->isGeoHaystack()); - }); $this->assertFalse($info->isSparse()); $this->assertFalse($info->isText()); $this->assertTrue($info->isTtl()); @@ -111,7 +91,6 @@ public function testDebugInfo(): void 'v' => 1, 'key' => ['x' => 1], 'name' => 'x_1', - 'ns' => 'foo.bar', ]; $info = new IndexInfo($expectedInfo); @@ -124,7 +103,6 @@ public function testImplementsArrayAccess(): void 'v' => 1, 'key' => ['x' => 1], 'name' => 'x_1', - 'ns' => 'foo.bar', ]); $this->assertInstanceOf('ArrayAccess', $info); @@ -138,7 +116,6 @@ public function testOffsetSetCannotBeCalled(): void 'v' => 1, 'key' => ['x' => 1], 'name' => 'x_1', - 'ns' => 'foo.bar', ]); $this->expectException(BadMethodCallException::class); @@ -152,7 +129,6 @@ public function testOffsetUnsetCannotBeCalled(): void 'v' => 1, 'key' => ['x' => 1], 'name' => 'x_1', - 'ns' => 'foo.bar', ]); $this->expectException(BadMethodCallException::class); @@ -166,40 +142,12 @@ public function testIs2dSphere(): void 'v' => 2, 'key' => ['pos' => '2dsphere'], 'name' => 'pos_2dsphere', - 'ns' => 'foo.bar', ]); $this->assertSame(2, $info->getVersion()); $this->assertSame(['pos' => '2dsphere'], $info->getKey()); $this->assertSame('pos_2dsphere', $info->getName()); - $this->assertSame('foo.bar', $info->getNamespace()); $this->assertTrue($info->is2dSphere()); - $this->assertDeprecated(function () use ($info): void { - $this->assertFalse($info->isGeoHaystack()); - }); - $this->assertFalse($info->isSparse()); - $this->assertFalse($info->isText()); - $this->assertFalse($info->isTtl()); - $this->assertFalse($info->isUnique()); - } - - public function testIsGeoHaystack(): void - { - $info = new IndexInfo([ - 'v' => 2, - 'key' => ['pos2' => 'geoHaystack', 'x' => 1], - 'name' => 'pos2_geoHaystack_x_1', - 'ns' => 'foo.bar', - ]); - - $this->assertSame(2, $info->getVersion()); - $this->assertSame(['pos2' => 'geoHaystack', 'x' => 1], $info->getKey()); - $this->assertSame('pos2_geoHaystack_x_1', $info->getName()); - $this->assertSame('foo.bar', $info->getNamespace()); - $this->assertFalse($info->is2dSphere()); - $this->assertDeprecated(function () use ($info): void { - $this->assertTrue($info->isGeoHaystack()); - }); $this->assertFalse($info->isSparse()); $this->assertFalse($info->isText()); $this->assertFalse($info->isTtl()); @@ -212,17 +160,12 @@ public function testIsText(): void 'v' => 2, 'key' => ['_fts' => 'text', '_ftsx' => 1], 'name' => 'title_text_description_text', - 'ns' => 'foo.bar', ]); $this->assertSame(2, $info->getVersion()); $this->assertSame(['_fts' => 'text', '_ftsx' => 1], $info->getKey()); $this->assertSame('title_text_description_text', $info->getName()); - $this->assertSame('foo.bar', $info->getNamespace()); $this->assertFalse($info->is2dSphere()); - $this->assertDeprecated(function () use ($info): void { - $this->assertFalse($info->isGeoHaystack()); - }); $this->assertFalse($info->isSparse()); $this->assertTrue($info->isText()); $this->assertFalse($info->isTtl()); diff --git a/tests/Operation/ExplainFunctionalTest.php b/tests/Operation/ExplainFunctionalTest.php index e40024bff..4c5949828 100644 --- a/tests/Operation/ExplainFunctionalTest.php +++ b/tests/Operation/ExplainFunctionalTest.php @@ -158,30 +158,6 @@ function (array $event): void { ); } - public function testFindModifiers(): void - { - $this->createFixtures(3); - - $operation = new Find( - $this->getDatabaseName(), - $this->getCollectionName(), - [], - ['modifiers' => ['$orderby' => ['_id' => 1]]], - ); - - (new CommandObserver())->observe( - function () use ($operation): void { - $explainOperation = new Explain($this->getDatabaseName(), $operation, ['typeMap' => ['root' => 'array', 'document' => 'array']]); - $explainOperation->execute($this->getPrimaryServer()); - }, - function (array $event): void { - $command = $event['started']->getCommand(); - $this->assertObjectHasProperty('sort', $command->explain); - $this->assertObjectNotHasProperty('modifiers', $command->explain); - }, - ); - } - #[DataProvider('provideVerbosityInformation')] public function testFindOne($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { diff --git a/tests/Operation/FindFunctionalTest.php b/tests/Operation/FindFunctionalTest.php index 81745c23f..0927b29fb 100644 --- a/tests/Operation/FindFunctionalTest.php +++ b/tests/Operation/FindFunctionalTest.php @@ -2,10 +2,8 @@ namespace MongoDB\Tests\Operation; -use MongoDB\BSON\Document; use MongoDB\Driver\BulkWrite; use MongoDB\Driver\ReadPreference; -use MongoDB\Model\BSONDocument; use MongoDB\Operation\CreateIndexes; use MongoDB\Operation\Find; use MongoDB\Tests\CommandObserver; @@ -14,7 +12,6 @@ use PHPUnit\Framework\Attributes\DataProvider; use stdClass; -use function is_array; use function microtime; class FindFunctionalTest extends FunctionalTestCase @@ -38,45 +35,6 @@ function (array $event) use ($expectedQuery): void { ); } - #[DataProvider('provideModifierDocuments')] - public function testModifierDocuments($modifiers, stdClass $expectedSort): void - { - (new CommandObserver())->observe( - function () use ($modifiers): void { - // @todo revert this lines after PHPC-2457 - if (is_array($modifiers)) { - $modifiers = [...$modifiers]; - } - - $operation = new Find( - $this->getDatabaseName(), - $this->getCollectionName(), - [], - ['modifiers' => $modifiers], - ); - - $this->assertDeprecated( - fn () => $operation->execute($this->getPrimaryServer()), - ); - }, - function (array $event) use ($expectedSort): void { - $this->assertEquals($expectedSort, $event['started']->getCommand()->sort ?? null); - }, - ); - } - - public static function provideModifierDocuments(): array - { - $expectedSort = (object) ['x' => 1]; - - return [ - 'array' => [['$orderby' => ['x' => 1]], $expectedSort], - 'object' => [(object) ['$orderby' => ['x' => 1]], $expectedSort], - 'Serializable' => [new BSONDocument(['$orderby' => ['x' => 1]]), $expectedSort], - 'Document' => [Document::fromPHP(['$orderby' => ['x' => 1]]), $expectedSort], - ]; - } - public function testDefaultReadConcernIsOmitted(): void { (new CommandObserver())->observe( diff --git a/tests/Operation/FindTest.php b/tests/Operation/FindTest.php index d7eccf7f3..97cc1d388 100644 --- a/tests/Operation/FindTest.php +++ b/tests/Operation/FindTest.php @@ -38,11 +38,8 @@ public static function provideInvalidConstructorOptions() 'limit' => self::getInvalidIntegerValues(), 'max' => self::getInvalidDocumentValues(), 'maxAwaitTimeMS' => self::getInvalidIntegerValues(), - 'maxScan' => self::getInvalidIntegerValues(), 'maxTimeMS' => self::getInvalidIntegerValues(), 'min' => self::getInvalidDocumentValues(), - 'modifiers' => self::getInvalidDocumentValues(), - 'oplogReplay' => self::getInvalidBooleanValues(), 'projection' => self::getInvalidDocumentValues(), 'readConcern' => self::getInvalidReadConcernValues(), 'readPreference' => self::getInvalidReadPreferenceValues(), @@ -50,7 +47,6 @@ public static function provideInvalidConstructorOptions() 'session' => self::getInvalidSessionValues(), 'showRecordId' => self::getInvalidBooleanValues(), 'skip' => self::getInvalidIntegerValues(), - 'snapshot' => self::getInvalidBooleanValues(), 'sort' => self::getInvalidDocumentValues(), 'typeMap' => self::getInvalidArrayValues(), ]); @@ -70,7 +66,6 @@ public static function provideInvalidConstructorCursorTypeOptions() public function testExplainableCommandDocument(): void { - // all options except deprecated "snapshot" and "maxScan" $options = [ 'allowDiskUse' => true, 'allowPartialResults' => true, @@ -83,7 +78,6 @@ public function testExplainableCommandDocument(): void 'maxTimeMS' => 100, 'min' => ['x' => 10], 'noCursorTimeout' => true, - 'oplogReplay' => true, 'projection' => ['_id' => 0], 'readConcern' => new ReadConcern(ReadConcern::LOCAL), 'returnKey' => true, @@ -94,7 +88,6 @@ public function testExplainableCommandDocument(): void // Intentionally omitted options 'cursorType' => Find::NON_TAILABLE, 'maxAwaitTimeMS' => 500, - 'modifiers' => ['foo' => 'bar'], 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), 'typeMap' => ['root' => 'array'], ]; @@ -111,7 +104,6 @@ public function testExplainableCommandDocument(): void 'limit' => 15, 'maxTimeMS' => 100, 'noCursorTimeout' => true, - 'oplogReplay' => true, 'projection' => ['_id' => 0], 'readConcern' => new ReadConcern(ReadConcern::LOCAL), 'returnKey' => true, diff --git a/tests/Operation/ListCollectionsFunctionalTest.php b/tests/Operation/ListCollectionsFunctionalTest.php index 259e6cbdb..ed8cb9b14 100644 --- a/tests/Operation/ListCollectionsFunctionalTest.php +++ b/tests/Operation/ListCollectionsFunctionalTest.php @@ -2,8 +2,8 @@ namespace MongoDB\Tests\Operation; +use Iterator; use MongoDB\Model\CollectionInfo; -use MongoDB\Model\CollectionInfoIterator; use MongoDB\Operation\DropDatabase; use MongoDB\Operation\InsertOne; use MongoDB\Operation\ListCollections; @@ -26,7 +26,7 @@ public function testListCollectionsForNewlyCreatedDatabase(): void $operation = new ListCollections($this->getDatabaseName(), ['filter' => ['name' => $this->getCollectionName()]]); $collections = $operation->execute($server); - $this->assertInstanceOf(CollectionInfoIterator::class, $collections); + $this->assertInstanceOf(Iterator::class, $collections); $this->assertCount(1, $collections); @@ -51,12 +51,14 @@ public function testIdIndexAndInfo(): void $operation = new ListCollections($this->getDatabaseName(), ['filter' => ['name' => $this->getCollectionName()]]); $collections = $operation->execute($server); - $this->assertInstanceOf(CollectionInfoIterator::class, $collections); + $this->assertInstanceOf(Iterator::class, $collections); foreach ($collections as $collection) { $this->assertInstanceOf(CollectionInfo::class, $collection); $this->assertArrayHasKey('readOnly', $collection['info']); - $this->assertEquals(['v' => 2, 'key' => ['_id' => 1], 'name' => '_id_', 'ns' => $this->getNamespace()], $collection['idIndex']); + // Use assertMatchesDocument as MongoDB 4.0 and 4.2 include a ns field + // TODO: change to assertEquals when dropping support for MongoDB 4.2 + $this->assertMatchesDocument(['v' => 2, 'key' => ['_id' => 1], 'name' => '_id_'], $collection['idIndex']); } } diff --git a/tests/Operation/ListDatabasesFunctionalTest.php b/tests/Operation/ListDatabasesFunctionalTest.php index 0a6271827..f881b34dd 100644 --- a/tests/Operation/ListDatabasesFunctionalTest.php +++ b/tests/Operation/ListDatabasesFunctionalTest.php @@ -2,8 +2,8 @@ namespace MongoDB\Tests\Operation; +use Iterator; use MongoDB\Model\DatabaseInfo; -use MongoDB\Model\DatabaseInfoIterator; use MongoDB\Operation\InsertOne; use MongoDB\Operation\ListDatabases; use MongoDB\Tests\CommandObserver; @@ -30,7 +30,7 @@ function (array $event): void { }, ); - $this->assertInstanceOf(DatabaseInfoIterator::class, $databases); + $this->assertInstanceOf(Iterator::class, $databases); foreach ($databases as $database) { $this->assertInstanceOf(DatabaseInfo::class, $database); @@ -65,7 +65,7 @@ public function testFilterOption(): void $operation = new ListDatabases(['filter' => ['name' => $this->getDatabaseName()]]); $databases = $operation->execute($server); - $this->assertInstanceOf(DatabaseInfoIterator::class, $databases); + $this->assertInstanceOf(Iterator::class, $databases); $this->assertCount(1, $databases); diff --git a/tests/Operation/ListIndexesFunctionalTest.php b/tests/Operation/ListIndexesFunctionalTest.php index dcdcac71c..f5a2f6bc2 100644 --- a/tests/Operation/ListIndexesFunctionalTest.php +++ b/tests/Operation/ListIndexesFunctionalTest.php @@ -2,8 +2,8 @@ namespace MongoDB\Tests\Operation; +use Iterator; use MongoDB\Model\IndexInfo; -use MongoDB\Model\IndexInfoIterator; use MongoDB\Operation\InsertOne; use MongoDB\Operation\ListIndexes; use MongoDB\Tests\CommandObserver; @@ -21,14 +21,13 @@ public function testListIndexesForNewlyCreatedCollection(): void $operation = new ListIndexes($this->getDatabaseName(), $this->getCollectionName()); $indexes = $operation->execute($this->getPrimaryServer()); - $this->assertInstanceOf(IndexInfoIterator::class, $indexes); + $this->assertInstanceOf(Iterator::class, $indexes); $this->assertCount(1, $indexes); foreach ($indexes as $index) { $this->assertInstanceOf(IndexInfo::class, $index); $this->assertEquals(['_id' => 1], $index->getKey()); - $this->assertSame($this->getNamespace(), $index->getNamespace()); } } diff --git a/tests/Operation/MapReduceFunctionalTest.php b/tests/Operation/MapReduceFunctionalTest.php deleted file mode 100644 index 5dbe45f3a..000000000 --- a/tests/Operation/MapReduceFunctionalTest.php +++ /dev/null @@ -1,314 +0,0 @@ -createCollection($this->getDatabaseName(), $this->getCollectionName()); - - (new CommandObserver())->observe( - function (): void { - $operation = new MapReduce( - $this->getDatabaseName(), - $this->getCollectionName(), - new Javascript('function() { emit(this.x, this.y); }'), - new Javascript('function(key, values) { return Array.sum(values); }'), - ['inline' => 1], - ['readConcern' => $this->createDefaultReadConcern()], - ); - - $operation->execute($this->getPrimaryServer()); - }, - function (array $event): void { - $this->assertObjectNotHasProperty('readConcern', $event['started']->getCommand()); - }, - ); - } - - public function testDefaultWriteConcernIsOmitted(): void - { - // Collection must exist for mapReduce command - $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); - $this->dropCollection($this->getDatabaseName(), $this->getCollectionName() . '.output'); - - (new CommandObserver())->observe( - function (): void { - $operation = new MapReduce( - $this->getDatabaseName(), - $this->getCollectionName(), - new Javascript('function() { emit(this.x, this.y); }'), - new Javascript('function(key, values) { return Array.sum(values); }'), - $this->getCollectionName() . '.output', - ['writeConcern' => $this->createDefaultWriteConcern()], - ); - - $operation->execute($this->getPrimaryServer()); - }, - function (array $event): void { - $this->assertObjectNotHasProperty('writeConcern', $event['started']->getCommand()); - }, - ); - } - - public function testFinalize(): void - { - $this->createFixtures(3); - - $map = new Javascript('function() { emit(this.x, this.y); }'); - $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); - $out = ['inline' => 1]; - $finalize = new Javascript('function(key, reducedValue) { return reducedValue; }'); - - $operation = new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out, ['finalize' => $finalize]); - $result = $operation->execute($this->getPrimaryServer()); - - $this->assertNotNull($result); - } - - public function testResult(): void - { - $this->createFixtures(3); - - $map = new Javascript('function() { emit(this.x, this.y); }'); - $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); - $out = ['inline' => 1]; - - $operation = new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out); - $result = $operation->execute($this->getPrimaryServer()); - - $this->assertInstanceOf(MapReduceResult::class, $result); - - if (version_compare($this->getServerVersion(), '4.3.0', '<')) { - $this->assertGreaterThanOrEqual(0, $result->getExecutionTimeMS()); - $this->assertNotEmpty($result->getCounts()); - } - } - - public function testResultIncludesTimingWithVerboseOption(): void - { - $this->skipIfServerVersion('>=', '4.3.0', 'mapReduce statistics are no longer exposed'); - - $this->createFixtures(3); - - $map = new Javascript('function() { emit(this.x, this.y); }'); - $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); - $out = ['inline' => 1]; - - $operation = new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out, ['verbose' => true]); - $result = $operation->execute($this->getPrimaryServer()); - - $this->assertInstanceOf(MapReduceResult::class, $result); - $this->assertGreaterThanOrEqual(0, $result->getExecutionTimeMS()); - $this->assertNotEmpty($result->getCounts()); - $this->assertNotEmpty($result->getTiming()); - } - - public function testResultDoesNotIncludeTimingWithoutVerboseOption(): void - { - $this->skipIfServerVersion('>=', '4.3.0', 'mapReduce statistics are no longer exposed'); - - $this->createFixtures(3); - - $map = new Javascript('function() { emit(this.x, this.y); }'); - $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); - $out = ['inline' => 1]; - - $operation = new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out, ['verbose' => false]); - $result = $operation->execute($this->getPrimaryServer()); - - $this->assertInstanceOf(MapReduceResult::class, $result); - $this->assertGreaterThanOrEqual(0, $result->getExecutionTimeMS()); - $this->assertNotEmpty($result->getCounts()); - $this->assertEmpty($result->getTiming()); - } - - public function testSessionOption(): void - { - $this->createFixtures(3); - - (new CommandObserver())->observe( - function (): void { - $operation = new MapReduce( - $this->getDatabaseName(), - $this->getCollectionName(), - new Javascript('function() { emit(this.x, this.y); }'), - new Javascript('function(key, values) { return Array.sum(values); }'), - ['inline' => 1], - ['session' => $this->createSession()], - ); - - $operation->execute($this->getPrimaryServer()); - }, - function (array $event): void { - $this->assertObjectHasProperty('lsid', $event['started']->getCommand()); - }, - ); - } - - public function testBypassDocumentValidationSetWhenTrue(): void - { - $this->createFixtures(1); - - (new CommandObserver())->observe( - function (): void { - $operation = new MapReduce( - $this->getDatabaseName(), - $this->getCollectionName(), - new Javascript('function() { emit(this.x, this.y); }'), - new Javascript('function(key, values) { return Array.sum(values); }'), - ['inline' => 1], - ['bypassDocumentValidation' => true], - ); - - $operation->execute($this->getPrimaryServer()); - }, - function (array $event): void { - $this->assertObjectHasProperty('bypassDocumentValidation', $event['started']->getCommand()); - $this->assertEquals(true, $event['started']->getCommand()->bypassDocumentValidation); - }, - ); - } - - public function testBypassDocumentValidationUnsetWhenFalse(): void - { - $this->createFixtures(1); - - (new CommandObserver())->observe( - function (): void { - $operation = new MapReduce( - $this->getDatabaseName(), - $this->getCollectionName(), - new Javascript('function() { emit(this.x, this.y); }'), - new Javascript('function(key, values) { return Array.sum(values); }'), - ['inline' => 1], - ['bypassDocumentValidation' => false], - ); - - $operation->execute($this->getPrimaryServer()); - }, - function (array $event): void { - $this->assertObjectNotHasProperty('bypassDocumentValidation', $event['started']->getCommand()); - }, - ); - } - - #[DataProvider('provideTypeMapOptionsAndExpectedDocuments')] - public function testTypeMapOptionWithInlineResults(?array $typeMap, array $expectedDocuments): void - { - $this->createFixtures(3); - - $map = new Javascript('function() { emit(this.x, this.y); }'); - $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); - $out = ['inline' => 1]; - - $operation = new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out, ['typeMap' => $typeMap]); - $results = iterator_to_array($operation->execute($this->getPrimaryServer())); - - $this->assertEquals($this->sortResults($expectedDocuments), $this->sortResults($results)); - } - - public static function provideTypeMapOptionsAndExpectedDocuments() - { - return [ - [ - null, - [ - (object) ['_id' => 1, 'value' => 3], - (object) ['_id' => 2, 'value' => 6], - (object) ['_id' => 3, 'value' => 9], - ], - ], - [ - ['root' => 'array'], - [ - ['_id' => 1, 'value' => 3], - ['_id' => 2, 'value' => 6], - ['_id' => 3, 'value' => 9], - ], - ], - [ - ['root' => 'object'], - [ - (object) ['_id' => 1, 'value' => 3], - (object) ['_id' => 2, 'value' => 6], - (object) ['_id' => 3, 'value' => 9], - ], - ], - ]; - } - - #[DataProvider('provideTypeMapOptionsAndExpectedDocuments')] - public function testTypeMapOptionWithOutputCollection(?array $typeMap, array $expectedDocuments): void - { - $this->createFixtures(3); - - $map = new Javascript('function() { emit(this.x, this.y); }'); - $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); - $out = $this->getCollectionName() . '.output'; - $this->dropCollection($this->getDatabaseName(), $out); - - $operation = new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out, ['typeMap' => $typeMap]); - $results = iterator_to_array($operation->execute($this->getPrimaryServer())); - - $this->assertEquals($this->sortResults($expectedDocuments), $this->sortResults($results)); - - $operation = new Find($this->getDatabaseName(), $out, [], ['typeMap' => $typeMap]); - $cursor = $operation->execute($this->getPrimaryServer()); - - $this->assertEquals($this->sortResults($expectedDocuments), $this->sortResults(iterator_to_array($cursor))); - } - - /** - * Create data fixtures. - */ - private function createFixtures(int $n): void - { - $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); - $bulkWrite = new BulkWrite(['ordered' => true]); - - for ($i = 1; $i <= $n; $i++) { - $bulkWrite->insert(['x' => $i, 'y' => $i]); - $bulkWrite->insert(['x' => $i, 'y' => $i * 2]); - } - - $result = $this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite); - - $this->assertEquals($n * 2, $result->getInsertedCount()); - } - - private function sortResults(array $results): array - { - $sortFunction = static function ($resultA, $resultB): int { - $idA = is_object($resultA) ? $resultA->_id : $resultA['_id']; - $idB = is_object($resultB) ? $resultB->_id : $resultB['_id']; - - return $idA <=> $idB; - }; - - $sortedResults = $results; - usort($sortedResults, $sortFunction); - - return $sortedResults; - } -} diff --git a/tests/Operation/MapReduceTest.php b/tests/Operation/MapReduceTest.php deleted file mode 100644 index 86af81197..000000000 --- a/tests/Operation/MapReduceTest.php +++ /dev/null @@ -1,95 +0,0 @@ -expectException(TypeError::class); - new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out); - } - - public static function provideInvalidOutValues() - { - return self::wrapValuesForDataProvider([123, 3.14, true]); - } - - #[DataProvider('provideDeprecatedOutValues')] - public function testConstructorOutArgumentDeprecations($out): void - { - $map = new Javascript('function() { emit(this.x, this.y); }'); - $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); - - $this->assertDeprecated(function () use ($map, $reduce, $out): void { - new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out); - }); - } - - public static function provideDeprecatedOutValues(): array - { - return [ - 'nonAtomic:array' => [['nonAtomic' => false]], - 'nonAtomic:object' => [(object) ['nonAtomic' => false]], - 'nonAtomic:Serializable' => [new BSONDocument(['nonAtomic' => false])], - 'nonAtomic:Document' => [Document::fromPHP(['nonAtomic' => false])], - 'sharded:array' => [['sharded' => false]], - 'sharded:object' => [(object) ['sharded' => false]], - 'sharded:Serializable' => [new BSONDocument(['sharded' => false])], - 'sharded:Document' => [Document::fromPHP(['sharded' => false])], - ]; - } - - #[DataProvider('provideInvalidConstructorOptions')] - public function testConstructorOptionTypeChecks(array $options): void - { - $map = new Javascript('function() { emit(this.x, this.y); }'); - $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); - $out = ['inline' => 1]; - - $this->expectException(InvalidArgumentException::class); - new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out, $options); - } - - public static function provideInvalidConstructorOptions() - { - return self::createOptionDataProvider([ - 'bypassDocumentValidation' => self::getInvalidBooleanValues(), - 'collation' => self::getInvalidDocumentValues(), - 'finalize' => self::getInvalidJavascriptValues(), - 'jsMode' => self::getInvalidBooleanValues(), - 'limit' => self::getInvalidIntegerValues(), - 'maxTimeMS' => self::getInvalidIntegerValues(), - 'query' => self::getInvalidDocumentValues(), - 'readConcern' => self::getInvalidReadConcernValues(), - 'readPreference' => self::getInvalidReadPreferenceValues(), - 'scope' => self::getInvalidDocumentValues(), - 'session' => self::getInvalidSessionValues(), - 'sort' => self::getInvalidDocumentValues(), - 'typeMap' => self::getInvalidArrayValues(), - 'verbose' => self::getInvalidBooleanValues(), - 'writeConcern' => self::getInvalidWriteConcernValues(), - ]); - } - - private static function getInvalidJavascriptValues() - { - return [123, 3.14, 'foo', true, [], new stdClass(), new ObjectId()]; - } -} diff --git a/tests/SpecTests/Operation.php b/tests/SpecTests/Operation.php index 3672314cf..d605a52aa 100644 --- a/tests/SpecTests/Operation.php +++ b/tests/SpecTests/Operation.php @@ -12,9 +12,9 @@ use MongoDB\Driver\Server; use MongoDB\Driver\Session; use MongoDB\GridFS\Bucket; -use MongoDB\MapReduceResult; use MongoDB\Operation\FindOneAndReplace; use MongoDB\Operation\FindOneAndUpdate; +use PHPUnit\Framework\Assert; use stdClass; use function array_diff_key; @@ -183,10 +183,6 @@ public function assert(FunctionalTestCase $test, Context $context, bool $bubbleE * is not used (e.g. Command Monitoring spec). */ if ($result instanceof Cursor) { $result = $result->toArray(); - } elseif ($result instanceof MapReduceResult) { - /* For mapReduce operations, we ignore the mapReduce metadata - * and only return the result iterator for evaluation. */ - $result = iterator_to_array($result->getIterator()); } } catch (Exception $e) { $exception = $e; @@ -411,12 +407,8 @@ private function executeForCollection(Collection $collection, Context $context): return $collection->listIndexes($args); case 'mapReduce': - return $collection->mapReduce( - $args['map'], - $args['reduce'], - $args['out'], - array_diff_key($args, ['map' => 1, 'reduce' => 1, 'out' => 1]), - ); + Assert::markTestSkipped('mapReduce is not supported'); + break; case 'watch': return $collection->watch( diff --git a/tests/UnifiedSpecTests/Operation.php b/tests/UnifiedSpecTests/Operation.php index 9957af855..f64803b7b 100644 --- a/tests/UnifiedSpecTests/Operation.php +++ b/tests/UnifiedSpecTests/Operation.php @@ -3,7 +3,6 @@ namespace MongoDB\Tests\UnifiedSpecTests; use Error; -use MongoDB\BSON\Javascript; use MongoDB\ChangeStream; use MongoDB\Client; use MongoDB\Collection; @@ -507,19 +506,8 @@ private function executeForCollection(Collection $collection) ); case 'mapReduce': - assertArrayHasKey('map', $args); - assertArrayHasKey('reduce', $args); - assertArrayHasKey('out', $args); - assertInstanceOf(Javascript::class, $args['map']); - assertInstanceOf(Javascript::class, $args['reduce']); - assertThat($args['out'], logicalOr(new IsType('string'), new IsType('array'), new IsType('object'))); - - return iterator_to_array($collection->mapReduce( - $args['map'], - $args['reduce'], - $args['out'], - array_diff_key($args, ['map' => 1, 'reduce' => 1, 'out' => 1]), - )); + Assert::markTestSkipped('mapReduce operation is not supported'); + break; case 'rename': assertArrayHasKey('to', $args); diff --git a/tests/UnifiedSpecTests/Util.php b/tests/UnifiedSpecTests/Util.php index d817d61d3..5d758ab6a 100644 --- a/tests/UnifiedSpecTests/Util.php +++ b/tests/UnifiedSpecTests/Util.php @@ -87,7 +87,7 @@ final class Util 'aggregate' => ['pipeline', 'session', 'allowDiskUse', 'batchSize', 'bypassDocumentValidation', 'collation', 'comment', 'explain', 'hint', 'let', 'maxAwaitTimeMS', 'maxTimeMS'], 'bulkWrite' => ['let', 'requests', 'session', 'ordered', 'bypassDocumentValidation', 'comment'], 'createChangeStream' => ['pipeline', 'session', 'fullDocument', 'fullDocumentBeforeChange', 'resumeAfter', 'startAfter', 'startAtOperationTime', 'batchSize', 'collation', 'maxAwaitTimeMS', 'comment', 'showExpandedEvents'], - 'createFindCursor' => ['filter', 'session', 'allowDiskUse', 'allowPartialResults', 'batchSize', 'collation', 'comment', 'cursorType', 'hint', 'limit', 'max', 'maxAwaitTimeMS', 'maxScan', 'maxTimeMS', 'min', 'modifiers', 'noCursorTimeout', 'oplogReplay', 'projection', 'returnKey', 'showRecordId', 'skip', 'snapshot', 'sort'], + 'createFindCursor' => ['filter', 'session', 'allowDiskUse', 'allowPartialResults', 'batchSize', 'collation', 'comment', 'cursorType', 'hint', 'limit', 'max', 'maxAwaitTimeMS', 'maxTimeMS', 'min', 'noCursorTimeout', 'projection', 'returnKey', 'showRecordId', 'skip', 'sort'], 'createIndex' => ['keys', 'comment', 'commitQuorum', 'maxTimeMS', 'name', 'session', 'unique'], 'createSearchIndex' => ['model'], 'createSearchIndexes' => ['models'], @@ -101,8 +101,8 @@ final class Util 'distinct' => ['fieldName', 'filter', 'session', 'collation', 'maxTimeMS', 'comment'], 'drop' => ['session', 'comment'], 'dropSearchIndex' => ['name'], - 'find' => ['let', 'filter', 'session', 'allowDiskUse', 'allowPartialResults', 'batchSize', 'collation', 'comment', 'cursorType', 'hint', 'limit', 'max', 'maxAwaitTimeMS', 'maxScan', 'maxTimeMS', 'min', 'modifiers', 'noCursorTimeout', 'oplogReplay', 'projection', 'returnKey', 'showRecordId', 'skip', 'snapshot', 'sort'], - 'findOne' => ['let', 'filter', 'session', 'allowDiskUse', 'allowPartialResults', 'batchSize', 'collation', 'comment', 'cursorType', 'hint', 'max', 'maxAwaitTimeMS', 'maxScan', 'maxTimeMS', 'min', 'modifiers', 'noCursorTimeout', 'oplogReplay', 'projection', 'returnKey', 'showRecordId', 'skip', 'snapshot', 'sort'], + 'find' => ['let', 'filter', 'session', 'allowDiskUse', 'allowPartialResults', 'batchSize', 'collation', 'comment', 'cursorType', 'hint', 'limit', 'max', 'maxAwaitTimeMS', 'maxTimeMS', 'min', 'noCursorTimeout', 'projection', 'returnKey', 'showRecordId', 'skip', 'sort'], + 'findOne' => ['let', 'filter', 'session', 'allowDiskUse', 'allowPartialResults', 'batchSize', 'collation', 'comment', 'cursorType', 'hint', 'max', 'maxAwaitTimeMS', 'maxTimeMS', 'min', 'noCursorTimeout', 'projection', 'returnKey', 'showRecordId', 'skip', 'sort'], 'findOneAndReplace' => ['let', 'returnDocument', 'filter', 'replacement', 'session', 'projection', 'returnDocument', 'upsert', 'arrayFilters', 'bypassDocumentValidation', 'collation', 'hint', 'maxTimeMS', 'new', 'remove', 'sort', 'comment'], 'rename' => ['to', 'comment', 'dropTarget'], 'replaceOne' => ['let', 'filter', 'replacement', 'session', 'upsert', 'arrayFilters', 'bypassDocumentValidation', 'collation', 'hint', 'comment'],