From 786471a73e1db897765fc4bd0320cc951ebf25e5 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Tue, 17 Mar 2015 21:02:30 -0400 Subject: [PATCH 1/8] PHPLIB-69: Prototypes for index drop methods --- src/Collection.php | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/Collection.php b/src/Collection.php index 511439e3f..92aa56173 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -64,6 +64,32 @@ public function __construct(Manager $manager, $ns, WriteConcern $wc = null, Read list($this->dbname, $this->collname) = explode(".", $ns, 2); } + /** + * Drop a single index in the collection. + * + * @see http://docs.mongodb.org/manual/reference/command/dropIndexes/ + * @see http://docs.mongodb.org/manual/reference/method/db.collection.dropIndex/ + * @param string $indexName + * @return Result + * @throws InvalidArgumentException if "*" is specified + */ + public function dropIndex($indexName) + { + // TODO + } + + /** + * Drop all indexes in the collection. + * + * @see http://docs.mongodb.org/manual/reference/command/dropIndexes/ + * @see http://docs.mongodb.org/manual/reference/method/db.collection.dropIndexes/ + * @return Result + */ + public function dropIndexes() + { + // TODO + } + /** * Performs a find (query) on the collection * From 0c0d33481074b1a8f4b268c2bc4166c0940afb1b Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Tue, 17 Mar 2015 21:10:50 -0400 Subject: [PATCH 2/8] PHPLIB-63: Prototypes for index creation methods --- src/Collection.php | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/Collection.php b/src/Collection.php index 92aa56173..cca36e0e0 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -64,6 +64,37 @@ public function __construct(Manager $manager, $ns, WriteConcern $wc = null, Read list($this->dbname, $this->collname) = explode(".", $ns, 2); } + /** + * Create a single index in the collection. + * + * @see http://docs.mongodb.org/manual/reference/command/createIndexes/ + * @see http://docs.mongodb.org/manual/reference/method/db.collection.createIndex/ + * @param array|object $keys + * @param array $options + * @return string The name of the created index + */ + public function createIndex($keys, array $options = array()) + { + // TODO + } + + /** + * Create multiple indexes in the collection. + * + * TODO: decide if $models should be an array of associative arrays, using + * createIndex()'s parameter names as keys, or tuples, using parameters in + * order (e.g. [keys, options]). + * + * @see http://docs.mongodb.org/manual/reference/command/createIndexes/ + * @see http://docs.mongodb.org/manual/reference/method/db.collection.createIndex/ + * @param array $models + * @return string[] The names of the created indexes + */ + public function createIndexes(array $models) + { + // TODO + } + /** * Drop a single index in the collection. * From 4b0d5ad079e0d1b537d7df0f711eafb6bcd17549 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Tue, 17 Mar 2015 21:11:43 -0400 Subject: [PATCH 3/8] PHPLIB-64: Prototype for collection create method --- src/Database.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Database.php b/src/Database.php index d56de3bad..8aaaf9fb8 100644 --- a/src/Database.php +++ b/src/Database.php @@ -32,6 +32,20 @@ public function __construct(Manager $manager, $databaseName, WriteConcern $wc = $this->rp = $rp; } + /** + * Create a new collection explicitly. + * + * @see http://docs.mongodb.org/manual/reference/command/create/ + * @see http://docs.mongodb.org/manual/reference/method/db.createCollection/ + * @param string $collectionName + * @param array $options + * @return Result + */ + public function createCollection($collectionName, array $options = array()) + { + // TODO + } + /** * Select a specific collection in this database * From b51e1b3c3e8c7e1779f984ffa7ecaca534974e0d Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Tue, 17 Mar 2015 21:16:37 -0400 Subject: [PATCH 4/8] PHPLIB-65: Prototypes for database drop methods --- src/Client.php | 16 ++++++++++++++-- src/Database.php | 13 ++++++++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/Client.php b/src/Client.php index f0140895d..4c926a2ca 100644 --- a/src/Client.php +++ b/src/Client.php @@ -2,9 +2,10 @@ namespace MongoDB; -use MongoDB\Driver\Manager; -use MongoDB\Database; use MongoDB\Collection; +use MongoDB\Database; +use MongoDB\Driver\Manager; +use MongoDB\Driver\Result; class Client { @@ -29,6 +30,17 @@ public function __construct($uri, $options, $driverOptions) $this->manager = new Manager($uri, $options, $driverOptions); } + /** + * Drop a database. + * + * @param string $databaseName + * @return Result + */ + public function dropDatabase($databaseName) + { + // TODO + } + /** * Select a database * diff --git a/src/Database.php b/src/Database.php index 8aaaf9fb8..2a8424830 100644 --- a/src/Database.php +++ b/src/Database.php @@ -2,8 +2,9 @@ namespace MongoDB; -use MongoDB\Driver\Manager; use MongoDB\Collection; +use MongoDB\Driver\Manager; +use MongoDB\Driver\Result; class Database { @@ -46,6 +47,16 @@ public function createCollection($collectionName, array $options = array()) // TODO } + /** + * Drop this database. + * + * @return Result + */ + public function drop() + { + // TODO + } + /** * Select a specific collection in this database * From b6a120e05e2da72a2b996f44c998c5b94a9e3150 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Tue, 17 Mar 2015 21:16:59 -0400 Subject: [PATCH 5/8] PHPLIB-71: Prototypes for collection drop methods --- src/Collection.php | 10 ++++++++++ src/Database.php | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/Collection.php b/src/Collection.php index cca36e0e0..3f907257b 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -95,6 +95,16 @@ public function createIndexes(array $models) // TODO } + /** + * Drop this collection. + * + * @return Result + */ + public function drop() + { + // TODO + } + /** * Drop a single index in the collection. * diff --git a/src/Database.php b/src/Database.php index 2a8424830..bb50a104c 100644 --- a/src/Database.php +++ b/src/Database.php @@ -57,6 +57,17 @@ public function drop() // TODO } + /** + * Drop a collection within this database. + * + * @param string $collectionName + * @return Result + */ + public function dropCollection($collectionName) + { + // TODO + } + /** * Select a specific collection in this database * From 495e46d4ddb3980126635f2397e4301d94f6c262 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Tue, 17 Mar 2015 22:56:31 -0400 Subject: [PATCH 6/8] Declare Collection methods alphabetically by visibility We can move this to a separate, pedantic test class later. --- src/Collection.php | 1415 +++++++++++++++++++------------------- tests/CollectionTest.php | 25 +- 2 files changed, 721 insertions(+), 719 deletions(-) diff --git a/src/Collection.php b/src/Collection.php index 3f907257b..3363ca4f0 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -64,6 +64,182 @@ public function __construct(Manager $manager, $ns, WriteConcern $wc = null, Read list($this->dbname, $this->collname) = explode(".", $ns, 2); } + /** + * Runs an aggregation framework pipeline + * NOTE: The return value of this method depends on your MongoDB server version + * and possibly options. + * MongoDB 2.6 (and later) will return a Cursor by default + * MongoDB pre 2.6 will return an ArrayIterator + * + * @see http://docs.mongodb.org/manual/reference/command/aggregate/ + * @see Collection::getAggregateOptions() for supported $options + * + * @param array $pipeline The pipeline to execute + * @param array $options Additional options + * @return Iterator + */ + public function aggregate(array $pipeline, array $options = array()) + { + $options = array_merge($this->getAggregateOptions(), $options); + $options = $this->_massageAggregateOptions($options); + $cmd = array( + "aggregate" => $this->collname, + "pipeline" => $pipeline, + ) + $options; + + $result = $this->_runCommand($this->dbname, $cmd); + $doc = $result->toArray(); + if (isset($cmd["cursor"]) && $cmd["cursor"]) { + return $result; + } else { + if ($doc["ok"]) { + return new \ArrayIterator($doc["result"]); + } + } + + throw $this->_generateCommandException($doc); + } + + /** + * Adds a full set of write operations into a bulk and executes it + * + * The syntax of the $bulk array is: + * $bulk = [ + * [ + * 'METHOD' => [ + * $document, + * $extraArgument1, + * $extraArgument2, + * ], + * ], + * [ + * 'METHOD' => [ + * $document, + * $extraArgument1, + * $extraArgument2, + * ], + * ], + * ] + * + * + * Where METHOD is one of + * - 'insertOne' + * Supports no $extraArgument + * - 'updateMany' + * Requires $extraArgument1, same as $update for Collection::updateMany() + * Optional $extraArgument2, same as $options for Collection::updateMany() + * - 'updateOne' + * Requires $extraArgument1, same as $update for Collection::updateOne() + * Optional $extraArgument2, same as $options for Collection::updateOne() + * - 'replaceOne' + * Requires $extraArgument1, same as $update for Collection::replaceOne() + * Optional $extraArgument2, same as $options for Collection::replaceOne() + * - 'deleteOne' + * Supports no $extraArgument + * - 'deleteMany' + * Supports no $extraArgument + * + * @example Collection-bulkWrite.php Using Collection::bulkWrite() + * + * @see Collection::getBulkOptions() for supported $options + * + * @param array $bulk Array of operations + * @param array $options Additional options + * @return WriteResult + */ + public function bulkWrite(array $bulk, array $options = array()) + { + $options = array_merge($this->getBulkOptions(), $options); + + $bulk = new BulkWrite($options["ordered"]); + + foreach ($bulk as $n => $op) { + foreach ($op as $opname => $args) { + if (!isset($args[0])) { + throw new \InvalidArgumentException(sprintf("Missing argument#1 for '%s' (operation#%d)", $opname, $n)); + } + + switch ($opname) { + case "insertOne": + $bulk->insert($args[0]); + break; + + case "updateMany": + if (!isset($args[1])) { + throw new \InvalidArgumentException(sprintf("Missing argument#2 for '%s' (operation#%d)", $opname, $n)); + } + $options = array_merge($this->getWriteOptions(), isset($args[2]) ? $args[2] : array(), array("limit" => 0)); + + $bulk->update($args[0], $args[1], $options); + break; + + case "updateOne": + if (!isset($args[1])) { + throw new \InvalidArgumentException(sprintf("Missing argument#2 for '%s' (operation#%d)", $opname, $n)); + } + $options = array_merge($this->getWriteOptions(), isset($args[2]) ? $args[2] : array(), array("limit" => 1)); + if (key($args[1])[0] != '$') { + throw new \InvalidArgumentException("First key in \$update must be a \$operator"); + } + + $bulk->update($args[0], $args[1], $options); + break; + + case "replaceOne": + if (!isset($args[1])) { + throw new \InvalidArgumentException(sprintf("Missing argument#2 for '%s' (operation#%d)", $opname, $n)); + } + $options = array_merge($this->getWriteOptions(), isset($args[2]) ? $args[2] : array(), array("limit" => 1)); + if (key($args[1])[0] == '$') { + throw new \InvalidArgumentException("First key in \$update must NOT be a \$operator"); + } + + $bulk->update($args[0], $args[1], $options); + break; + + case "deleteOne": + $options = array_merge($this->getWriteOptions(), isset($args[1]) ? $args[1] : array(), array("limit" => 1)); + $bulk->delete($args[0], $options); + break; + + case "deleteMany": + $options = array_merge($this->getWriteOptions(), isset($args[1]) ? $args[1] : array(), array("limit" => 0)); + $bulk->delete($args[0], $options); + break; + + default: + throw new \InvalidArgumentException(sprintf("Unknown operation type called '%s' (operation#%d)", $opname, $n)); + } + } + } + return $this->manager->executeBulkWrite($this->ns, $bulk, $this->wc); + } + + /** + * Counts all documents matching $filter + * If no $filter provided, returns the numbers of documents in the collection + * + * @see http://docs.mongodb.org/manual/reference/command/count/ + * @see Collection::getCountOptions() for supported $options + * + * @param array $filter The find query to execute + * @param array $options Additional options + * @return integer + */ + public function count(array $filter = array(), array $options = array()) + { + $cmd = array( + "count" => $this->collname, + "query" => $filter, + ) + $options; + + $doc = $this->_runCommand($this->dbname, $cmd)->toArray(); + if ($doc["ok"]) { + return $doc["n"]; + } + throw $this->_generateCommandException($doc); + } + /** * Create a single index in the collection. * @@ -95,6 +271,65 @@ public function createIndexes(array $models) // TODO } + /** + * Deletes a document matching the $filter criteria. + * NOTE: Will delete ALL documents matching $filter + * + * @see http://docs.mongodb.org/manual/reference/command/delete/ + * + * @param array $filter The $filter criteria to delete + * @return DeleteResult + */ + public function deleteMany(array $filter) + { + $wr = $this->_delete($filter, 0); + + return new DeleteResult($wr); + } + + /** + * Deletes a document matching the $filter criteria. + * NOTE: Will delete at most ONE document matching $filter + * + * @see http://docs.mongodb.org/manual/reference/command/delete/ + * + * @param array $filter The $filter criteria to delete + * @return DeleteResult + */ + public function deleteOne(array $filter) + { + $wr = $this->_delete($filter); + + return new DeleteResult($wr); + } + + /** + * Finds the distinct values for a specified field across the collection + * + * @see http://docs.mongodb.org/manual/reference/command/distinct/ + * @see Collection::getDistinctOptions() for supported $options + * + * @param string $fieldName The fieldname to use + * @param array $filter The find query to execute + * @param array $options Additional options + * @return integer + */ + public function distinct($fieldName, array $filter = array(), array $options = array()) + { + $options = array_merge($this->getDistinctOptions(), $options); + $cmd = array( + "distinct" => $this->collname, + "key" => $fieldName, + "query" => $filter, + ) + $options; + + $doc = $this->_runCommand($this->dbname, $cmd)->toArray(); + if ($doc["ok"]) { + return $doc["values"]; + } + throw $this->_generateCommandException($doc); + } + /** * Drop this collection. * @@ -179,916 +414,633 @@ public function findOne(array $filter = array(), array $options = array()) } /** - * Retrieves all find options with their default values. + * Finds a single document and deletes it, returning the original. * - * @return array of Collection::find() options + * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ + * @see Collection::getFindOneAndDelete() for supported $options + * + * @param array $filter The $filter criteria to search for + * @param array $options Additional options + * @return array The original document */ - public function getFindOptions() + public function findOneAndDelete(array $filter, array $options = array()) { - return array( - /** - * Get partial results from a mongos if some shards are down (instead of throwing an error). - * - * @see http://docs.mongodb.org/meta-driver/latest/legacy/mongodb-wire-protocol/#op-query - */ - "allowPartialResults" => false, - - /** - * The number of documents to return per batch. - * - * @see http://docs.mongodb.org/manual/reference/method/cursor.batchSize/ - */ - "batchSize" => 101, - - /** - * Attaches a comment to the query. If $comment also exists - * in the modifiers document, the comment field overwrites $comment. - * - * @see http://docs.mongodb.org/manual/reference/operator/meta/comment/ - */ - "comment" => "", - - /** - * Indicates the type of cursor to use. This value includes both - * the tailable and awaitData options. - * The default is Collection::CURSOR_TYPE_NON_TAILABLE. - * - * @see http://docs.mongodb.org/manual/reference/operator/meta/comment/ - */ - "cursorType" => self::CURSOR_TYPE_NON_TAILABLE, - - /** - * The maximum number of documents to return. - * - * @see http://docs.mongodb.org/manual/reference/method/cursor.limit/ - */ - "limit" => 0, - - /** - * The maximum amount of time to allow the query to run. If $maxTimeMS also exists - * in the modifiers document, the maxTimeMS field overwrites $maxTimeMS. - * - * @see http://docs.mongodb.org/manual/reference/operator/meta/maxTimeMS/ - */ - "maxTimeMS" => 0, - - /** - * Meta-operators modifying the output or behavior of a query. - * - * @see http://docs.mongodb.org/manual/reference/operator/query-modifier/ - */ - "modifiers" => array(), - - /** - * The server normally times out idle cursors after an inactivity period (10 minutes) - * to prevent excess memory use. Set this option to prevent that. - * - * @see http://docs.mongodb.org/meta-driver/latest/legacy/mongodb-wire-protocol/#op-query - */ - "noCursorTimeout" => false, - - /** - * Internal replication use only - driver should not set - * - * @see http://docs.mongodb.org/meta-driver/latest/legacy/mongodb-wire-protocol/#op-query - * @internal - */ - "oplogReplay" => false, - - /** - * Limits the fields to return for all matching documents. - * - * @see http://docs.mongodb.org/manual/tutorial/project-fields-from-query-results/ - */ - "projection" => array(), + $options = array_merge($this->getFindOneAndDeleteOptions(), $options); + $options = $this->_massageFindAndModifyOptions($options); + $cmd = array( + "findandmodify" => $this->collname, + "query" => $filter, + ) + $options; - /** - * The number of documents to skip before returning. - * - * @see http://docs.mongodb.org/manual/reference/method/cursor.skip/ - */ - "skip" => 0, + $doc = $this->_runCommand($this->dbname, $cmd)->toArray(); + if ($doc["ok"]) { + return $doc["value"]; + } - /** - * The order in which to return matching documents. If $orderby also exists - * in the modifiers document, the sort field overwrites $orderby. - * - * @see http://docs.mongodb.org/manual/reference/method/cursor.sort/ - */ - "sort" => array(), - ); + throw $this->_generateCommandException($doc); } /** - * Constructs the Query Wire Protocol field 'flags' based on $options - * provided to other helpers + * Finds a single document and replaces it, returning either the original or the replaced document + * By default, returns the original document. + * To return the new document set: + * $options = array("returnDocument" => Collection::FIND_ONE_AND_RETURN_AFTER); * - * @param array $options - * @return integer OP_QUERY Wire Protocol flags - * @internal - */ - final protected function _opQueryFlags($options) - { - $flags = 0; - - $flags |= $options["allowPartialResults"] ? self::QUERY_FLAG_PARTIAL : 0; - $flags |= $options["cursorType"] ? $options["cursorType"] : 0; - $flags |= $options["oplogReplay"] ? self::QUERY_FLAG_OPLOG_REPLY: 0; - $flags |= $options["noCursorTimeout"] ? self::QUERY_FLAG_NO_CURSOR_TIMEOUT : 0; - - return $flags; - } - - /** - * Helper to build a Query object + * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ + * @see Collection::getFindOneAndReplace() for supported $options * - * @param array $filter the query document - * @param array $options query/protocol options - * @return Query - * @internal + * @param array $filter The $filter criteria to search for + * @param array $replacement The document to replace with + * @param array $options Additional options + * @return array */ - final protected function _buildQuery($filter, $options) + public function findOneAndReplace(array $filter, array $replacement, array $options = array()) { - if ($options["comment"]) { - $options["modifiers"]['$comment'] = $options["comment"]; - } - if ($options["maxTimeMS"]) { - $options["modifiers"]['$maxTimeMS'] = $options["maxTimeMS"]; - } - if ($options["sort"]) { - $options['$orderby'] = $options["sort"]; + if (key($replacement)[0] == '$') { + throw new \InvalidArgumentException("First key in \$replacement must NOT be a \$operator"); } - $flags = $this->_opQueryFlags($options); - $options["cursorFlags"] = $flags; - - - $query = new Query($filter, $options); + $options = array_merge($this->getFindOneAndReplaceOptions(), $options); + $options = $this->_massageFindAndModifyOptions($options, $replacement); - return $query; - } + $cmd = array( + "findandmodify" => $this->collname, + "query" => $filter, + ) + $options; - /** - * Retrieves all Write options with their default values. - * - * @return array of available Write options - */ - public function getWriteOptions() - { - return array( - "ordered" => false, - "upsert" => false, - "limit" => 1, - ); - } + $doc = $this->_runCommand($this->dbname, $cmd)->toArray(); + if ($doc["ok"]) { + return $doc["value"]; + } - /** - * Retrieves all Bulk Write options with their default values. - * - * @return array of available Bulk Write options - */ - public function getBulkOptions() - { - return array( - "ordered" => false, - ); + throw $this->_generateCommandException($doc); } /** - * Adds a full set of write operations into a bulk and executes it - * - * The syntax of the $bulk array is: - * $bulk = [ - * [ - * 'METHOD' => [ - * $document, - * $extraArgument1, - * $extraArgument2, - * ], - * ], - * [ - * 'METHOD' => [ - * $document, - * $extraArgument1, - * $extraArgument2, - * ], - * ], - * ] - * - * - * Where METHOD is one of - * - 'insertOne' - * Supports no $extraArgument - * - 'updateMany' - * Requires $extraArgument1, same as $update for Collection::updateMany() - * Optional $extraArgument2, same as $options for Collection::updateMany() - * - 'updateOne' - * Requires $extraArgument1, same as $update for Collection::updateOne() - * Optional $extraArgument2, same as $options for Collection::updateOne() - * - 'replaceOne' - * Requires $extraArgument1, same as $update for Collection::replaceOne() - * Optional $extraArgument2, same as $options for Collection::replaceOne() - * - 'deleteOne' - * Supports no $extraArgument - * - 'deleteMany' - * Supports no $extraArgument + * Finds a single document and updates it, returning either the original or the updated document + * By default, returns the original document. + * To return the new document set: + * $options = array("returnDocument" => Collection::FIND_ONE_AND_RETURN_AFTER); * - * @example Collection-bulkWrite.php Using Collection::bulkWrite() * - * @see Collection::getBulkOptions() for supported $options + * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ + * @see Collection::getFindOneAndUpdate() for supported $options * - * @param array $bulk Array of operations - * @param array $options Additional options - * @return WriteResult + * @param array $filter The $filter criteria to search for + * @param array $update An array of update operators to apply to the document + * @param array $options Additional options + * @return array */ - public function bulkWrite(array $bulk, array $options = array()) + public function findOneAndUpdate(array $filter, array $update, array $options = array()) { - $options = array_merge($this->getBulkOptions(), $options); - - $bulk = new BulkWrite($options["ordered"]); - - foreach ($bulk as $n => $op) { - foreach ($op as $opname => $args) { - if (!isset($args[0])) { - throw new \InvalidArgumentException(sprintf("Missing argument#1 for '%s' (operation#%d)", $opname, $n)); - } - - switch ($opname) { - case "insertOne": - $bulk->insert($args[0]); - break; - - case "updateMany": - if (!isset($args[1])) { - throw new \InvalidArgumentException(sprintf("Missing argument#2 for '%s' (operation#%d)", $opname, $n)); - } - $options = array_merge($this->getWriteOptions(), isset($args[2]) ? $args[2] : array(), array("limit" => 0)); - - $bulk->update($args[0], $args[1], $options); - break; - - case "updateOne": - if (!isset($args[1])) { - throw new \InvalidArgumentException(sprintf("Missing argument#2 for '%s' (operation#%d)", $opname, $n)); - } - $options = array_merge($this->getWriteOptions(), isset($args[2]) ? $args[2] : array(), array("limit" => 1)); - if (key($args[1])[0] != '$') { - throw new \InvalidArgumentException("First key in \$update must be a \$operator"); - } - - $bulk->update($args[0], $args[1], $options); - break; - - case "replaceOne": - if (!isset($args[1])) { - throw new \InvalidArgumentException(sprintf("Missing argument#2 for '%s' (operation#%d)", $opname, $n)); - } - $options = array_merge($this->getWriteOptions(), isset($args[2]) ? $args[2] : array(), array("limit" => 1)); - if (key($args[1])[0] == '$') { - throw new \InvalidArgumentException("First key in \$update must NOT be a \$operator"); - } - - $bulk->update($args[0], $args[1], $options); - break; + if (key($update)[0] != '$') { + throw new \InvalidArgumentException("First key in \$update must be a \$operator"); + } - case "deleteOne": - $options = array_merge($this->getWriteOptions(), isset($args[1]) ? $args[1] : array(), array("limit" => 1)); - $bulk->delete($args[0], $options); - break; + $options = array_merge($this->getFindOneAndUpdateOptions(), $options); + $options = $this->_massageFindAndModifyOptions($options, $update); - case "deleteMany": - $options = array_merge($this->getWriteOptions(), isset($args[1]) ? $args[1] : array(), array("limit" => 0)); - $bulk->delete($args[0], $options); - break; + $cmd = array( + "findandmodify" => $this->collname, + "query" => $filter, + ) + $options; - default: - throw new \InvalidArgumentException(sprintf("Unknown operation type called '%s' (operation#%d)", $opname, $n)); - } - } + $doc = $this->_runCommand($this->dbname, $cmd)->toArray(); + if ($doc["ok"]) { + return $doc["value"]; } - return $this->manager->executeBulkWrite($this->ns, $bulk, $this->wc); - } - - /** - * Inserts the provided document - * - * @see http://docs.mongodb.org/manual/reference/command/insert/ - * - * @param array $document The document to insert - * @param array $options Additional options - * @return InsertOneResult - */ - public function insertOne(array $document) - { - $options = array_merge($this->getWriteOptions()); - - $bulk = new BulkWrite($options["ordered"]); - $id = $bulk->insert($document); - $wr = $this->manager->executeBulkWrite($this->ns, $bulk, $this->wc); - return new InsertOneResult($wr, $id); + throw $this->_generateCommandException($doc); } /** - * Inserts the provided documents - * - * @see http://docs.mongodb.org/manual/reference/command/insert/ + * Retrieves all aggregate options with their default values. * - * @param array $documents The documents to insert - * @return InsertManyResult + * @return array of Collection::aggregate() options */ - public function insertMany(array $documents) + public function getAggregateOptions() { - $options = array_merge($this->getWriteOptions()); - - $bulk = new BulkWrite($options["ordered"]); - $insertedIds = array(); + $opts = array( + /** + * Enables writing to temporary files. When set to true, aggregation stages + * can write data to the _tmp subdirectory in the dbPath directory. The + * default is false. + * + * @see http://docs.mongodb.org/manual/reference/command/aggregate/ + */ + "allowDiskUse" => false, - foreach ($documents as $i => $document) { - $insertedId = $bulk->insert($document); + /** + * The number of documents to return per batch. + * + * @see http://docs.mongodb.org/manual/reference/command/aggregate/ + */ + "batchSize" => 0, - if ($insertedId !== null) { - $insertedIds[$i] = $insertedId; - } - } + /** + * The maximum amount of time to allow the query to run. + * + * @see http://docs.mongodb.org/manual/reference/command/aggregate/ + */ + "maxTimeMS" => 0, - $writeResult = $this->manager->executeBulkWrite($this->ns, $bulk, $this->wc); + /** + * Indicates if the results should be provided as a cursor. + * + * The default for this value depends on the version of the server. + * - Servers >= 2.6 will use a default of true. + * - Servers < 2.6 will use a default of false. + * + * As with any other property, this value can be changed. + * + * @see http://docs.mongodb.org/manual/reference/command/aggregate/ + */ + "useCursor" => true, + ); - return new InsertManyResult($writeResult, $insertedIds); + /* FIXME: Add a version check for useCursor */ + return $opts; } /** - * Internal helper for delete one/many documents - * @internal + * Retrieves all Bulk Write options with their default values. + * + * @return array of available Bulk Write options */ - final protected function _delete($filter, $limit = 1) + public function getBulkOptions() { - $options = array_merge($this->getWriteOptions(), array("limit" => $limit)); - - $bulk = new BulkWrite($options["ordered"]); - $bulk->delete($filter, $options); - return $this->manager->executeBulkWrite($this->ns, $bulk, $this->wc); + return array( + "ordered" => false, + ); } /** - * Deletes a document matching the $filter criteria. - * NOTE: Will delete at most ONE document matching $filter - * - * @see http://docs.mongodb.org/manual/reference/command/delete/ + * Returns the CollectionName this object operates on * - * @param array $filter The $filter criteria to delete - * @return DeleteResult + * @return string */ - public function deleteOne(array $filter) + public function getCollectionName() { - $wr = $this->_delete($filter); - - return new DeleteResult($wr); + return $this->collname; } /** - * Deletes a document matching the $filter criteria. - * NOTE: Will delete ALL documents matching $filter - * - * @see http://docs.mongodb.org/manual/reference/command/delete/ + * Retrieves all count options with their default values. * - * @param array $filter The $filter criteria to delete - * @return DeleteResult + * @return array of Collection::count() options */ - public function deleteMany(array $filter) + public function getCountOptions() { - $wr = $this->_delete($filter, 0); + return array( + /** + * The index to use. + * + * @see http://docs.mongodb.org/manual/reference/command/count/ + */ + "hint" => "", // string or document - return new DeleteResult($wr); + /** + * The maximum number of documents to count. + * + * @see http://docs.mongodb.org/manual/reference/command/count/ + */ + "limit" => 0, + + /** + * The maximum amount of time to allow the query to run. + * + * @see http://docs.mongodb.org/manual/reference/command/count/ + */ + "maxTimeMS" => 0, + + /** + * The number of documents to skip before returning the documents. + * + * @see http://docs.mongodb.org/manual/reference/command/count/ + */ + "skip" => 0, + ); } /** - * Internal helper for replacing/updating one/many documents - * @internal + * Returns the DatabaseName this object operates on + * + * @return string */ - protected function _update($filter, $update, $options) + public function getDatabaseName() { - $options = array_merge($this->getWriteOptions(), $options); - - $bulk = new BulkWrite($options["ordered"]); - $bulk->update($filter, $update, $options); - return $this->manager->executeBulkWrite($this->ns, $bulk, $this->wc); + return $this->dbname; } /** - * Replace one document - * - * @see http://docs.mongodb.org/manual/reference/command/update/ - * @see Collection::getWriteOptions() for supported $options + * Retrieves all distinct options with their default values. * - * @param array $filter The document to be replaced - * @param array $update The document to replace with - * @param array $options Additional options - * @return UpdateResult + * @return array of Collection::distinct() options */ - public function replaceOne(array $filter, array $update, array $options = array()) + public function getDistinctOptions() { - if (key($update)[0] == '$') { - throw new \InvalidArgumentException("First key in \$update must NOT be a \$operator"); - } - $wr = $this->_update($filter, $update, $options); - - return new UpdateResult($wr); + return array( + /** + * The maximum amount of time to allow the query to run. The default is infinite. + * + * @see http://docs.mongodb.org/manual/reference/command/distinct/ + */ + "maxTimeMS" => 0, + ); } /** - * Update one document - * NOTE: Will update at most ONE document matching $filter - * - * @see http://docs.mongodb.org/manual/reference/command/update/ - * @see Collection::getWriteOptions() for supported $options + * Retrieves all findOneDelete options with their default values. * - * @param array $filter The document to be replaced - * @param array $update An array of update operators to apply to the document - * @param array $options Additional options - * @return UpdateResult + * @return array of Collection::findOneAndDelete() options */ - public function updateOne(array $filter, array $update, array $options = array()) + public function getFindOneAndDeleteOptions() { - if (key($update)[0] != '$') { - throw new \InvalidArgumentException("First key in \$update must be a \$operator"); - } - $wr = $this->_update($filter, $update, $options); + return array( - return new UpdateResult($wr); + /** + * The maximum amount of time to allow the query to run. + * + * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ + */ + "maxTimeMS" => 0, + + /** + * Limits the fields to return for all matching documents. + * + * @see http://docs.mongodb.org/manual/tutorial/project-fields-from-query-results + */ + "projection" => array(), + + /** + * Determines which document the operation modifies if the query selects multiple documents. + * + * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ + */ + "sort" => array(), + ); } /** - * Update one document - * NOTE: Will update ALL documents matching $filter - * - * @see http://docs.mongodb.org/manual/reference/command/update/ - * @see Collection::getWriteOptions() for supported $options + * Retrieves all findOneAndReplace options with their default values. * - * @param array $filter The document to be replaced - * @param array $update An array of update operators to apply to the document - * @param array $options Additional options - * @return UpdateResult + * @return array of Collection::findOneAndReplace() options */ - public function updateMany(array $filter, $update, array $options = array()) + public function getFindOneAndReplaceOptions() { - $wr = $this->_update($filter, $update, $options + array("limit" => 0)); + return array( - return new UpdateResult($wr); + /** + * The maximum amount of time to allow the query to run. + * + * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ + */ + "maxTimeMS" => 0, + + /** + * Limits the fields to return for all matching documents. + * + * @see http://docs.mongodb.org/manual/tutorial/project-fields-from-query-results + */ + "projection" => array(), + + /** + * When ReturnDocument.After, returns the replaced or inserted document rather than the original. + * Defaults to ReturnDocument.Before. + * + * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ + */ + "returnDocument" => self::FIND_ONE_AND_RETURN_BEFORE, + + /** + * Determines which document the operation modifies if the query selects multiple documents. + * + * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ + */ + "sort" => array(), + + /** + * When true, findAndModify creates a new document if no document matches the query. The + * default is false. + * + * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ + */ + "upsert" => false, + ); } /** - * Counts all documents matching $filter - * If no $filter provided, returns the numbers of documents in the collection - * - * @see http://docs.mongodb.org/manual/reference/command/count/ - * @see Collection::getCountOptions() for supported $options + * Retrieves all findOneAndUpdate options with their default values. * - * @param array $filter The find query to execute - * @param array $options Additional options - * @return integer + * @return array of Collection::findOneAndUpdate() options */ - public function count(array $filter = array(), array $options = array()) + public function getFindOneAndUpdateOptions() { - $cmd = array( - "count" => $this->collname, - "query" => $filter, - ) + $options; + return array( - $doc = $this->_runCommand($this->dbname, $cmd)->toArray(); - if ($doc["ok"]) { - return $doc["n"]; - } - throw $this->_generateCommandException($doc); + /** + * The maximum amount of time to allow the query to run. + * + * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ + */ + "maxTimeMS" => 0, + + /** + * Limits the fields to return for all matching documents. + * + * @see http://docs.mongodb.org/manual/tutorial/project-fields-from-query-results + */ + "projection" => array(), + + /** + * When ReturnDocument.After, returns the updated or inserted document rather than the original. + * Defaults to ReturnDocument.Before. + * + * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ + */ + "returnDocument" => self::FIND_ONE_AND_RETURN_BEFORE, + + /** + * Determines which document the operation modifies if the query selects multiple documents. + * + * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ + */ + "sort" => array(), + + /** + * When true, creates a new document if no document matches the query. The default is false. + * + * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ + */ + "upsert" => false, + ); } /** - * Retrieves all count options with their default values. + * Retrieves all find options with their default values. * - * @return array of Collection::count() options + * @return array of Collection::find() options */ - public function getCountOptions() + public function getFindOptions() { return array( /** - * The index to use. + * Get partial results from a mongos if some shards are down (instead of throwing an error). * - * @see http://docs.mongodb.org/manual/reference/command/count/ + * @see http://docs.mongodb.org/meta-driver/latest/legacy/mongodb-wire-protocol/#op-query */ - "hint" => "", // string or document + "allowPartialResults" => false, /** - * The maximum number of documents to count. + * The number of documents to return per batch. * - * @see http://docs.mongodb.org/manual/reference/command/count/ + * @see http://docs.mongodb.org/manual/reference/method/cursor.batchSize/ */ - "limit" => 0, + "batchSize" => 101, /** - * The maximum amount of time to allow the query to run. + * Attaches a comment to the query. If $comment also exists + * in the modifiers document, the comment field overwrites $comment. * - * @see http://docs.mongodb.org/manual/reference/command/count/ + * @see http://docs.mongodb.org/manual/reference/operator/meta/comment/ */ - "maxTimeMS" => 0, + "comment" => "", /** - * The number of documents to skip before returning the documents. + * Indicates the type of cursor to use. This value includes both + * the tailable and awaitData options. + * The default is Collection::CURSOR_TYPE_NON_TAILABLE. * - * @see http://docs.mongodb.org/manual/reference/command/count/ + * @see http://docs.mongodb.org/manual/reference/operator/meta/comment/ */ - "skip" => 0, - ); - } - - /** - * Finds the distinct values for a specified field across the collection - * - * @see http://docs.mongodb.org/manual/reference/command/distinct/ - * @see Collection::getDistinctOptions() for supported $options - * - * @param string $fieldName The fieldname to use - * @param array $filter The find query to execute - * @param array $options Additional options - * @return integer - */ - public function distinct($fieldName, array $filter = array(), array $options = array()) - { - $options = array_merge($this->getDistinctOptions(), $options); - $cmd = array( - "distinct" => $this->collname, - "key" => $fieldName, - "query" => $filter, - ) + $options; + "cursorType" => self::CURSOR_TYPE_NON_TAILABLE, - $doc = $this->_runCommand($this->dbname, $cmd)->toArray(); - if ($doc["ok"]) { - return $doc["values"]; - } - throw $this->_generateCommandException($doc); - } + /** + * The maximum number of documents to return. + * + * @see http://docs.mongodb.org/manual/reference/method/cursor.limit/ + */ + "limit" => 0, - /** - * Retrieves all distinct options with their default values. - * - * @return array of Collection::distinct() options - */ - public function getDistinctOptions() - { - return array( /** - * The maximum amount of time to allow the query to run. The default is infinite. + * The maximum amount of time to allow the query to run. If $maxTimeMS also exists + * in the modifiers document, the maxTimeMS field overwrites $maxTimeMS. * - * @see http://docs.mongodb.org/manual/reference/command/distinct/ + * @see http://docs.mongodb.org/manual/reference/operator/meta/maxTimeMS/ */ "maxTimeMS" => 0, - ); - } - - /** - * Runs an aggregation framework pipeline - * NOTE: The return value of this method depends on your MongoDB server version - * and possibly options. - * MongoDB 2.6 (and later) will return a Cursor by default - * MongoDB pre 2.6 will return an ArrayIterator - * - * @see http://docs.mongodb.org/manual/reference/command/aggregate/ - * @see Collection::getAggregateOptions() for supported $options - * - * @param array $pipeline The pipeline to execute - * @param array $options Additional options - * @return Iterator - */ - public function aggregate(array $pipeline, array $options = array()) - { - $options = array_merge($this->getAggregateOptions(), $options); - $options = $this->_massageAggregateOptions($options); - $cmd = array( - "aggregate" => $this->collname, - "pipeline" => $pipeline, - ) + $options; - - $result = $this->_runCommand($this->dbname, $cmd); - $doc = $result->toArray(); - if (isset($cmd["cursor"]) && $cmd["cursor"]) { - return $result; - } else { - if ($doc["ok"]) { - return new \ArrayIterator($doc["result"]); - } - } - - throw $this->_generateCommandException($doc); - } - /** - * Retrieves all aggregate options with their default values. - * - * @return array of Collection::aggregate() options - */ - public function getAggregateOptions() - { - $opts = array( /** - * Enables writing to temporary files. When set to true, aggregation stages - * can write data to the _tmp subdirectory in the dbPath directory. The - * default is false. + * Meta-operators modifying the output or behavior of a query. * - * @see http://docs.mongodb.org/manual/reference/command/aggregate/ + * @see http://docs.mongodb.org/manual/reference/operator/query-modifier/ */ - "allowDiskUse" => false, + "modifiers" => array(), /** - * The number of documents to return per batch. + * The server normally times out idle cursors after an inactivity period (10 minutes) + * to prevent excess memory use. Set this option to prevent that. * - * @see http://docs.mongodb.org/manual/reference/command/aggregate/ + * @see http://docs.mongodb.org/meta-driver/latest/legacy/mongodb-wire-protocol/#op-query */ - "batchSize" => 0, + "noCursorTimeout" => false, /** - * The maximum amount of time to allow the query to run. + * Internal replication use only - driver should not set * - * @see http://docs.mongodb.org/manual/reference/command/aggregate/ + * @see http://docs.mongodb.org/meta-driver/latest/legacy/mongodb-wire-protocol/#op-query + * @internal */ - "maxTimeMS" => 0, + "oplogReplay" => false, /** - * Indicates if the results should be provided as a cursor. + * Limits the fields to return for all matching documents. * - * The default for this value depends on the version of the server. - * - Servers >= 2.6 will use a default of true. - * - Servers < 2.6 will use a default of false. + * @see http://docs.mongodb.org/manual/tutorial/project-fields-from-query-results/ + */ + "projection" => array(), + + /** + * The number of documents to skip before returning. * - * As with any other property, this value can be changed. + * @see http://docs.mongodb.org/manual/reference/method/cursor.skip/ + */ + "skip" => 0, + + /** + * The order in which to return matching documents. If $orderby also exists + * in the modifiers document, the sort field overwrites $orderby. * - * @see http://docs.mongodb.org/manual/reference/command/aggregate/ + * @see http://docs.mongodb.org/manual/reference/method/cursor.sort/ */ - "useCursor" => true, + "sort" => array(), ); - - /* FIXME: Add a version check for useCursor */ - return $opts; } /** - * Internal helper for massaging aggregate options - * @internal + * Retrieves all Write options with their default values. + * + * @return array of available Write options */ - protected function _massageAggregateOptions($options) + public function getWriteOptions() { - if ($options["useCursor"]) { - $options["cursor"] = array("batchSize" => $options["batchSize"]); - } - unset($options["useCursor"], $options["batchSize"]); - - return $options; + return array( + "ordered" => false, + "upsert" => false, + "limit" => 1, + ); } /** - * Finds a single document and deletes it, returning the original. + * Inserts the provided documents * - * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ - * @see Collection::getFindOneAndDelete() for supported $options + * @see http://docs.mongodb.org/manual/reference/command/insert/ * - * @param array $filter The $filter criteria to search for - * @param array $options Additional options - * @return array The original document + * @param array $documents The documents to insert + * @return InsertManyResult */ - public function findOneAndDelete(array $filter, array $options = array()) + public function insertMany(array $documents) { - $options = array_merge($this->getFindOneAndDeleteOptions(), $options); - $options = $this->_massageFindAndModifyOptions($options); - $cmd = array( - "findandmodify" => $this->collname, - "query" => $filter, - ) + $options; + $options = array_merge($this->getWriteOptions()); - $doc = $this->_runCommand($this->dbname, $cmd)->toArray(); - if ($doc["ok"]) { - return $doc["value"]; + $bulk = new BulkWrite($options["ordered"]); + $insertedIds = array(); + + foreach ($documents as $i => $document) { + $insertedId = $bulk->insert($document); + + if ($insertedId !== null) { + $insertedIds[$i] = $insertedId; + } } - throw $this->_generateCommandException($doc); + $writeResult = $this->manager->executeBulkWrite($this->ns, $bulk, $this->wc); + + return new InsertManyResult($writeResult, $insertedIds); } /** - * Retrieves all findOneDelete options with their default values. + * Inserts the provided document * - * @return array of Collection::findOneAndDelete() options + * @see http://docs.mongodb.org/manual/reference/command/insert/ + * + * @param array $document The document to insert + * @param array $options Additional options + * @return InsertOneResult */ - public function getFindOneAndDeleteOptions() + public function insertOne(array $document) { - return array( - - /** - * The maximum amount of time to allow the query to run. - * - * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ - */ - "maxTimeMS" => 0, + $options = array_merge($this->getWriteOptions()); - /** - * Limits the fields to return for all matching documents. - * - * @see http://docs.mongodb.org/manual/tutorial/project-fields-from-query-results - */ - "projection" => array(), + $bulk = new BulkWrite($options["ordered"]); + $id = $bulk->insert($document); + $wr = $this->manager->executeBulkWrite($this->ns, $bulk, $this->wc); - /** - * Determines which document the operation modifies if the query selects multiple documents. - * - * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ - */ - "sort" => array(), - ); + return new InsertOneResult($wr, $id); } /** - * Finds a single document and replaces it, returning either the original or the replaced document - * By default, returns the original document. - * To return the new document set: - * $options = array("returnDocument" => Collection::FIND_ONE_AND_RETURN_AFTER); + * Replace one document * - * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ - * @see Collection::getFindOneAndReplace() for supported $options + * @see http://docs.mongodb.org/manual/reference/command/update/ + * @see Collection::getWriteOptions() for supported $options * - * @param array $filter The $filter criteria to search for - * @param array $replacement The document to replace with - * @param array $options Additional options - * @return array + * @param array $filter The document to be replaced + * @param array $update The document to replace with + * @param array $options Additional options + * @return UpdateResult */ - public function findOneAndReplace(array $filter, array $replacement, array $options = array()) + public function replaceOne(array $filter, array $update, array $options = array()) { - if (key($replacement)[0] == '$') { - throw new \InvalidArgumentException("First key in \$replacement must NOT be a \$operator"); - } - - $options = array_merge($this->getFindOneAndReplaceOptions(), $options); - $options = $this->_massageFindAndModifyOptions($options, $replacement); - - $cmd = array( - "findandmodify" => $this->collname, - "query" => $filter, - ) + $options; - - $doc = $this->_runCommand($this->dbname, $cmd)->toArray(); - if ($doc["ok"]) { - return $doc["value"]; + if (key($update)[0] == '$') { + throw new \InvalidArgumentException("First key in \$update must NOT be a \$operator"); } + $wr = $this->_update($filter, $update, $options); - throw $this->_generateCommandException($doc); + return new UpdateResult($wr); } /** - * Retrieves all findOneAndReplace options with their default values. + * Update one document + * NOTE: Will update ALL documents matching $filter * - * @return array of Collection::findOneAndReplace() options + * @see http://docs.mongodb.org/manual/reference/command/update/ + * @see Collection::getWriteOptions() for supported $options + * + * @param array $filter The document to be replaced + * @param array $update An array of update operators to apply to the document + * @param array $options Additional options + * @return UpdateResult */ - public function getFindOneAndReplaceOptions() + public function updateMany(array $filter, $update, array $options = array()) { - return array( - - /** - * The maximum amount of time to allow the query to run. - * - * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ - */ - "maxTimeMS" => 0, - - /** - * Limits the fields to return for all matching documents. - * - * @see http://docs.mongodb.org/manual/tutorial/project-fields-from-query-results - */ - "projection" => array(), - - /** - * When ReturnDocument.After, returns the replaced or inserted document rather than the original. - * Defaults to ReturnDocument.Before. - * - * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ - */ - "returnDocument" => self::FIND_ONE_AND_RETURN_BEFORE, - - /** - * Determines which document the operation modifies if the query selects multiple documents. - * - * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ - */ - "sort" => array(), + $wr = $this->_update($filter, $update, $options + array("limit" => 0)); - /** - * When true, findAndModify creates a new document if no document matches the query. The - * default is false. - * - * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ - */ - "upsert" => false, - ); + return new UpdateResult($wr); } /** - * Finds a single document and updates it, returning either the original or the updated document - * By default, returns the original document. - * To return the new document set: - * $options = array("returnDocument" => Collection::FIND_ONE_AND_RETURN_AFTER); - * + * Update one document + * NOTE: Will update at most ONE document matching $filter * - * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ - * @see Collection::getFindOneAndUpdate() for supported $options + * @see http://docs.mongodb.org/manual/reference/command/update/ + * @see Collection::getWriteOptions() for supported $options * - * @param array $filter The $filter criteria to search for + * @param array $filter The document to be replaced * @param array $update An array of update operators to apply to the document * @param array $options Additional options - * @return array + * @return UpdateResult */ - public function findOneAndUpdate(array $filter, array $update, array $options = array()) + public function updateOne(array $filter, array $update, array $options = array()) { if (key($update)[0] != '$') { throw new \InvalidArgumentException("First key in \$update must be a \$operator"); } + $wr = $this->_update($filter, $update, $options); - $options = array_merge($this->getFindOneAndUpdateOptions(), $options); - $options = $this->_massageFindAndModifyOptions($options, $update); - - $cmd = array( - "findandmodify" => $this->collname, - "query" => $filter, - ) + $options; - - $doc = $this->_runCommand($this->dbname, $cmd)->toArray(); - if ($doc["ok"]) { - return $doc["value"]; - } - - throw $this->_generateCommandException($doc); + return new UpdateResult($wr); } /** - * Retrieves all findOneAndUpdate options with their default values. + * Helper to build a Query object * - * @return array of Collection::findOneAndUpdate() options + * @param array $filter the query document + * @param array $options query/protocol options + * @return Query + * @internal */ - public function getFindOneAndUpdateOptions() + final protected function _buildQuery($filter, $options) { - return array( - - /** - * The maximum amount of time to allow the query to run. - * - * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ - */ - "maxTimeMS" => 0, + if ($options["comment"]) { + $options["modifiers"]['$comment'] = $options["comment"]; + } + if ($options["maxTimeMS"]) { + $options["modifiers"]['$maxTimeMS'] = $options["maxTimeMS"]; + } + if ($options["sort"]) { + $options['$orderby'] = $options["sort"]; + } - /** - * Limits the fields to return for all matching documents. - * - * @see http://docs.mongodb.org/manual/tutorial/project-fields-from-query-results - */ - "projection" => array(), + $flags = $this->_opQueryFlags($options); + $options["cursorFlags"] = $flags; - /** - * When ReturnDocument.After, returns the updated or inserted document rather than the original. - * Defaults to ReturnDocument.Before. - * - * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ - */ - "returnDocument" => self::FIND_ONE_AND_RETURN_BEFORE, - /** - * Determines which document the operation modifies if the query selects multiple documents. - * - * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ - */ - "sort" => array(), + $query = new Query($filter, $options); - /** - * When true, creates a new document if no document matches the query. The default is false. - * - * @see http://docs.mongodb.org/manual/reference/command/findAndModify/ - */ - "upsert" => false, - ); + return $query; } /** - * Internal helper for massaging findandmodify options + * Internal helper for delete one/many documents * @internal */ - final protected function _massageFindAndModifyOptions($options, $update = array()) + final protected function _delete($filter, $limit = 1) { - $ret = array( - "sort" => $options["sort"], - "new" => isset($options["returnDocument"]) ? $options["returnDocument"] == self::FIND_ONE_AND_RETURN_AFTER : false, - "fields" => $options["projection"], - "upsert" => isset($options["upsert"]) ? $options["upsert"] : false, - ); - if ($update) { - $ret["update"] = $update; - } else { - $ret["remove"] = true; - } + $options = array_merge($this->getWriteOptions(), array("limit" => $limit)); - return $ret; + $bulk = new BulkWrite($options["ordered"]); + $bulk->delete($filter, $options); + return $this->manager->executeBulkWrite($this->ns, $bulk, $this->wc); } /** @@ -1105,33 +1057,60 @@ final protected function _generateCommandException($doc) } /** - * Internal helper for running a command + * Internal helper for massaging aggregate options * @internal */ - final protected function _runCommand($dbname, array $cmd, ReadPreference $rp = null) + protected function _massageAggregateOptions($options) { - //var_dump(\BSON\toJSON(\BSON\fromArray($cmd))); - $command = new Command($cmd); - return $this->manager->executeCommand($dbname, $command, $rp); + if ($options["useCursor"]) { + $options["cursor"] = array("batchSize" => $options["batchSize"]); + } + unset($options["useCursor"], $options["batchSize"]); + + return $options; } /** - * Returns the CollectionName this object operates on + * Constructs the Query Wire Protocol field 'flags' based on $options + * provided to other helpers * - * @return string + * @param array $options + * @return integer OP_QUERY Wire Protocol flags + * @internal */ - public function getCollectionName() + final protected function _opQueryFlags($options) { - return $this->collname; + $flags = 0; + + $flags |= $options["allowPartialResults"] ? self::QUERY_FLAG_PARTIAL : 0; + $flags |= $options["cursorType"] ? $options["cursorType"] : 0; + $flags |= $options["oplogReplay"] ? self::QUERY_FLAG_OPLOG_REPLY: 0; + $flags |= $options["noCursorTimeout"] ? self::QUERY_FLAG_NO_CURSOR_TIMEOUT : 0; + + return $flags; } /** - * Returns the DatabaseName this object operates on - * - * @return string + * Internal helper for running a command + * @internal */ - public function getDatabaseName() + final protected function _runCommand($dbname, array $cmd, ReadPreference $rp = null) { - return $this->dbname; + //var_dump(\BSON\toJSON(\BSON\fromArray($cmd))); + $command = new Command($cmd); + return $this->manager->executeCommand($dbname, $command, $rp); + } + + /** + * Internal helper for replacing/updating one/many documents + * @internal + */ + protected function _update($filter, $update, $options) + { + $options = array_merge($this->getWriteOptions(), $options); + + $bulk = new BulkWrite($options["ordered"]); + $bulk->update($filter, $update, $options); + return $this->manager->executeBulkWrite($this->ns, $bulk, $this->wc); } } diff --git a/tests/CollectionTest.php b/tests/CollectionTest.php index 059caa547..e54ee92bb 100644 --- a/tests/CollectionTest.php +++ b/tests/CollectionTest.php @@ -6,7 +6,7 @@ class CollectionTest extends PHPUnit_Framework_TestCase { function setUp() { - require __DIR__ . "/" . "utils.inc"; + require_once __DIR__ . "/" . "utils.inc"; $this->faker = Faker\Factory::create(); $this->faker->seed(1234); @@ -44,5 +44,28 @@ function testInsertAndRetrieve() { } $this->assertEquals(0, $n); } + + public function testMethodOrder() + { + $class = new ReflectionClass('MongoDB\Collection'); + + $filters = array( + 'public' => ReflectionMethod::IS_PUBLIC, + 'protected' => ReflectionMethod::IS_PROTECTED, + 'private' => ReflectionMethod::IS_PRIVATE, + ); + + foreach ($filters as $visibility => $filter) { + $methods = array_map( + function(ReflectionMethod $method) { return $method->getName(); }, + $class->getMethods($filter) + ); + + $sortedMethods = $methods; + sort($sortedMethods); + + $this->assertEquals($methods, $sortedMethods, sprintf('%s methods are declared alphabetically', ucfirst($visibility))); + } + } } From 448800b20143bd3d61268bb00996567e0fbd0ffe Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Tue, 17 Mar 2015 23:33:34 -0400 Subject: [PATCH 7/8] PHPLIB-46: Prototypes for index enumeration method --- src/Collection.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Collection.php b/src/Collection.php index 3363ca4f0..49856b55e 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -939,6 +939,18 @@ public function insertOne(array $document) return new InsertOneResult($wr, $id); } + /** + * Returns information for all indexes in the collection. + * + * @see http://docs.mongodb.org/manual/reference/command/listIndexes/ + * @see http://docs.mongodb.org/manual/reference/method/db.collection.getIndexes/ + * @return Result + */ + public function listIndexes() + { + // TODO + } + /** * Replace one document * From ea6da09fd9d40e27aa84feadbcbbe0514ea66530 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Tue, 17 Mar 2015 23:33:52 -0400 Subject: [PATCH 8/8] PHPLIB-45: Prototype for collection enumeration method --- src/Database.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Database.php b/src/Database.php index bb50a104c..cb772b3a6 100644 --- a/src/Database.php +++ b/src/Database.php @@ -68,6 +68,18 @@ public function dropCollection($collectionName) // TODO } + /** + * Returns information for all collections in this database. + * + * @see http://docs.mongodb.org/manual/reference/command/listCollections/ + * @param array $options + * @return Result + */ + public function listCollections(array $options = array()) + { + // TODO + } + /** * Select a specific collection in this database *