Ver Fonte

Add missing methods to MongoCollection #2

Olivier Lechevalier há 10 anos atrás
pai
commit
ee7998cb9d

+ 146 - 13
lib/Mongo/MongoCollection.php

@@ -226,7 +226,12 @@ class MongoCollection
      */
     public function validate($scan_data = FALSE)
     {
-        $this->notImplemented();
+        $command = [
+            'validate' => $this->name,
+            'full'     => $scan_data,
+        ];
+
+        return $this->db->command($command, [], $hash);
     }
 
     /**
@@ -381,9 +386,32 @@ class MongoCollection
      * @param array $options An array of options to apply, such as remove the match document from the DB and return it.
      * @return array Returns the original document, or the modified document when new is set.
      */
-    public function findAndModify(array $query, array $update = NULL, array $fields = NULL, array $options = NULL)
+    public function findAndModify(array $query, array $update = NULL, array $fields = NULL, array $options = [])
     {
-
+        $query = TypeConverter::convertLegacyArrayToObject($query);
+        if (isset($options['remove'])) {
+            unset($options['remove']);
+            $document = $this->collection->findOneAndDelete($query, $options);
+        } else {
+            if (is_array($update)) {
+                $update = TypeConverter::convertLegacyArrayToObject($update);
+            }
+            if (is_array($fields)) {
+                $fields = TypeConverter::convertLegacyArrayToObject($fields);
+            }
+            if (isset($options['new'])) {
+                $options['returnDocument'] = \MongoDB\Operation\FindOneAndUpdate::RETURN_DOCUMENT_AFTER;
+                unset($options['new']);
+            }
+            if ($fields) {
+                $options['projection'] = $fields;
+            }
+            $document = $this->collection->findOneAndUpdate($query, $update, $options);
+        }
+        if ($document) {
+            $document = TypeConverter::convertObjectToLegacyArray($document);
+        }
+        return $document;
     }
 
     /**
@@ -410,7 +438,10 @@ class MongoCollection
      * @param array $options [optional] This parameter is an associative array of the form array("optionname" => <boolean>, ...).
      * @return array Returns the database response.
      */
-    public function createIndex(array $keys, array $options = array()) {}
+    public function createIndex(array $keys, array $options = array())
+    {
+        return $this->collection->createIndex($keys, $options);
+    }
 
     /**
      * @deprecated Use MongoCollection::createIndex() instead.
@@ -420,7 +451,11 @@ class MongoCollection
      * @param array $options [optional] This parameter is an associative array of the form array("optionname" => <boolean>, ...).
      * @return boolean always true
      */
-    public function ensureIndex(array $keys, array $options = array()) {}
+    public function ensureIndex(array $keys, array $options = array())
+    {
+        $this->createIndex($keys, $options);
+        return true;
+    }
 
     /**
      * Deletes an index from this collection
@@ -428,21 +463,40 @@ class MongoCollection
      * @param string|array $keys Field or fields from which to delete the index.
      * @return array Returns the database response.
      */
-    public function deleteIndex($keys) {}
+    public function deleteIndex($keys)
+    {
+        if (is_string($keys)) {
+            $indexName = $keys;
+        } elseif (is_array($keys)) {
+            $indexName = self::toIndexString($keys);
+        } else {
+            throw new \InvalidArgumentException();
+        }
+        return $this->collection->dropIndex($indexName);
+    }
 
     /**
      * Delete all indexes for this collection
      * @link http://www.php.net/manual/en/mongocollection.deleteindexes.php
      * @return array Returns the database response.
      */
-    public function deleteIndexes() {}
+    public function deleteIndexes()
+    {
+        return $this->collection->dropIndexes();
+    }
 
     /**
      * Returns an array of index names for this collection
      * @link http://www.php.net/manual/en/mongocollection.getindexinfo.php
      * @return array Returns a list of index names.
      */
-    public function getIndexInfo() {}
+    public function getIndexInfo()
+    {
+        $convertIndex = function($indexInfo) {
+            return $indexInfo->__debugInfo();
+        };
+        return array_map($convertIndex, iterator_to_array($this->collection->listIndexes()));
+    }
 
     /**
      * Counts the number of documents in this collection
@@ -478,7 +532,22 @@ class MongoCollection
      * @return array|boolean If w was set, returns an array containing the status of the save.
      * Otherwise, returns a boolean representing if the array was not empty (an empty array will not be inserted).
      */
-    public function save($a, array $options = array()) {}
+    public function save($a, array $options = array())
+    {
+        if (is_object($a)) {
+            $a = (array)$a;
+        }
+        if ( ! array_key_exists('_id', $a)) {
+            $id = new \MongoId();
+        } else {
+            $id = $a['_id'];
+            unset($a['_id']);
+        }
+        $filter = ['_id' => $id];
+        $filter = TypeConverter::convertLegacyArrayToObject($filter);
+        $a = TypeConverter::convertLegacyArrayToObject($a);
+        return $this->collection->updateOne($filter, ['$set' => $a], ['upsert' => true]);
+    }
 
     /**
      * Creates a database reference
@@ -486,7 +555,10 @@ class MongoCollection
      * @param array $a Object to which to create a reference.
      * @return array Returns a database reference array.
      */
-    public function createDBRef(array $a) {}
+    public function createDBRef(array $a)
+    {
+        return \MongoDBRef::create($this->name, $a['_id']);
+    }
 
     /**
      * Fetches the document pointed to by a database reference
@@ -494,14 +566,24 @@ class MongoCollection
      * @param array $ref A database reference.
      * @return array Returns the database document pointed to by the reference.
      */
-    public function getDBRef(array $ref) {}
+    public function getDBRef(array $ref)
+    {
+        return \MongoDBRef::get($this->db, $ref);
+    }
 
     /**
      * @param  mixed $keys
      * @static
      * @return string
      */
-    protected static function toIndexString($keys) {}
+    protected static function toIndexString($keys)
+    {
+        $result = '';
+        foreach ($keys as $name => $direction) {
+            $result .= sprintf('%s_%d', $name, $direction);
+        }
+        return $result;
+    }
 
     /**
      * Performs an operation similar to SQL's GROUP BY command
@@ -512,7 +594,58 @@ class MongoCollection
      * @param array $condition An condition that must be true for a row to be considered.
      * @return array
      */
-    public function group($keys, array $initial, MongoCode $reduce, array $condition = array()) {}
+    public function group($keys, array $initial, $reduce, array $condition = [])
+    {
+        if (is_string($reduce)) {
+            $reduce = new MongoCode($reduce);
+        }
+        if ( ! $reduce instanceof MongoCode) {
+            throw new \InvalidArgumentExcption('reduce parameter should be a string or MongoCode instance.');
+        }
+        $command = [
+            'group' => [
+                'ns'      => $this->name,
+                '$reduce' => (string)$reduce,
+                'initial' => $initial,
+                'cond'    => $condition,
+            ],
+        ];
+
+        if ($keys instanceof MongoCode) {
+            $command['group']['$keyf'] = (string)$keys;
+        } else {
+            $command['group']['key'] = $keys;
+        }
+        if (array_key_exists('condition', $condition)) {
+            $command['group']['cond'] = $condition['condition'];
+        }
+        if (array_key_exists('finalize', $condition)) {
+            if ($condition['finalize'] instanceof MongoCode) {
+                $condition['finalize'] = (string)$condition['finalize'];
+            }
+            $command['group']['finalize'] = $condition['finalize'];
+        }
+
+        $result = $this->db->command($command, [], $hash);
+        unset($result['waitedMS']);
+        $result['ok'] = (int)$result['ok'];
+        if (count($result['retval'])) {
+            $result['retval'] = current($result['retval']);
+        }
+        return $result;
+    }
+
+    /**
+     * Returns an array of cursors to iterator over a full collection in parallel
+     *
+     * @link http://www.php.net/manual/en/mongocollection.parallelcollectionscan.php
+     * @param int $num_cursors The number of cursors to request from the server. Please note, that the server can return less cursors than you requested.
+     * @return MongoCommandCursor[]
+     */
+    public function parallelCollectionScan($num_cursors)
+    {
+        $this->notImplemented();
+    }
 
     protected function notImplemented()
     {

+ 258 - 1
tests/Alcaeus/MongoDbAdapter/MongoCollectionTest.php

@@ -55,7 +55,7 @@ class MongoCollectionTest extends TestCase
         $this->prepareData();
 
         $document = $this->getCollection()->findOne(['foo' => 'foo'], ['_id' => false]);
-        $this->assertEquals(['foo' => 'foo'], $document);
+        $this->assertSame(['foo' => 'foo'], $document);
     }
 
     public function testDistinct()
@@ -201,6 +201,263 @@ class MongoCollectionTest extends TestCase
         $this->assertSame(['w' => 'majority', 'wtimeout' => 100], $collection->getWriteConcern());
     }
 
