Commit 3db125f1 authored by Jeremy Mikola's avatar Jeremy Mikola

Merge pull request #5

parents 27543788 ea6da09f
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
namespace MongoDB; namespace MongoDB;
use MongoDB\Driver\Manager;
use MongoDB\Database;
use MongoDB\Collection; use MongoDB\Collection;
use MongoDB\Database;
use MongoDB\Driver\Manager;
use MongoDB\Driver\Result;
class Client class Client
{ {
...@@ -29,6 +30,17 @@ class Client ...@@ -29,6 +30,17 @@ class Client
$this->manager = new Manager($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 * Select a database
* *
......
...@@ -65,226 +65,39 @@ class Collection ...@@ -65,226 +65,39 @@ class Collection
} }
/** /**
* Performs a find (query) on the collection * Runs an aggregation framework pipeline
* * NOTE: The return value of this method depends on your MongoDB server version
* @see http://docs.mongodb.org/manual/core/read-operations-introduction/ * and possibly options.
* @see Collection::getFindOptions() for supported $options * MongoDB 2.6 (and later) will return a Cursor by default
* * MongoDB pre 2.6 will return an ArrayIterator
* @param array $filter The find query to execute
* @param array $options Additional options
* @return Result
*/
public function find(array $filter = array(), array $options = array())
{
$options = array_merge($this->getFindOptions(), $options);
$query = $this->_buildQuery($filter, $options);
$cursor = $this->manager->executeQuery($this->ns, $query, $this->rp);
return $cursor;
}
/**
* Performs a find (query) on the collection, returning at most one result
* *
* @see http://docs.mongodb.org/manual/core/read-operations-introduction/ * @see http://docs.mongodb.org/manual/reference/command/aggregate/
* @see Collection::getFindOptions() for supported $options * @see Collection::getAggregateOptions() for supported $options
* *
* @param array $filter The find query to execute * @param array $pipeline The pipeline to execute
* @param array $options Additional options * @param array $options Additional options
* @return array|false The matched document, or false on failure * @return Iterator
*/
public function findOne(array $filter = array(), array $options = array())
{
$options = array_merge($this->getFindOptions(), array("limit" => 1), $options);
$query = $this->_buildQuery($filter, $options);
$cursor = $this->manager->executeQuery($this->ns, $query, $this->rp);
$array = iterator_to_array($cursor);
if ($array) {
return $array[0];
}
return false;
}
/**
* Retrieves all find options with their default values.
*
* @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/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(),
/**
* 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 aggregate(array $pipeline, array $options = array())
{ {
if ($options["comment"]) { $options = array_merge($this->getAggregateOptions(), $options);
$options["modifiers"]['$comment'] = $options["comment"]; $options = $this->_massageAggregateOptions($options);
} $cmd = array(
if ($options["maxTimeMS"]) { "aggregate" => $this->collname,
$options["modifiers"]['$maxTimeMS'] = $options["maxTimeMS"]; "pipeline" => $pipeline,
} ) + $options;
if ($options["sort"]) {
$options['$orderby'] = $options["sort"];
}
$flags = $this->_opQueryFlags($options);
$options["cursorFlags"] = $flags;
$query = new Query($filter, $options);
return $query; $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"]);
} }
/**
* 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,
);
} }
/** throw $this->_generateCommandException($doc);
* Retrieves all Bulk Write options with their default values.
*
* @return array of available Bulk Write options
*/
public function getBulkOptions()
{
return array(
"ordered" => false,
);
} }
/** /**
...@@ -403,311 +216,298 @@ class Collection ...@@ -403,311 +216,298 @@ class Collection
} }
/** /**
* Inserts the provided document * 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/insert/ * @see http://docs.mongodb.org/manual/reference/command/count/
* @see Collection::getCountOptions() for supported $options
* *
* @param array $document The document to insert * @param array $filter The find query to execute
* @param array $options Additional options * @param array $options Additional options
* @return InsertOneResult * @return integer
*/ */
public function insertOne(array $document) public function count(array $filter = array(), array $options = array())
{ {
$options = array_merge($this->getWriteOptions()); $cmd = array(
"count" => $this->collname,
$bulk = new BulkWrite($options["ordered"]); "query" => $filter,
$id = $bulk->insert($document); ) + $options;
$wr = $this->manager->executeBulkWrite($this->ns, $bulk, $this->wc);
return new InsertOneResult($wr, $id); $doc = $this->_runCommand($this->dbname, $cmd)->toArray();
if ($doc["ok"]) {
return $doc["n"];
}
throw $this->_generateCommandException($doc);
} }
/** /**
* Inserts the provided documents * Create a single index in the collection.
*
* @see http://docs.mongodb.org/manual/reference/command/insert/
* *
* @param array $documents The documents to insert * @see http://docs.mongodb.org/manual/reference/command/createIndexes/
* @return InsertManyResult * @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 insertMany(array $documents) public function createIndex($keys, array $options = array())
{ {
$options = array_merge($this->getWriteOptions()); // TODO
$bulk = new BulkWrite($options["ordered"]);
$insertedIds = array();
foreach ($documents as $i => $document) {
$insertedId = $bulk->insert($document);
if ($insertedId !== null) {
$insertedIds[$i] = $insertedId;
}
}
$writeResult = $this->manager->executeBulkWrite($this->ns, $bulk, $this->wc);
return new InsertManyResult($writeResult, $insertedIds);
} }
/** /**
* Internal helper for delete one/many documents * Create multiple indexes in the collection.
* @internal *
* 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
*/ */
final protected function _delete($filter, $limit = 1) public function createIndexes(array $models)
{ {
$options = array_merge($this->getWriteOptions(), array("limit" => $limit)); // TODO
$bulk = new BulkWrite($options["ordered"]);
$bulk->delete($filter, $options);
return $this->manager->executeBulkWrite($this->ns, $bulk, $this->wc);
} }
/** /**
* Deletes a document matching the $filter criteria. * Deletes a document matching the $filter criteria.
* NOTE: Will delete at most ONE document matching $filter * NOTE: Will delete ALL documents matching $filter
* *
* @see http://docs.mongodb.org/manual/reference/command/delete/ * @see http://docs.mongodb.org/manual/reference/command/delete/
* *
* @param array $filter The $filter criteria to delete * @param array $filter The $filter criteria to delete
* @return DeleteResult * @return DeleteResult
*/ */
public function deleteOne(array $filter) public function deleteMany(array $filter)
{ {
$wr = $this->_delete($filter); $wr = $this->_delete($filter, 0);
return new DeleteResult($wr); return new DeleteResult($wr);
} }
/** /**
* Deletes a document matching the $filter criteria. * Deletes a document matching the $filter criteria.
* NOTE: Will delete ALL documents matching $filter * NOTE: Will delete at most ONE document matching $filter
* *
* @see http://docs.mongodb.org/manual/reference/command/delete/ * @see http://docs.mongodb.org/manual/reference/command/delete/
* *
* @param array $filter The $filter criteria to delete * @param array $filter The $filter criteria to delete
* @return DeleteResult * @return DeleteResult
*/ */
public function deleteMany(array $filter) public function deleteOne(array $filter)
{ {
$wr = $this->_delete($filter, 0); $wr = $this->_delete($filter);
return new DeleteResult($wr); return new DeleteResult($wr);
} }
/** /**
* Internal helper for replacing/updating one/many documents * Finds the distinct values for a specified field across the collection
* @internal *
* @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
*/ */
protected function _update($filter, $update, $options) public function distinct($fieldName, array $filter = array(), array $options = array())
{ {
$options = array_merge($this->getWriteOptions(), $options); $options = array_merge($this->getDistinctOptions(), $options);
$cmd = array(
"distinct" => $this->collname,
"key" => $fieldName,
"query" => $filter,
) + $options;
$bulk = new BulkWrite($options["ordered"]); $doc = $this->_runCommand($this->dbname, $cmd)->toArray();
$bulk->update($filter, $update, $options); if ($doc["ok"]) {
return $this->manager->executeBulkWrite($this->ns, $bulk, $this->wc); return $doc["values"];
}
throw $this->_generateCommandException($doc);
} }
/** /**
* Replace one document * Drop this collection.
* *
* @see http://docs.mongodb.org/manual/reference/command/update/ * @return Result
* @see Collection::getWriteOptions() for supported $options */
public function drop()
{
// TODO
}
/**
* Drop a single index in the collection.
* *
* @param array $filter The document to be replaced * @see http://docs.mongodb.org/manual/reference/command/dropIndexes/
* @param array $update The document to replace with * @see http://docs.mongodb.org/manual/reference/method/db.collection.dropIndex/
* @param array $options Additional options * @param string $indexName
* @return UpdateResult * @return Result
* @throws InvalidArgumentException if "*" is specified
*/ */
public function replaceOne(array $filter, array $update, array $options = array()) public function dropIndex($indexName)
{ {
if (key($update)[0] == '$') { // TODO
throw new \InvalidArgumentException("First key in \$update must NOT be a \$operator");
} }
$wr = $this->_update($filter, $update, $options);
return new UpdateResult($wr); /**
* 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
} }
/** /**
* Update one document * Performs a find (query) on the collection
* NOTE: Will update at most ONE document matching $filter
* *
* @see http://docs.mongodb.org/manual/reference/command/update/ * @see http://docs.mongodb.org/manual/core/read-operations-introduction/
* @see Collection::getWriteOptions() for supported $options * @see Collection::getFindOptions() for supported $options
* *
* @param array $filter The document to be replaced * @param array $filter The find query to execute
* @param array $update An array of update operators to apply to the document
* @param array $options Additional options * @param array $options Additional options
* @return UpdateResult * @return Result
*/ */
public function updateOne(array $filter, array $update, array $options = array()) public function find(array $filter = array(), array $options = array())
{ {
if (key($update)[0] != '$') { $options = array_merge($this->getFindOptions(), $options);
throw new \InvalidArgumentException("First key in \$update must be a \$operator");
}
$wr = $this->_update($filter, $update, $options);
return new UpdateResult($wr); $query = $this->_buildQuery($filter, $options);
$cursor = $this->manager->executeQuery($this->ns, $query, $this->rp);
return $cursor;
} }
/** /**
* Update one document * Performs a find (query) on the collection, returning at most one result
* NOTE: Will update ALL documents matching $filter
* *
* @see http://docs.mongodb.org/manual/reference/command/update/ * @see http://docs.mongodb.org/manual/core/read-operations-introduction/
* @see Collection::getWriteOptions() for supported $options * @see Collection::getFindOptions() for supported $options
* *
* @param array $filter The document to be replaced * @param array $filter The find query to execute
* @param array $update An array of update operators to apply to the document
* @param array $options Additional options * @param array $options Additional options
* @return UpdateResult * @return array|false The matched document, or false on failure
*/ */
public function updateMany(array $filter, $update, array $options = array()) public function findOne(array $filter = array(), array $options = array())
{ {
$wr = $this->_update($filter, $update, $options + array("limit" => 0)); $options = array_merge($this->getFindOptions(), array("limit" => 1), $options);
return new UpdateResult($wr); $query = $this->_buildQuery($filter, $options);
$cursor = $this->manager->executeQuery($this->ns, $query, $this->rp);
$array = iterator_to_array($cursor);
if ($array) {
return $array[0];
}
return false;
} }
/** /**
* Counts all documents matching $filter * Finds a single document and deletes it, returning the original.
* If no $filter provided, returns the numbers of documents in the collection
* *
* @see http://docs.mongodb.org/manual/reference/command/count/ * @see http://docs.mongodb.org/manual/reference/command/findAndModify/
* @see Collection::getCountOptions() for supported $options * @see Collection::getFindOneAndDelete() for supported $options
* *
* @param array $filter The find query to execute * @param array $filter The $filter criteria to search for
* @param array $options Additional options * @param array $options Additional options
* @return integer * @return array The original document
*/ */
public function count(array $filter = array(), array $options = array()) public function findOneAndDelete(array $filter, array $options = array())
{ {
$options = array_merge($this->getFindOneAndDeleteOptions(), $options);
$options = $this->_massageFindAndModifyOptions($options);
$cmd = array( $cmd = array(
"count" => $this->collname, "findandmodify" => $this->collname,
"query" => $filter, "query" => $filter,
) + $options; ) + $options;
$doc = $this->_runCommand($this->dbname, $cmd)->toArray(); $doc = $this->_runCommand($this->dbname, $cmd)->toArray();
if ($doc["ok"]) { if ($doc["ok"]) {
return $doc["n"]; return $doc["value"];
}
throw $this->_generateCommandException($doc);
} }
/** throw $this->_generateCommandException($doc);
* Retrieves all count options with their default values.
*
* @return array of Collection::count() options
*/
public function getCountOptions()
{
return array(
/**
* The index to use.
*
* @see http://docs.mongodb.org/manual/reference/command/count/
*/
"hint" => "", // string or document
/**
* 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,
);
} }
/** /**
* Finds the distinct values for a specified field across the collection * 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 http://docs.mongodb.org/manual/reference/command/distinct/ * @see http://docs.mongodb.org/manual/reference/command/findAndModify/
* @see Collection::getDistinctOptions() for supported $options * @see Collection::getFindOneAndReplace() for supported $options
* *
* @param string $fieldName The fieldname to use * @param array $filter The $filter criteria to search for
* @param array $filter The find query to execute * @param array $replacement The document to replace with
* @param array $options Additional options * @param array $options Additional options
* @return integer * @return array
*/ */
public function distinct($fieldName, array $filter = array(), array $options = array()) public function findOneAndReplace(array $filter, array $replacement, array $options = array())
{ {
$options = array_merge($this->getDistinctOptions(), $options); 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( $cmd = array(
"distinct" => $this->collname, "findandmodify" => $this->collname,
"key" => $fieldName,
"query" => $filter, "query" => $filter,
) + $options; ) + $options;
$doc = $this->_runCommand($this->dbname, $cmd)->toArray(); $doc = $this->_runCommand($this->dbname, $cmd)->toArray();
if ($doc["ok"]) { if ($doc["ok"]) {
return $doc["values"]; return $doc["value"];
} }
throw $this->_generateCommandException($doc); throw $this->_generateCommandException($doc);
} }
/** /**
* Retrieves all distinct options with their default values. * 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);
* *
* @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.
* *
* @see http://docs.mongodb.org/manual/reference/command/distinct/ * @see http://docs.mongodb.org/manual/reference/command/findAndModify/
*/ * @see Collection::getFindOneAndUpdate() for supported $options
"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 $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 * @param array $options Additional options
* @return Iterator * @return array
*/ */
public function aggregate(array $pipeline, array $options = array()) public function findOneAndUpdate(array $filter, array $update, array $options = array())
{ {
$options = array_merge($this->getAggregateOptions(), $options); if (key($update)[0] != '$') {
$options = $this->_massageAggregateOptions($options); throw new \InvalidArgumentException("First key in \$update must be a \$operator");
}
$options = array_merge($this->getFindOneAndUpdateOptions(), $options);
$options = $this->_massageFindAndModifyOptions($options, $update);
$cmd = array( $cmd = array(
"aggregate" => $this->collname, "findandmodify" => $this->collname,
"pipeline" => $pipeline, "query" => $filter,
) + $options; ) + $options;
$result = $this->_runCommand($this->dbname, $cmd); $doc = $this->_runCommand($this->dbname, $cmd)->toArray();
$doc = $result->toArray();
if (isset($cmd["cursor"]) && $cmd["cursor"]) {
return $result;
} else {
if ($doc["ok"]) { if ($doc["ok"]) {
return new \ArrayIterator($doc["result"]); return $doc["value"];
}
} }
throw $this->_generateCommandException($doc); throw $this->_generateCommandException($doc);
...@@ -763,44 +563,90 @@ class Collection ...@@ -763,44 +563,90 @@ class Collection
} }
/** /**
* Internal helper for massaging aggregate options * Retrieves all Bulk Write options with their default values.
* @internal *
* @return array of available Bulk Write options
*/ */
protected function _massageAggregateOptions($options) public function getBulkOptions()
{ {
if ($options["useCursor"]) { return array(
$options["cursor"] = array("batchSize" => $options["batchSize"]); "ordered" => false,
);
} }
unset($options["useCursor"], $options["batchSize"]);
return $options; /**
* Returns the CollectionName this object operates on
*
* @return string
*/
public function getCollectionName()
{
return $this->collname;
} }
/** /**
* Finds a single document and deletes it, returning the original. * Retrieves all count options with their default values.
*
* @see http://docs.mongodb.org/manual/reference/command/findAndModify/
* @see Collection::getFindOneAndDelete() for supported $options
* *
* @param array $filter The $filter criteria to search for * @return array of Collection::count() options
* @param array $options Additional options
* @return array The original document
*/ */
public function findOneAndDelete(array $filter, array $options = array()) public function getCountOptions()
{ {
$options = array_merge($this->getFindOneAndDeleteOptions(), $options); return array(
$options = $this->_massageFindAndModifyOptions($options); /**
$cmd = array( * The index to use.
"findandmodify" => $this->collname, *
"query" => $filter, * @see http://docs.mongodb.org/manual/reference/command/count/
) + $options; */
"hint" => "", // string or document
$doc = $this->_runCommand($this->dbname, $cmd)->toArray(); /**
if ($doc["ok"]) { * The maximum number of documents to count.
return $doc["value"]; *
* @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,
);
} }
throw $this->_generateCommandException($doc); /**
* Returns the DatabaseName this object operates on
*
* @return string
*/
public function getDatabaseName()
{
return $this->dbname;
}
/**
* 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.
*
* @see http://docs.mongodb.org/manual/reference/command/distinct/
*/
"maxTimeMS" => 0,
);
} }
/** /**
...@@ -835,42 +681,6 @@ class Collection ...@@ -835,42 +681,6 @@ class Collection
); );
} }
/**
* 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 http://docs.mongodb.org/manual/reference/command/findAndModify/
* @see Collection::getFindOneAndReplace() 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
*/
public function findOneAndReplace(array $filter, array $replacement, 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"];
}
throw $this->_generateCommandException($doc);
}
/** /**
* Retrieves all findOneAndReplace options with their default values. * Retrieves all findOneAndReplace options with their default values.
* *
...@@ -919,43 +729,6 @@ class Collection ...@@ -919,43 +729,6 @@ class Collection
); );
} }
/**
* 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/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 array
*/
public function findOneAndUpdate(array $filter, array $update, array $options = array())
{
if (key($update)[0] != '$') {
throw new \InvalidArgumentException("First key in \$update must be a \$operator");
}
$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);
}
/** /**
* Retrieves all findOneAndUpdate options with their default values. * Retrieves all findOneAndUpdate options with their default values.
* *
...@@ -1004,67 +777,352 @@ class Collection ...@@ -1004,67 +777,352 @@ class Collection
} }
/** /**
* Internal helper for massaging findandmodify options * Retrieves all find options with their default values.
* @internal *
* @return array of Collection::find() options
*/ */
final protected function _massageFindAndModifyOptions($options, $update = array()) public function getFindOptions()
{ {
$ret = array( return 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;
}
return $ret;
}
/** /**
* Internal helper for throwing an exception with error message * Get partial results from a mongos if some shards are down (instead of throwing an error).
* @internal *
* @see http://docs.mongodb.org/meta-driver/latest/legacy/mongodb-wire-protocol/#op-query
*/ */
final protected function _generateCommandException($doc) "allowPartialResults" => false,
{
if ($doc["errmsg"]) {
return new Exception($doc["errmsg"]);
}
var_dump($doc);
return new \Exception("FIXME: Unknown error");
}
/** /**
* Internal helper for running a command * The number of documents to return per batch.
* @internal *
* @see http://docs.mongodb.org/manual/reference/method/cursor.batchSize/
*/ */
final protected function _runCommand($dbname, array $cmd, ReadPreference $rp = null) "batchSize" => 101,
{
//var_dump(\BSON\toJSON(\BSON\fromArray($cmd)));
$command = new Command($cmd);
return $this->manager->executeCommand($dbname, $command, $rp);
}
/** /**
* Returns the CollectionName this object operates on * Attaches a comment to the query. If $comment also exists
* in the modifiers document, the comment field overwrites $comment.
* *
* @return string * @see http://docs.mongodb.org/manual/reference/operator/meta/comment/
*/ */
public function getCollectionName() "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(),
);
}
/**
* Retrieves all Write options with their default values.
*
* @return array of available Write options
*/
public function getWriteOptions()
{ {
return $this->collname; return array(
"ordered" => false,
"upsert" => false,
"limit" => 1,
);
} }
/** /**
* Returns the DatabaseName this object operates on * Inserts the provided documents
* *
* @return string * @see http://docs.mongodb.org/manual/reference/command/insert/
*
* @param array $documents The documents to insert
* @return InsertManyResult
*/ */
public function getDatabaseName() public function insertMany(array $documents)
{ {
return $this->dbname; $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;
}
}
$writeResult = $this->manager->executeBulkWrite($this->ns, $bulk, $this->wc);
return new InsertManyResult($writeResult, $insertedIds);
}
/**
* 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);
}
/**
* 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
*
* @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
*/
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);
}
/**
* 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
*
* @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 updateMany(array $filter, $update, array $options = array())
{
$wr = $this->_update($filter, $update, $options + array("limit" => 0));
return new UpdateResult($wr);
}
/**
* 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
*
* @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 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);
}
/**
* 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)
{
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 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);
}
/**
* Internal helper for throwing an exception with error message
* @internal
*/
final protected function _generateCommandException($doc)
{
if ($doc["errmsg"]) {
return new Exception($doc["errmsg"]);
}
var_dump($doc);
return new \Exception("FIXME: Unknown error");
}
/**
* Internal helper for massaging aggregate options
* @internal
*/
protected function _massageAggregateOptions($options)
{
if ($options["useCursor"]) {
$options["cursor"] = array("batchSize" => $options["batchSize"]);
}
unset($options["useCursor"], $options["batchSize"]);
return $options;
}
/**
* 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;
}
/**
* Internal helper for running a command
* @internal
*/
final protected function _runCommand($dbname, array $cmd, ReadPreference $rp = null)
{
//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);
} }
} }
...@@ -2,8 +2,9 @@ ...@@ -2,8 +2,9 @@
namespace MongoDB; namespace MongoDB;
use MongoDB\Driver\Manager;
use MongoDB\Collection; use MongoDB\Collection;
use MongoDB\Driver\Manager;
use MongoDB\Driver\Result;
class Database class Database
{ {
...@@ -32,6 +33,53 @@ class Database ...@@ -32,6 +33,53 @@ class Database
$this->rp = $rp; $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
}
/**
* Drop this database.
*
* @return Result
*/
public function drop()
{
// TODO
}
/**
* Drop a collection within this database.
*
* @param string $collectionName
* @return Result
*/
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 * Select a specific collection in this database
* *
......
...@@ -6,7 +6,7 @@ use MongoDB\Driver\Manager; ...@@ -6,7 +6,7 @@ use MongoDB\Driver\Manager;
class CollectionTest extends PHPUnit_Framework_TestCase { class CollectionTest extends PHPUnit_Framework_TestCase {
function setUp() { function setUp() {
require __DIR__ . "/" . "utils.inc"; require_once __DIR__ . "/" . "utils.inc";
$this->faker = Faker\Factory::create(); $this->faker = Faker\Factory::create();
$this->faker->seed(1234); $this->faker->seed(1234);
...@@ -44,5 +44,28 @@ class CollectionTest extends PHPUnit_Framework_TestCase { ...@@ -44,5 +44,28 @@ class CollectionTest extends PHPUnit_Framework_TestCase {
} }
$this->assertEquals(0, $n); $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