Skip to content

Commit

Permalink
Merge pull request #32 from arthurdarcet/bulk
Browse files Browse the repository at this point in the history
Bulk operations
  • Loading branch information
julien-c committed Dec 4, 2014
2 parents 703dd00 + 3d7f83d commit e0c6ab7
Show file tree
Hide file tree
Showing 5 changed files with 258 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ matrix:

services: mongodb

before_install:
- sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10
- echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list
- sudo apt-get update
- sudo apt-get install mongodb-org-server

before_script:
- echo "extension = mongo.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
- composer install --dev

before_script:
- until nc -z localhost 27017; do echo Waiting for MongoDB; sleep 1; done
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,33 @@ public function index()
}
```

#### Bulk operations

In order to make bulk operations less painful, Mongovel implements the [standard bulk interface](http://docs.mongodb.org/manual/reference/method/js-bulk/):
```php
$bulk = Book::initializeOrderedBulk();

$bulk->insert(array('author' => 'Me', 'title' => 'My life'));

$bulk->find(array('author' => 'Me'))
->update(array('$push' => array('reviews' => "Awesome!")));

$bulk->find(array('title' => 'My life'))
->updateOne(array('$push' => array('reviews' => "Incredible!")));

$bulk->find(array('title' => 'My life, II'))
->upsert()
->update(array('$push' => array('reviews' => "Can't wait!")));

$bulk->find(array('author' => 'Not me'))
->remove();

$bulk->find(array('title' => 'My life, II'))
->removeOne();

$bulk->execute();
```

### How to install

Add `julien-c/mongovel` as a requirement to composer.json, then run `composer update`.
Expand Down
89 changes: 89 additions & 0 deletions src/Mongovel/Bulk.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php
namespace Mongovel;

use MongoCollection;
use MongoInsertBatch;
use MongoUpdateBatch;
use MongoDeleteBatch;


class Bulk
{
public function __construct(MongoCollection $collection, array $options = array())
{
$this->bulks = array(
// operation => [hasOne, phpBatch]
'insert' => [false, new MongoInsertBatch($collection, $options)],
'update' => [false, new MongoUpdateBatch($collection, $options)],
'remove' => [false, new MongoDeleteBatch($collection, $options)],
);
}

public function find($query)
{
return new BulkOperation($this, $query);
}

public function insert($doc)
{
return $this->_add('insert', $doc);
}

public function _add($operation, $arg)
{
$this->bulks[$operation][0] = true;
$this->bulks[$operation][1]->add($arg);
}

public function execute()
{
foreach ($this->bulks as $b) {
if ($b[0]) {
$b[1]->execute();
}
}
}
}

class BulkOperation
{
public function __construct(Bulk $bulk, array $query)
{
$this->bulk = $bulk;
$this->call = array('q' => $query);
}

public function remove($limit = 0)
{
if (!isset($this->call['limit'])) {
$this->call['limit'] = $limit;
}
return $this->bulk->_add('remove', $this->call);
}

public function removeOne()
{
return $this->remove(1);
}

public function update($u)
{
$this->call['multi'] = true;
$this->call['u'] = $u;
return $this->bulk->_add('update', $this->call);
}

public function updateOne($u)
{
$this->call['multi'] = false;
$this->call['u'] = $u;
return $this->bulk->_add('update', $this->call);
}

public function upsert()
{
$this->call['upsert'] = true;
return $this;
}

}
10 changes: 10 additions & 0 deletions src/Mongovel/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,16 @@ public static function textSearch($q, $filter = array())

return new Collection($items);
}

public static function initializeUnorderedBulkOp()
{
return new Bulk(static::getCollection(), ['ordered' => false]);
}

public static function initializeOrderedBulkOp()
{
return new Bulk(static::getCollection(), ['ordered' => true]);
}

////////////////////////////////////////////////////////////////////
/////////////////////////// SERIALIZATION //////////////////////////
Expand Down
123 changes: 123 additions & 0 deletions tests/BulkTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<?php

require_once '_start.php';

class BulkTest extends MongovelTests
{

public function testCanBulkInsert()
{
$bulk = Book::initializeUnorderedBulkOp();

$bulk->insert(array('key' => "A"));
$bulk->insert(array('key' => "B"));
$bulk->execute();

$this->assert(array("A", "B"));
}

public function testCanBulkUpdateMultiple()
{
$bulk = Book::initializeUnorderedBulkOp();
static::insertFixture();

$bulk->find(array('key' => "B"))
->update(array('$set' => array('key' => "C")));
$bulk->execute();

$this->assert(array("A", "C", "C"));
}

public function testCanBulkUpdateOne()
{
$bulk = Book::initializeUnorderedBulkOp();
static::insertFixture();

$bulk->find(array('key' => "B"))
->updateOne(array('$set' => array('key' => "C")));
$bulk->execute();

$this->assert(array("A", "C", "B"));
}

public function testCanBulkUpsert()
{
$bulk = Book::initializeUnorderedBulkOp();
static::insertFixture();

$bulk->find(array('missing' => "attr"))
->upsert()
->updateOne(array('$set' => array('key' => "C")));
$bulk->execute();

$this->assert(array("A", "B", "B", "C"));
}

public function testCanBulkRemove()
{
$bulk = Book::initializeUnorderedBulkOp();
static::insertFixture();

$bulk->find(array('key' => "B"))
->remove();
$bulk->execute();

$this->assert(array("A"));
}

public function testCanBulkRemoveOne()
{
$bulk = Book::initializeUnorderedBulkOp();
static::insertFixture();

$bulk->find(array('key' => "B"))
->removeOne();
$bulk->execute();

$this->assert(array("A", "B"));
}

public function testOrderedBulk()
{
$bulk = Book::initializeOrderedBulkOp();
static::insertFixture();

$bulk->find(array('key' => "B"))
->update(array('$set' => array('key' => "C")));
$bulk->find(array('key' => "A"))
->update(array('$set' => array('key' => "B")));
$bulk->execute();

$this->assert(array("B", "C", "C"));
}

public function assert($vals)
{
$res = array_pluck(iterator_to_array(Book::find()), 'key');
$this->assertEquals($vals, array_values($res));
}

//////////////////////////////////////////////////////////
//////////////////////// Fixtures ////////////////////////
//////////////////////////////////////////////////////////

public static function insertFixture()
{
self::$db->db()->books->batchInsert(array(
array('key' => "A"),
array('key' => "B"),
array('key' => "B"),
));
}


//////////////////////////////////////////////////////////
//////// Drop database before and after each test ////////
//////////////////////////////////////////////////////////

protected function setUp()
{
self::$db->db()->drop();
}

}

0 comments on commit e0c6ab7

Please sign in to comment.