+    public function testSaveInsert()
+    {
+        $id = '54203e08d51d4a1f868b456e';
+        $collection = $this->getCollection();
+
+        $collection->save(['_id' => new \MongoId($id), 'foo' => 'bar']);
+        $newCollection = $this->getCheckDatabase()->selectCollection('test');
+        $this->assertSame(1, $newCollection->count());
+        $object = $newCollection->findOne();
+
+        $this->assertNotNull($object);
+        $this->assertAttributeInstanceOf('MongoDB\BSON\ObjectID', '_id', $object);
+        $this->assertSame($id, (string) $object->_id);
+        $this->assertObjectHasAttribute('foo', $object);
+        $this->assertAttributeSame('bar', 'foo', $object);
+    }
+
+    public function testSaveUpdate()
+    {
+        $id = '54203e08d51d4a1f868b456e';
+        $collection = $this->getCollection();
+
+        $collection->insert(['_id' => new \MongoId($id), 'foo' => 'bar']);
+        $collection->save(['_id' => new \MongoId($id), 'foo' => 'foo']);
+
+        $newCollection = $this->getCheckDatabase()->selectCollection('test');
+        $this->assertSame(1, $newCollection->count());
+        $object = $newCollection->findOne();
+
+        $this->assertNotNull($object);
+        $this->assertAttributeInstanceOf('MongoDB\BSON\ObjectID', '_id', $object);
+        $this->assertSame($id, (string) $object->_id);
+        $this->assertObjectHasAttribute('foo', $object);
+        $this->assertAttributeSame('foo', 'foo', $object);
+    }
+
+    public function testGetDBRef()
+    {
+        $collection = $this->getCollection();
+
+        $collection->insert(['_id' => 1, 'foo' => 'bar']);
+
+        $document = $collection->getDBRef([
+            '$ref' => 'test',
+            '$id'  => 1,
+        ]);
+        $this->assertEquals(['_id' => 1, 'foo' => 'bar'], $document);
+    }
+
+    public function testCreateDBRef()
+    {
+        $collection = $this->getCollection();
+        $reference = $collection->createDBRef(['_id' => 'foo']);
+        $this->assertSame(
+            [
+                '$ref' => 'test',
+                '$id'  => 'foo',
+            ],
+            $reference
+        );
+    }
+
+    public function testCreateIndex()
+    {
+        $collection = $this->getCollection();
+        $collection->createIndex(['foo' => 1]);
+
+        $newCollection = $this->getCheckDatabase()->selectCollection('test');
+        $iterator = $newCollection->listIndexes();
+        $indexes = iterator_to_array($iterator);
+        $this->assertCount(2, $indexes);
+        $index = $indexes[1];
+        $this->assertSame(['foo' => 1], $index->getKey());
+        $this->assertSame('mongo-php-adapter.test', $index->getNamespace());
+    }
+
+    public function testEnsureIndex()
+    {
+        $collection = $this->getCollection();
+        $this->assertTrue($collection->ensureIndex(['bar' => 1], ['unique' => true]));
+
+        $newCollection = $this->getCheckDatabase()->selectCollection('test');
+        $indexes = iterator_to_array($newCollection->listIndexes());
+        $this->assertCount(2, $indexes);
+        $index = $indexes[1];
+        $this->assertSame(['bar' => 1], $index->getKey());
+        $this->assertTrue($index->isUnique());
+        $this->assertSame('mongo-php-adapter.test', $index->getNamespace());
+    }
+
+    public function testDeleteIndexUsingIndexName()
+    {
+        $collection = $this->getCollection();
+        $newCollection = $this->getCheckDatabase()->selectCollection('test');
+
+        $newCollection->createIndex(['bar' => 1], ['name' => 'bar']);
+        $collection->deleteIndex('bar');
+
+        $this->assertCount(1, iterator_to_array($newCollection->listIndexes()));
+    }
+
+    public function testdeleteindexusingkeys()
+    {
+        $collection = $this->getcollection();
+        $newCollection = $this->getCheckDatabase()->selectCollection('test');
+
+        $newCollection->createIndex(['bar' => 1]);
+        $collection->deleteIndex(['bar' => 1]);
+
+        $this->assertCount(1, iterator_to_array($newCollection->listIndexes()));
+    }
+
+    public function testDeleteIndexes()
+    {
+        $collection = $this->getcollection();
+        $newCollection = $this->getCheckDatabase()->selectCollection('test');
+
+        $newCollection->createIndex(['bar' => 1]);
+        $collection->deleteIndexes();
+
+        $this->assertCount(1, iterator_to_array($newCollection->listIndexes())); // ID index is present by default
+    }
+
+    public function testGetIndexInfo()
+    {
+        $this->prepareData();
+
+        $this->assertSame(
+            [
+                [
+                    'v'    => 1,
+                    'key'  => ['_id' => 1],
+                    'name' => '_id_',
+                    'ns'   => 'mongo-php-adapter.test',
+                ],
+            ],
+            $this->getcollection()->getIndexInfo()
+        );
+    }
+
+    public function testFindAndModifyUpdate()
+    {
+        $id = '54203e08d51d4a1f868b456e';
+        $collection = $this->getCollection();
+
+        $collection->insert(['_id' => new \MongoId($id), 'foo' => 'bar']);
+        $document = $collection->findAndModify(
+            ['_id' => new \MongoId($id)],
+            ['$set' => ['foo' => 'foo']]
+        );
+        $this->assertSame('bar', $document['foo']);
+
+        $newCollection = $this->getCheckDatabase()->selectCollection('test');
+        $this->assertSame(1, $newCollection->count());
+        $object = $newCollection->findOne();
+
+        $this->assertNotNull($object);
+        $this->assertAttributeSame('foo', 'foo', $object);
+    }
+
+    public function testFindAndModifyUpdateReturnNew()
+    {
+        $id = '54203e08d51d4a1f868b456e';
+        $collection = $this->getCollection();
+
+        $collection->insert(['_id' => new \MongoId($id), 'foo' => 'bar']);
+        $document = $collection->findAndModify(
+            ['_id' => new \MongoId($id)],
+            ['$set' => ['foo' => 'foo']],
+            null,
+            ['new' => true]
+        );
+        $this->assertSame('foo', $document['foo']);
+    }
+
+    public function testFindAndModifyWithFields()
+    {
+        $id = '54203e08d51d4a1f868b456e';
+        $collection = $this->getCollection();
+
+        $collection->insert([
+            '_id' => new \MongoId($id),
+            'foo' => 'bar',
+            'bar' => 'foo',
+        ]);
+        $document = $collection->findAndModify(
+            ['_id' => new \MongoId($id)],
+            ['$set' => ['foo' => 'foo']],
+            ['foo' => true]
+        );
+        $this->assertArrayNotHasKey('bar', $document);
+        $this->assertArrayHasKey('foo', $document);
+    }
+
+    public function testGroup()
+    {
+        $collection = $this->getCollection();
+
+        $collection->insert(['a' => 2]);
+        $collection->insert(['b' => 5]);
+        $collection->insert(['a' => 1]);
+        $keys = [];
+        $initial = ["count" => 0];
+        $reduce = "function (obj, prev) { prev.count++; }";
+        $condition = ['condition' => ["a" => [ '$gt' => 1]]];
+
+        $result = $collection->group($keys, $initial, $reduce, $condition);
+
+        $this->assertEquals(
+            [
+                'retval' => ['count' => 1],
+                'count'  => 1,
+                'keys'   => 1,
+                'ok'     => 1,
+            ],
+            $result
+        );
+    }
+
+    public function testFindAndModifyRemove()
+    {
+        $id = '54203e08d51d4a1f868b456e';
+        $collection = $this->getCollection();
+
+        $collection->insert(['_id' => new \MongoId($id), 'foo' => 'bar']);
+        $document = $collection->findAndModify(
+            ['_id' => new \MongoId($id)],
+            null,
+            null,
+            ['remove' => true]
+        );
+
+        $newCollection = $this->getCheckDatabase()->selectCollection('test');
+        $this->assertSame(0, $newCollection->count());
+    }
+
+    public function testValidate()
+    {
+        $collection = $this->getCollection();
+        $collection->insert(['foo' => 'bar']);
+        $result = $collection->validate();
+
+        $this->assertArraySubset(
+            [
+                'ns' => 'mongo-php-adapter.test',
+                'nrecords' => 1,
+                'nIndexes' => 1,
+                'keysPerIndex' => ['mongo-php-adapter.test.$_id_' => 1],
+                'valid' => true,
+                'errors' => [],
+                'warning' => 'Some checks omitted for speed. use {full:true} option to do more thorough scan.',
+                'ok'  => 1.0
+            ],
+            $result
+        );
+    }
+
     /**
      * @return \MongoCollection
      */