Skip to content
This repository has been archived by the owner on Nov 11, 2020. It is now read-only.

Commit

Permalink
[Query] Improve builder interface for group queries
Browse files Browse the repository at this point in the history
 * Use separate options array for group queries
 * Handle condition and finalize options for group queries
 * Allow reduce() and finalize() to be used for both map/reduce and group queries
  • Loading branch information
jmikola committed Jul 16, 2012
1 parent 59f3707 commit 6f41298
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 34 deletions.
50 changes: 40 additions & 10 deletions lib/Doctrine/MongoDB/Query/Builder.php
Expand Up @@ -65,7 +65,12 @@ class Builder
'sort' => array(),
'limit' => null,
'skip' => null,
'group' => array(),
'group' => array(
'keys' => null,
'initial' => null,
'reduce' => null,
'options' => array(),
),
'hints' => array(),
'immortal' => false,
'snapshot' => false,
Expand Down Expand Up @@ -277,17 +282,21 @@ public function remove()
/**
* Perform an operation similar to SQL's GROUP BY command
*
* @param $keys
* @param mixed $keys
* @param array $initial
* @param string|MongoCode $reduce
* @param array $options
* @return Builder
*/
public function group($keys, array $initial)
public function group($keys, array $initial, $reduce = null, array $options = array())
{
$this->query['type'] = Query::TYPE_GROUP;
$this->query['group'] = array(
'keys' => $keys,
'initial' => $initial
'initial' => $initial,
'reduce' => $reduce,
'options' => $options,
);
$this->query['type'] = Query::TYPE_GROUP;
return $this;
}

Expand Down Expand Up @@ -728,13 +737,23 @@ public function map($map)
*
* @param string|MongoCode $reduce
* @return Builder
* @throws BadMethodCallException if the query type is unsupported
*/
public function reduce($reduce)
{
$this->query['mapReduce']['reduce'] = $reduce;
if (isset($this->query['mapReduce']['map']) && isset($this->query['mapReduce']['reduce'])) {
$this->query['type'] = Query::TYPE_MAP_REDUCE;
switch ($this->query['type']) {
case Query::TYPE_MAP_REDUCE:
$this->query['mapReduce']['reduce'] = $reduce;
break;

case Query::TYPE_GROUP:
$this->query['group']['reduce'] = $reduce;
break;

default:
throw new \BadMethodCallException('mapReduce(), map() or group() must be called before reduce()');
}

return $this;
}

Expand All @@ -746,8 +765,19 @@ public function reduce($reduce)
*/
public function finalize($finalize)
{
$this->query['mapReduce']['options']['finalize'] = $finalize;
$this->query['type'] = Query::TYPE_MAP_REDUCE;
switch ($this->query['type']) {
case Query::TYPE_MAP_REDUCE:
$this->query['mapReduce']['options']['finalize'] = $finalize;
break;

case Query::TYPE_GROUP:
$this->query['group']['options']['finalize'] = $finalize;
break;

default:
throw new \BadMethodCallException('mapReduce(), map() or group() must be called before reduce()');
}

return $this;
}

Expand Down
8 changes: 7 additions & 1 deletion lib/Doctrine/MongoDB/Query/Query.php
Expand Up @@ -190,7 +190,13 @@ public function execute()
return $this->collection->remove($this->query['query'], $this->options);

case self::TYPE_GROUP:
return $this->collection->group($this->query['group']['keys'], $this->query['group']['initial'], $this->query['mapReduce']['reduce'], $this->query['query']);
if (!empty($this->query['query'])) {
$this->query['group']['options']['condition'] = $this->query['query'];
}

$options = array_merge($this->options, $this->query['group']['options']);

return $this->collection->group($this->query['group']['keys'], $this->query['group']['initial'], $this->query['group']['reduce'], $options);

case self::TYPE_MAP_REDUCE:
if (!isset($this->query['mapReduce']['out'])) {
Expand Down
78 changes: 55 additions & 23 deletions tests/Doctrine/MongoDB/Tests/Query/BuilderTest.php
Expand Up @@ -133,12 +133,49 @@ public function testFindAndUpdateQuery()
$this->assertNull($query->execute());
}

public function testGroupQuery()
public function testGroupQueryWithSingleMethod()
{
$keys = array();
$initial = array('count' => 0, 'sum' => 0);
$reduce = 'function(obj, prev) { prev.count++; prev.sum += obj.a; }';
$finalize = 'function(obj) { if (obj.count) { obj.avg = obj.sum / obj.count; } else { obj.avg = 0; } }';

$qb = $this->getTestQueryBuilder()
->group($keys, $initial, $reduce, array('finalize' => $finalize));

$expected = array(
'keys' => $keys,
'initial' => $initial,
'reduce' => $reduce,
'options' => array('finalize' => $finalize),
);

$this->assertEquals(Query::TYPE_GROUP, $qb->getType());
$this->assertEquals($expected, $qb->debug('group'));
$this->assertInstanceOf('Doctrine\MongoDB\ArrayIterator', $qb->getQuery()->execute());
}

public function testGroupQueryWithMultipleMethods()
{
$keys = array();
$initial = array('count' => 0, 'sum' => 0);
$reduce = 'function(obj, prev) { prev.count++; prev.sum += obj.a; }';
$finalize = 'function(obj) { if (obj.count) { obj.avg = obj.sum / obj.count; } else { obj.avg = 0; } }';

$qb = $this->getTestQueryBuilder()
->group(array(), array());
->group($keys, $initial)
->reduce($reduce)
->finalize($finalize);

$expected = array(
'keys' => $keys,
'initial' => $initial,
'reduce' => $reduce,
'options' => array('finalize' => $finalize),
);

$this->assertEquals(Query::TYPE_GROUP, $qb->getType());
$this->assertEquals($expected, $qb->debug('group'));
$this->assertInstanceOf('Doctrine\MongoDB\ArrayIterator', $qb->getQuery()->execute());
}

Expand Down Expand Up @@ -185,6 +222,22 @@ public function testRemoveQuery()
$this->assertTrue($qb->getQuery()->execute());
}

/**
* @expectedException BadMethodCallException
*/
public function testFinalizeShouldThrowExceptionForUnsupportedQueryType()
{
$qb = $this->getTestQueryBuilder()->finalize('function() { }');
}

/**
* @expectedException BadMethodCallException
*/
public function testReduceShouldThrowExceptionForUnsupportedQueryType()
{
$qb = $this->getTestQueryBuilder()->reduce('function() { }');
}

public function testThatOrAcceptsAnotherQuery()
{
$coll = $this->conn->selectCollection('db', 'users');
Expand Down Expand Up @@ -336,27 +389,6 @@ public function testUnsetField()
$this->assertEquals($expected, $qb->getNewObj());
}

public function testGroup()
{
$qb = $this->getTestQueryBuilder()
->group(array(), array('count' => 0))
->reduce('function (obj, prev) { prev.count++; }');

$expected = array(
'initial' => array(
'count' => 0
),
'keys' => array()
);
$this->assertEquals($expected, $qb->debug('group'));

$expected = array(
'map' => null,
'options' => array(),
'reduce' => 'function (obj, prev) { prev.count++; }');
$this->assertEquals($expected, $qb->debug('mapReduce'));
}

public function testDateRange()
{
$start = new \MongoDate(strtotime('1985-09-01 01:00:00'));
Expand Down

0 comments on commit 6f41298

Please sign in to comment.