Commit 495e46d4 authored by Jeremy Mikola's avatar Jeremy Mikola

Declare Collection methods alphabetically by visibility

We can move this to a separate, pedantic test class later.
parent b6a120e0
......@@ -64,6 +64,182 @@ class Collection
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 @@ class Collection
// 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,487 +414,174 @@ class Collection
}
/**
* Retrieves all find options with their default values.
* Finds a single document and deletes it, returning the original.
*
* @return array of Collection::find() options
*/
public function getFindOptions()
{
return array(
/**
* Get partial results from a mongos if some shards are down (instead of throwing an error).
* @see http://docs.mongodb.org/manual/reference/command/findAndModify/
* @see Collection::getFindOneAndDelete() for supported $options
*
* @see http://docs.mongodb.org/meta-driver/latest/legacy/mongodb-wire-protocol/#op-query
* @param array $filter The $filter criteria to search for
* @param array $options Additional options
* @return array The original document
*/
"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(),
/**
* The number of documents to skip before returning.
*
* @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/method/cursor.sort/
*/
"sort" => array(),
);
}
/**
* Constructs the Query Wire Protocol field 'flags' based on $options
* provided to other helpers
*
* @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
*
* @param array $filter the query document
* @param array $options query/protocol options
* @return Query
* @internal
*/
final protected function _buildQuery($filter, $options)
public function findOneAndDelete(array $filter, 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"];
}
$flags = $this->_opQueryFlags($options);
$options["cursorFlags"] = $flags;
$query = new Query($filter, $options);
return $query;
}
$options = array_merge($this->getFindOneAndDeleteOptions(), $options);
$options = $this->_massageFindAndModifyOptions($options);
$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
*
* @example Collection-bulkWrite.php Using Collection::bulkWrite()
* 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);
*
* @see Collection::getBulkOptions() for supported $options
* @see http://docs.mongodb.org/manual/reference/command/findAndModify/
* @see Collection::getFindOneAndReplace() for supported $options
*
* @param array $bulk Array of operations
* @param array $filter The $filter criteria to search for
* @param array $replacement The document to replace with
* @param array $options Additional options
* @return WriteResult
* @return array
*/
public function bulkWrite(array $bulk, array $options = array())
public function findOneAndReplace(array $filter, array $replacement, 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));
if (key($replacement)[0] == '$') {
throw new \InvalidArgumentException("First key in \$replacement must NOT be a \$operator");
}
$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");
}
$options = array_merge($this->getFindOneAndReplaceOptions(), $options);
$options = $this->_massageFindAndModifyOptions($options, $replacement);
$bulk->update($args[0], $args[1], $options);
break;
$cmd = array(
"findandmodify" => $this->collname,
"query" => $filter,
) + $options;
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");
$doc = $this->_runCommand($this->dbname, $cmd)->toArray();
if ($doc["ok"]) {
return $doc["value"];
}
$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);
throw $this->_generateCommandException($doc);
}
/**
* Inserts the provided document
* 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);
*
* @see http://docs.mongodb.org/manual/reference/command/insert/
*
* @param array $document The document to insert
* @see http://docs.mongodb.org/manual/reference/command/findAndModify/
* @see Collection::getFindOneAndUpdate() for supported $options
*
* @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 InsertOneResult
* @return array
*/
public function insertOne(array $document)
public function findOneAndUpdate(array $filter, array $update, array $options = array())
{
$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);
if (key($update)[0] != '$') {
throw new \InvalidArgumentException("First key in \$update must be a \$operator");
}
/**
* Inserts the provided documents
*
* @see http://docs.mongodb.org/manual/reference/command/insert/
*
* @param array $documents The documents to insert
* @return InsertManyResult
*/
public function insertMany(array $documents)
{
$options = array_merge($this->getWriteOptions());
$bulk = new BulkWrite($options["ordered"]);
$insertedIds = array();
foreach ($documents as $i => $document) {
$insertedId = $bulk->insert($document);
if ($insertedId !== null) {
$insertedIds[$i] = $insertedId;
}
}
$options = array_merge($this->getFindOneAndUpdateOptions(), $options);
$options = $this->_massageFindAndModifyOptions($options, $update);
$writeResult = $this->manager->executeBulkWrite($this->ns, $bulk, $this->wc);
$cmd = array(
"findandmodify" => $this->collname,
"query" => $filter,
) + $options;
return new InsertManyResult($writeResult, $insertedIds);
$doc = $this->_runCommand($this->dbname, $cmd)->toArray();
if ($doc["ok"]) {
return $doc["value"];
}
/**
* Internal helper for delete one/many documents
* @internal
*/
final protected function _delete($filter, $limit = 1)
{
$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);
throw $this->_generateCommandException($doc);
}
/**
* 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/
* Retrieves all aggregate options with their default values.
*
* @param array $filter The $filter criteria to delete
* @return DeleteResult
* @return array of Collection::aggregate() options
*/
public function deleteOne(array $filter)
public function getAggregateOptions()
{
$wr = $this->_delete($filter);
return new DeleteResult($wr);
}
$opts = array(
/**
* Deletes a document matching the $filter criteria.
* NOTE: Will delete ALL documents matching $filter
*
* @see http://docs.mongodb.org/manual/reference/command/delete/
* 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.
*
* @param array $filter The $filter criteria to delete
* @return DeleteResult
* @see http://docs.mongodb.org/manual/reference/command/aggregate/
*/
public function deleteMany(array $filter)
{
$wr = $this->_delete($filter, 0);
return new DeleteResult($wr);
}
"allowDiskUse" => false,
/**
* Internal helper for replacing/updating one/many documents
* @internal
* The number of documents to return per batch.
*
* @see http://docs.mongodb.org/manual/reference/command/aggregate/
*/
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);
}
"batchSize" => 0,
/**
* Replace one document
*
* @see http://docs.mongodb.org/manual/reference/command/update/
* @see Collection::getWriteOptions() for supported $options
* The maximum amount of time to allow the query to run.
*
* @param array $filter The document to be replaced
* @param array $update The document to replace with
* @param array $options Additional options
* @return UpdateResult
* @see http://docs.mongodb.org/manual/reference/command/aggregate/
*/
public function replaceOne(array $filter, array $update, array $options = array())
{
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);
}
"maxTimeMS" => 0,
/**
* Update one document
* NOTE: Will update at most ONE document matching $filter
* Indicates if the results should be provided as a cursor.
*
* @see http://docs.mongodb.org/manual/reference/command/update/
* @see Collection::getWriteOptions() for supported $options
* 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.
*
* @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
* As with any other property, this value can be changed.
*
* @see http://docs.mongodb.org/manual/reference/command/aggregate/
*/
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);
"useCursor" => true,
);
return new UpdateResult($wr);
/* FIXME: Add a version check for useCursor */
return $opts;
}
/**
* 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 Bulk Write 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 available Bulk Write options
*/
public function updateMany(array $filter, $update, array $options = array())
public function getBulkOptions()
{
$wr = $this->_update($filter, $update, $options + array("limit" => 0));
return new UpdateResult($wr);
return array(
"ordered" => 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
* Returns the CollectionName this object operates on
*
* @param array $filter The find query to execute
* @param array $options Additional options
* @return integer
* @return string
*/
public function count(array $filter = array(), array $options = array())
public function getCollectionName()
{
$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);
return $this->collname;
}
/**
......@@ -701,30 +623,13 @@ class Collection
}
/**
* 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
* Returns the DatabaseName this object operates on
*
* @param string $fieldName The fieldname to use
* @param array $filter The find query to execute
* @param array $options Additional options
* @return integer
* @return string
*/
public function distinct($fieldName, array $filter = array(), array $options = array())
public function getDatabaseName()
{
$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);
return $this->dbname;
}
/**
......@@ -745,137 +650,91 @@ class Collection
}
/**
* 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
* Retrieves all findOneDelete options with their default values.
*
* @param array $pipeline The pipeline to execute
* @param array $options Additional options
* @return Iterator
* @return array of Collection::findOneAndDelete() options
*/
public function aggregate(array $pipeline, array $options = array())
public function getFindOneAndDeleteOptions()
{
$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"]);
}
}
return array(
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,
/**
* Retrieves all aggregate options with their default values.
* Limits the fields to return for all matching documents.
*
* @return array of Collection::aggregate() options
* @see http://docs.mongodb.org/manual/tutorial/project-fields-from-query-results
*/
public function getAggregateOptions()
{
$opts = array(
"projection" => 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.
* Determines which document the operation modifies if the query selects multiple documents.
*
* @see http://docs.mongodb.org/manual/reference/command/aggregate/
* @see http://docs.mongodb.org/manual/reference/command/findAndModify/
*/
"allowDiskUse" => false,
"sort" => array(),
);
}
/**
* The number of documents to return per batch.
* Retrieves all findOneAndReplace options with their default values.
*
* @see http://docs.mongodb.org/manual/reference/command/aggregate/
* @return array of Collection::findOneAndReplace() options
*/
"batchSize" => 0,
public function getFindOneAndReplaceOptions()
{
return array(
/**
* The maximum amount of time to allow the query to run.
*
* @see http://docs.mongodb.org/manual/reference/command/aggregate/
* @see http://docs.mongodb.org/manual/reference/command/findAndModify/
*/
"maxTimeMS" => 0,
/**
* 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.
* Limits the fields to return for all matching documents.
*
* @see http://docs.mongodb.org/manual/reference/command/aggregate/
* @see http://docs.mongodb.org/manual/tutorial/project-fields-from-query-results
*/
"useCursor" => true,
);
/* FIXME: Add a version check for useCursor */
return $opts;
}
"projection" => array(),
/**
* Internal helper for massaging aggregate options
* @internal
* 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/
*/
protected function _massageAggregateOptions($options)
{
if ($options["useCursor"]) {
$options["cursor"] = array("batchSize" => $options["batchSize"]);
}
unset($options["useCursor"], $options["batchSize"]);
return $options;
}
"returnDocument" => self::FIND_ONE_AND_RETURN_BEFORE,
/**
* Finds a single document and deletes it, returning the original.
* Determines which document the operation modifies if the query selects multiple documents.
*
* @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 findOneAndDelete(array $filter, array $options = array())
{
$options = array_merge($this->getFindOneAndDeleteOptions(), $options);
$options = $this->_massageFindAndModifyOptions($options);
$cmd = array(
"findandmodify" => $this->collname,
"query" => $filter,
) + $options;
$doc = $this->_runCommand($this->dbname, $cmd)->toArray();
if ($doc["ok"]) {
return $doc["value"];
}
"sort" => array(),
throw $this->_generateCommandException($doc);
/**
* 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,
);
}
/**
* Retrieves all findOneDelete options with their default values.
* Retrieves all findOneAndUpdate options with their default values.
*
* @return array of Collection::findOneAndDelete() options
* @return array of Collection::findOneAndUpdate() options
*/
public function getFindOneAndDeleteOptions()
public function getFindOneAndUpdateOptions()
{
return array(
......@@ -893,202 +752,295 @@ class Collection
*/
"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(),
);
}
/**
* 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);
* 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/
* @see Collection::getFindOneAndReplace() for supported $options
*/
"upsert" => false,
);
}
/**
* Retrieves all find options with their default values.
*
* @param array $filter The $filter criteria to search for
* @param array $replacement The document to replace with
* @param array $options Additional options
* @return array
* @return array of Collection::find() options
*/
public function findOneAndReplace(array $filter, array $replacement, array $options = array())
public function getFindOptions()
{
if (key($replacement)[0] == '$') {
throw new \InvalidArgumentException("First key in \$replacement must NOT be a \$operator");
}
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,
$options = array_merge($this->getFindOneAndReplaceOptions(), $options);
$options = $this->_massageFindAndModifyOptions($options, $replacement);
/**
* The number of documents to return per batch.
*
* @see http://docs.mongodb.org/manual/reference/method/cursor.batchSize/
*/
"batchSize" => 101,
$cmd = array(
"findandmodify" => $this->collname,
"query" => $filter,
) + $options;
/**
* 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" => "",
$doc = $this->_runCommand($this->dbname, $cmd)->toArray();
if ($doc["ok"]) {
return $doc["value"];
}
/**
* 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,
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 findOneAndReplace options with their default values.
* 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.
*
* @return array of Collection::findOneAndReplace() options
* @see http://docs.mongodb.org/meta-driver/latest/legacy/mongodb-wire-protocol/#op-query
*/
public function getFindOneAndReplaceOptions()
{
return array(
"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/findAndModify/
* @see http://docs.mongodb.org/meta-driver/latest/legacy/mongodb-wire-protocol/#op-query
* @internal
*/
"maxTimeMS" => 0,
"oplogReplay" => false,
/**
* Limits the fields to return for all matching documents.
*
* @see http://docs.mongodb.org/manual/tutorial/project-fields-from-query-results
* @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.
* The number of documents to skip before returning.
*
* @see http://docs.mongodb.org/manual/reference/command/findAndModify/
* @see http://docs.mongodb.org/manual/reference/method/cursor.skip/
*/
"returnDocument" => self::FIND_ONE_AND_RETURN_BEFORE,
"skip" => 0,
/**
* Determines which document the operation modifies if the query selects multiple documents.
* 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/findAndModify/
* @see http://docs.mongodb.org/manual/reference/method/cursor.sort/
*/
"sort" => array(),
);
}
/**
* When true, findAndModify creates a new document if no document matches the query. The
* default is false.
* Retrieves all Write options with their default values.
*
* @see http://docs.mongodb.org/manual/reference/command/findAndModify/
* @return array of available Write options
*/
public function getWriteOptions()
{
return array(
"ordered" => false,
"upsert" => false,
"limit" => 1,
);
}
/**
* 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);
*
* Inserts the provided documents
*
* @see http://docs.mongodb.org/manual/reference/command/findAndModify/
* @see Collection::getFindOneAndUpdate() for supported $options
* @see http://docs.mongodb.org/manual/reference/command/insert/
*
* @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
* @param array $documents The documents to insert
* @return InsertManyResult
*/
public function findOneAndUpdate(array $filter, array $update, array $options = array())
public function insertMany(array $documents)
{
if (key($update)[0] != '$') {
throw new \InvalidArgumentException("First key in \$update must be a \$operator");
}
$options = array_merge($this->getWriteOptions());
$options = array_merge($this->getFindOneAndUpdateOptions(), $options);
$options = $this->_massageFindAndModifyOptions($options, $update);
$bulk = new BulkWrite($options["ordered"]);
$insertedIds = array();
$cmd = array(
"findandmodify" => $this->collname,
"query" => $filter,
) + $options;
foreach ($documents as $i => $document) {
$insertedId = $bulk->insert($document);
$doc = $this->_runCommand($this->dbname, $cmd)->toArray();
if ($doc["ok"]) {
return $doc["value"];
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 findOneAndUpdate options with their default values.
* Inserts the provided document
*
* @return array of Collection::findOneAndUpdate() 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 getFindOneAndUpdateOptions()
public function insertOne(array $document)
{
return array(
$options = array_merge($this->getWriteOptions());
/**
* The maximum amount of time to allow the query to run.
*
* @see http://docs.mongodb.org/manual/reference/command/findAndModify/
*/
"maxTimeMS" => 0,
$bulk = new BulkWrite($options["ordered"]);
$id = $bulk->insert($document);
$wr = $this->manager->executeBulkWrite($this->ns, $bulk, $this->wc);
return new InsertOneResult($wr, $id);
}
/**
* Limits the fields to return for all matching documents.
* Replace one document
*
* @see http://docs.mongodb.org/manual/tutorial/project-fields-from-query-results
* @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 The document to replace with
* @param array $options Additional options
* @return UpdateResult
*/
"projection" => array(),
public function replaceOne(array $filter, array $update, array $options = array())
{
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);
}
/**
* When ReturnDocument.After, returns the updated or inserted document rather than the original.
* Defaults to ReturnDocument.Before.
* Update one document
* NOTE: Will update ALL documents matching $filter
*
* @see http://docs.mongodb.org/manual/reference/command/findAndModify/
* @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
*/
"returnDocument" => self::FIND_ONE_AND_RETURN_BEFORE,
public function updateMany(array $filter, $update, array $options = array())
{
$wr = $this->_update($filter, $update, $options + array("limit" => 0));
return new UpdateResult($wr);
}
/**
* Determines which document the operation modifies if the query selects multiple documents.
* Update one document
* NOTE: Will update at most ONE document matching $filter
*
* @see http://docs.mongodb.org/manual/reference/command/findAndModify/
* @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
*/
"sort" => 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);
return new UpdateResult($wr);
}
/**
* When true, creates a new document if no document matches the query. The default is false.
* Helper to build a Query object
*
* @see http://docs.mongodb.org/manual/reference/command/findAndModify/
* @param array $filter the query document
* @param array $options query/protocol options
* @return Query
* @internal
*/
"upsert" => false,
);
final protected function _buildQuery($filter, $options)
{
if ($options["comment"]) {
$options["modifiers"]['$comment'] = $options["comment"];
}
if ($options["maxTimeMS"]) {
$options["modifiers"]['$maxTimeMS'] = $options["maxTimeMS"];
}
if ($options["sort"]) {
$options['$orderby'] = $options["sort"];
}
$flags = $this->_opQueryFlags($options);
$options["cursorFlags"] = $flags;
$query = new Query($filter, $options);
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 @@ class Collection
}
/**
* 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);
}
}
......@@ -6,7 +6,7 @@ use MongoDB\Driver\Manager;
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 @@ class CollectionTest extends PHPUnit_Framework_TestCase {
}
$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)));
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment