Skip to content

Commit

Permalink
Implementing find('threaded') to showcase again how to use map-reduce...
Browse files Browse the repository at this point in the history
Still hacks the end result by returning objects instead of arrays, this
can almost trivially be fixed later, though
  • Loading branch information
lorenzo committed Aug 17, 2013
1 parent 6fbdb04 commit ea4cf1f
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 17 deletions.
8 changes: 4 additions & 4 deletions lib/Cake/ORM/MapReduce.php
Expand Up @@ -35,10 +35,10 @@ class MapReduce implements IteratorAggregate {

protected $_counter = 0;

public function __construct($data, callable $mapper, callable $reducer) {
public function __construct($data, array $routines) {
$this->_data = $data;
$this->_mapper = $mapper;
$this->_reducer = $reducer;
$this->_mapper = $routines['mapper'];
$this->_reducer = isset($routines['reducer']) ? $routines['reducer'] : null;
}

public function getIterator() {
Expand All @@ -54,6 +54,7 @@ public function emitIntermediate($key, $value) {

public function emit($value, $slot = null) {
$this->_result[$slot === null ? $this->_counter : $slot] = $value;
$this->_counter++;
}

protected function _execute() {
Expand All @@ -63,7 +64,6 @@ protected function _execute() {

foreach ($this->_intermediate as $key => $list) {
$this->_reducer->__invoke($key, $list, $this);
$this->_counter++;
}
$this->_execute = true;
}
Expand Down
20 changes: 10 additions & 10 deletions lib/Cake/ORM/Query.php
Expand Up @@ -67,9 +67,7 @@ class Query extends DatabaseQuery {
*/
protected $_loadEagerly = [];

protected $_mapper;

protected $_reducer;
protected $_mapReduce = [];

/**
* List of options accepted by associations in contain()
Expand Down Expand Up @@ -529,21 +527,23 @@ public function applyOptions(array $options) {
return $this;
}

public function mapReduce(callable $mapper, callable $reducer) {
$this->_mapper = $mapper;
$this->_reducer = $reducer;
public function mapReduce(callable $mapper, callable $reducer = null) {
$this->_mapReduce[] = compact('mapper', 'reducer');
return $this;
}

protected function _applyFormatters($result) {
foreach ($this->_formatters as $formatter) {
$result = new ResultSetDecorator($result, $formatter);
}
if (!empty($this->_mapper) && !empty($this->_reducer)) {
$result = new ResultSetDecorator(
new MapReduce($result, $this->_mapper, $this->_reducer)
);

foreach ($this->_mapReduce as $mappers) {
$result = new MapReduce($result, $mappers);
}

if (!empty($this->_mapReduce)) {
$result = new ResultSetDecorator($result);
}
return $result;
}

Expand Down
20 changes: 18 additions & 2 deletions lib/Cake/ORM/Table.php
Expand Up @@ -525,7 +525,7 @@ public function findAll(Query $query, array $options = []) {
}

public function findList(Query $query, array $options = []) {
$mapper = function($key, $row, $mr) use ($query, &$columns) {
$mapper = function($key, $row, $mr) use (&$columns) {
if (empty($columns)) {
$columns = array_slice(array_keys($row), 0, 3);
}
Expand All @@ -540,7 +540,7 @@ public function findList(Query $query, array $options = []) {
$mr->emitIntermediate($key, [$row[$rowKey] => $row[$rowVal]]);
};

$reducer = function($key, $values, $mr) use (&$columns) {
$reducer = function($key, $values, $mr) {
$result = [];
foreach ($values as $value) {
$result += $value;
Expand All @@ -551,6 +551,22 @@ public function findList(Query $query, array $options = []) {
return $query->mapReduce($mapper, $reducer);
}

public function findThreaded(Query $query, array $options = []) {
$parents = new \ArrayObject;
$mapper = function($key, $row, $mr) use ($parents) {
$parents[$row['id']] = new \ArrayObject($row);
if (empty($row['parent_id'])) {
return $mr->emit($parents[$row['id']]);
}
$mr->emitIntermediate($row['parent_id'], $parents[$row['id']]);
};

$reducer = function($key, $values, $mr) use ($parents) {
$parents[$key]['children'] = $values;
};
return $query->mapReduce($mapper, $reducer);
}

/**
* Creates a new Query instance for this table
*
Expand Down
57 changes: 56 additions & 1 deletion lib/Cake/Test/TestCase/ORM/TableTest.php
Expand Up @@ -54,7 +54,7 @@ class MyUsersTable extends Table {
*/
class TableTest extends \Cake\TestSuite\TestCase {

public $fixtures = ['core.user'];
public $fixtures = ['core.user', 'core.category'];

public function setUp() {
parent::setUp();
Expand Down Expand Up @@ -539,4 +539,59 @@ public function testFindList() {
$this->assertSame($expected, $query->toArray());
}

public function testFindThreaded() {
$table = new Table(['table' => 'categories', 'connection' => $this->connection]);
$expected = array(
new \ArrayObject(array(
'id' => 1,
'parent_id' => 0,
'name' => 'Category 1',
'children' => array(
new \ArrayObject(array(
'id' => 2,
'parent_id' => 1,
'name' => 'Category 1.1',
'children' => array(
new \ArrayObject(array(
'id' => 7,
'parent_id' => 2,
'name' => 'Category 1.1.1',
)),
new \ArrayObject(array(
'id' => 8,
'parent_id' => '2',
'name' => 'Category 1.1.2',
))
),
)),
new \ArrayObject(array(
'id' => 3,
'parent_id' => '1',
'name' => 'Category 1.2',
)),
)
)),
new \ArrayObject(array(
'id' => 4,
'parent_id' => 0,
'name' => 'Category 2',
)),
new \ArrayObject(array(
'id' => 5,
'parent_id' => 0,
'name' => 'Category 3',
'children' => array(
new \ArrayObject(array(
'id' => '6',
'parent_id' => '5',
'name' => 'Category 3.1',
))
)
))
);
$results = $table->find('threaded')
->select(['id', 'parent_id', 'name'])
->toArray();
$this->assertEquals($expected, $results);
}
}

0 comments on commit ea4cf1f

Please sign in to comment.