Commit 92ee3c51 authored by Jeremy Mikola's avatar Jeremy Mikola

PHPLIB-45: Collection enumeration methods

parent da4fdf19
...@@ -5,9 +5,14 @@ namespace MongoDB; ...@@ -5,9 +5,14 @@ namespace MongoDB;
use MongoDB\Collection; use MongoDB\Collection;
use MongoDB\Driver\Command; use MongoDB\Driver\Command;
use MongoDB\Driver\Manager; use MongoDB\Driver\Manager;
use MongoDB\Driver\Query;
use MongoDB\Driver\ReadPreference; use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Result; use MongoDB\Driver\Result;
use MongoDB\Driver\WriteConcern; use MongoDB\Driver\WriteConcern;
use MongoDB\Model\CollectionInfoIterator;
use MongoDB\Model\CollectionInfoCommandIterator;
use MongoDB\Model\CollectionInfoLegacyIterator;
use InvalidArgumentException;
class Database class Database
{ {
...@@ -84,11 +89,12 @@ class Database ...@@ -84,11 +89,12 @@ class Database
* *
* @see http://docs.mongodb.org/manual/reference/command/listCollections/ * @see http://docs.mongodb.org/manual/reference/command/listCollections/
* @param array $options * @param array $options
* @return Result * @return CollectionInfoIterator
*/ */
public function listCollections(array $options = array()) public function listCollections(array $options = array())
{ {
// TODO // TODO: Determine if command or legacy method should be used
return $this->listCollectionsCommand($options);
} }
/** /**
...@@ -110,4 +116,58 @@ class Database ...@@ -110,4 +116,58 @@ class Database
return new Collection($this->manager, $namespace, $writeConcern, $readPreference); return new Collection($this->manager, $namespace, $writeConcern, $readPreference);
} }
/**
* Returns information for all collections in this database using the
* listCollections command.
*
* @param array $options
* @return CollectionInfoCommandIterator
*/
private function listCollectionsCommand(array $options = array())
{
$command = new Command(array('listCollections' => 1) + $options);
// TODO: Relax RP if connected to a secondary node in standalone mode
$readPreference = new ReadPreference(ReadPreference::RP_PRIMARY);
$result = $this->manager->executeCommand($this->databaseName, $command, $readPreference);
return new CollectionInfoCommandIterator($result);
}
/**
* Returns information for all collections in this database by querying
* the "system.namespaces" collection (MongoDB <2.8).
*
* @param array $options
* @return CollectionInfoLegacyIterator
* @throws InvalidArgumentException if the filter option is neither an array
* nor object, or if filter.name is not a
* string.
*/
private function listCollectionsLegacy(array $options = array())
{
$filter = array_key_exists('filter', $options) ? $options['filter'] : array();
if ( ! is_array($filter) && ! is_object($filter)) {
throw new InvalidArgumentException(sprintf('Expected filter to be array or object, %s given', gettype($filter)));
}
if (array_key_exists('name', $filter)) {
if ( ! is_string($filter['name'])) {
throw new InvalidArgumentException(sprintf('Filter "name" must be a string for MongoDB <2.8, %s given', gettype($filter['name'])));
}
$filter['name'] = $this->databaseName . '.' . $filter['name'];
}
$namespace = $this->databaseName . '.system.namespaces';
$query = new Query($filter);
// TODO: Relax RP if connected to a secondary node in standalone mode
$readPreference = new ReadPreference(ReadPreference::RP_PRIMARY);
$result = $this->manager->executeQuery($namespace, $query, $readPreference);
return new CollectionInfoLegacyIterator($result);
}
} }
<?php
namespace MongoDB\Model;
class CollectionInfo
{
private $name;
private $options;
/**
* Constructor.
*
* @param array $info Collection info
*/
public function __construct(array $info)
{
$this->name = (string) $info['name'];
$this->options = isset($info['options']) ? (array) $info['options'] : array();
}
/**
* Return the collection name.
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Return the collection options.
*
* @return array
*/
public function getOptions()
{
return $this->options;
}
/**
* Return whether the collection is a capped collection.
*
* @return boolean
*/
public function isCapped()
{
return isset($this->options['capped']) ? (boolean) $this->options['capped'] : false;
}
/**
* Return the maximum number of documents to keep in the capped collection.
*
* @return integer|null
*/
public function getCappedMax()
{
return isset($this->options['max']) ? (integer) $this->options['max'] : null;
}
/**
* Return the maximum size (in bytes) of the capped collection.
*
* @return integer|null
*/
public function getCappedSize()
{
return isset($this->options['size']) ? (integer) $this->options['size'] : null;
}
}
<?php
namespace MongoDB\Model;
use IteratorIterator;
class CollectionInfoCommandIterator extends IteratorIterator implements CollectionInfoIterator
{
/**
* Return the current element as a CollectionInfo instance.
*
* @return CollectionInfo
*/
public function current()
{
return new CollectionInfo(parent::current());
}
}
<?php
namespace MongoDB\Model;
use Iterator;
interface CollectionInfoIterator extends Iterator
{
/**
* Return the current element as a CollectionInfo instance.
*
* @return CollectionInfo
*/
public function current();
}
<?php
namespace MongoDB\Model;
use FilterIterator;
class CollectionInfoLegacyIterator extends FilterIterator implements CollectionInfoIterator
{
/**
* Return the current element as a CollectionInfo instance.
*
* @return CollectionInfo
*/
public function current()
{
$info = parent::current();
// Trim the database prefix up to and including the first dot
$firstDot = strpos($info['name'], '.');
if ($firstDot !== false) {
$info['name'] = (string) substr($info['name'], $firstDot + 1);
}
return new CollectionInfo($info);
}
/**
* Filter out internal or invalid collections.
*
* @see http://php.net/manual/en/filteriterator.accept.php
* @return boolean
*/
public function accept()
{
$info = parent::current();
if ( ! isset($info['name']) || ! is_string($info['name'])) {
return false;
}
// Reject names with "$" characters (e.g. indexes, oplog)
if (strpos($info['name'], '$') !== false) {
return false;
}
$firstDot = strpos($info['name'], '.');
/* Legacy collection names are a namespace and should be prefixed with
* the database name and a dot. Reject values that omit this prefix or
* are empty beyond it.
*/
if ($firstDot === false || $firstDot + 1 == strlen($info['name'])) {
return false;
}
return true;
}
}
...@@ -39,4 +39,24 @@ class DatabaseFunctionalTest extends FunctionalTestCase ...@@ -39,4 +39,24 @@ class DatabaseFunctionalTest extends FunctionalTestCase
$this->assertCommandSucceeded($commandResult); $this->assertCommandSucceeded($commandResult);
$this->assertCollectionCount($this->getNamespace(), 0); $this->assertCollectionCount($this->getNamespace(), 0);
} }
public function testListCollections()
{
$writeResult = $this->manager->executeInsert($this->getNamespace(), array('x' => 1));
$this->assertEquals(1, $writeResult->getInsertedCount());
$collections = $this->database->listCollections();
$this->assertInstanceOf('MongoDB\Model\CollectionInfoIterator', $collections);
$foundCollection = null;
foreach ($collections as $collection) {
if ($collection->getName() === $this->getCollectionName()) {
$foundCollection = $collection;
break;
}
}
$this->assertNotNull($foundCollection, 'Found test collection in list of collection');
}
} }
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