Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

map/reduce

  • Loading branch information...
commit 964d4c5b27e62ffdd7535905d361801d7ba8b277 1 parent 2a12f84
Tom Maiaroto tmaiaroto authored
7 data/model/Query.php
View
@@ -427,8 +427,8 @@ public function export(Source $dataSource, array $options = array()) {
$keys = $options['keys'] ?: array_keys($this->_config);
$methods = $dataSource->methods();
- $results = array('type' => $this->_type);
-
+ $results = array();
+
$apply = array_intersect($keys, $methods);
$copy = array_diff($keys, $apply);
@@ -439,6 +439,9 @@ public function export(Source $dataSource, array $options = array()) {
if (in_array($item, $keys)) {
$results[$item] = $this->_config[$item];
}
+ if ($item == 'type') {
+ $results['type'] = $this->_type;
+ }
}
if (in_array('data', $keys)) {
$results['data'] = $this->_exportData();
105 data/source/MongoDb.php
View
@@ -12,6 +12,7 @@
use MongoCode;
use MongoRegex;
use lithium\util\Inflector;
+use lithium\core\Libraries;
use lithium\core\NetworkException;
use Exception;
use lithium\data\source\mongo_db\Schema;
@@ -126,7 +127,7 @@ class MongoDb extends \lithium\data\Source {
* @see lithium\data\source\MongoDb::$_strategies
* @var array
*/
- protected $_readStrategies = array('calculate', 'group', 'findAndModify', 'find');
+ protected $_readStrategies = array('mapReduce', 'calculate', 'group', 'findAndModify', 'find');
/**
* List of configuration keys which will be automatically assigned to their corresponding
@@ -191,7 +192,7 @@ protected function _init() {
$this->_strategies = array(
'find' => function($source, $query, array $options) {
$keys = array('conditions', 'fields', 'order', 'limit', 'offset');
- $args = $query->export($source, $keys);
+ $args = $query->export($source, compact('keys'));
$collection = $source->collection($query);
$result = $collection->find($args['conditions'], $args['fields']);
@@ -211,7 +212,8 @@ protected function _init() {
if (!isset($update) && !isset($remove)) {
return;
}
- $args = $query->export($source, array('conditions', 'order', 'fields'));
+ $keys = array('conditions', 'order', 'fields');
+ $args = $query->export($source, compact('keys'));
$result = $source->connection->command(compact('update', 'remove') + array(
'findAndModify' => $query->source(),
@@ -227,7 +229,8 @@ protected function _init() {
if (!$query->calculate()) {
return;
}
- $args = $query->export($source, array('conditions'));
+ $keys = array('conditions');
+ $args = $query->export($source, compact('keys'));
return $source->collection($query)->find($args['conditions']);
},
'group' => function($source, $query, array $options) {
@@ -237,7 +240,8 @@ protected function _init() {
$col = $source->collection($query);
$key = $source->group($key, $query);
$init = $query->initial();
- $options = $query->export($source, array('conditions', 'finalize'));
+ $keys = array('conditions', 'finalize');
+ $options = $query->export($source, compact('keys'));
if (isset($options['conditions'])) {
$options['condition'] = $options['conditions'];
@@ -249,6 +253,28 @@ protected function _init() {
$config = array('class' => 'set') + compact('query', 'stats');
return $source->item($query->model(), $data, $config);
+ },
+ 'mapReduce' => function($source, $query, array $options) {
+ if (!$query->map() || !$query->reduce()) {
Simon JAILLET Collaborator
jails added a note

Maybe we should rename the current Query::map https://github.com/UnionOfRAD/lithium/blob/master/data/model/Query.php#L255 to avoid confusion ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ return;
+ }
+
+ // Note: 'mapreduce' MUST come first so it can be passed first to command()
+ $keys = array('mapreduce', 'conditions', 'query', 'source', 'out', 'map', 'reduce', 'finalize');
+ $args = $query->export($source, compact('keys')) + array_fill_keys($keys, null);
+ $args['query'] = $args['query'] ?: $args['conditions'];
+ $args['mapreduce'] = $args['mapreduce'] ?: $args['source'];
+ $args['out'] = $args['out'] ?: array('inline' => true);
+ unset($args['source']);
+ unset($args['conditions']);
+
+ $stats = $source->connection->command($args);
+
+ $data = $stats['results'];
+ unset($stats['results']);
+
+ $config = array('class' => 'set') + compact('query', 'stats');
+ return $source->item($query->model(), $data, $config);
}
);
}
@@ -697,6 +723,75 @@ public function group($group, $context) {
}
return $group;
}
+
+ /**
+ * Sets the collection for the "mapreduce" key. This is collection that the map/reduce
+ * is performed on. If a `Model` class name has been passed, the collection will be used
+ * from that model's settings. If any other string is passed, it will use that collection.
+ *
+ * @param mixed $mapreduce A `Model` class name string or any collection name
+ * @param object $context A `Query` object representing the group query being executed.
+ * @return object Returns a `MongoCode` instance representing the finalize function in a group
+ * query.
+ */
+ public function mapreduce($mapreduce, $context) {
+ $model = Libraries::locate('models', $mapreduce);
+ return is_string($model) ? $model::meta('source'):$mapreduce;
+ }
+
+ /**
+ * Sets the output method and collection for the map/reduce.
+ * The array key is the output method, ie. `replace` or `merge` etc.
+ * The array value is the collection name.
+ *
+ * If the collection name is a model name, the proper collection name for
+ * that model will be used. Otherwise, the passed string will be used as
+ * the output collection.
+ *
+ * If only a string was passed then the `replace` output method will be used.
+ * If the 'inline' was passeds, then array('inline' => true) will be returned
+ * as it is assumed that was desired. To use an actual collection called "inline"
+ * an array would need to be passed with the output type as the key.
+ *
+ * @param mixed $out An array or `Model` class name string or any collection name
+ * @param object $context A `Query` object representing the group query being executed.
+ * @return object Returns a `MongoCode` instance representing the finalize function in a group
+ * query.
+ */
+ public function out($out, $context) {
+ $model = is_array($out) ? Libraries::locate('models', end($out)):Libraries::locate('models', $out);
+ $collection = is_string($model) ? $model::meta('source'):$out;
+ $out = is_string($collection) ? array('replace' => $collection):$collection;
+ return $collection == 'inline' || empty($collection) ? array('inline' => true):$out;
+ }
+
+ /**
+ * Ensures that code for a map function in a query is wrapped in a `MongoCode`
+ * object.
+ *
+ * @param mixed $map Either a string containing a JavaScript function, or a `MongoCode`
+ * instance.
+ * @param object $context A `Query` object representing the group query being executed.
+ * @return object Returns a `MongoCode` instance representing the finalize function in a group
+ * query.
+ */
+ public function map($map, $context) {
+ return is_string($map) ? new MongoCode($map) : $map;
+ }
+
+ /**
+ * Ensures that code for a reduce function in a query is wrapped in a `MongoCode`
+ * object.
+ *
+ * @param mixed $reduce Either a string containing a JavaScript function, or a `MongoCode`
+ * instance.
+ * @param object $context A `Query` object representing the group query being executed.
+ * @return object Returns a `MongoCode` instance representing the finalize function in a group
+ * query.
+ */
+ public function reduce($reduce, $context) {
+ return is_string($reduce) ? new MongoCode($reduce) : $reduce;
+ }
/**
* Ensures that code for a finalize function in a group query is wrapped in a `MongoCode`
4 tests/cases/data/source/MongoDbTest.php
View
@@ -774,6 +774,10 @@ public function testSchemaCallback() {
}));
$this->assertEqual($schema, $db->describe(null)->fields());
}
+
+ public function testMapReduce() {
+
+ }
}
?>

3 comments on commit 964d4c5

Nate Abele
Owner

@tmaiaroto Hey, anything we can do to help you wrap this branch up in the next few days? /cc @gwoo

Tom Maiaroto
Owner

test cases. i suck at em. and i think that was more or less the major outstanding thing... i should take a look at this again though to ensure things are still ok given new driver changes. but i believe they should be. i would also kinda like to work aggregation framework stuff in but if you want to wrap it up in the next few days then maybe wait on that part.

Simon JAILLET
Collaborator

Maybe we should rename the current Query::map https://github.com/UnionOfRAD/lithium/blob/master/data/model/Query.php#L255 to avoid confusion ?

Simon JAILLET
Collaborator

For a first stage, maybe the simplest could be to unit testing each closure independently by passing a MockSource as first parameter and using some lithium\tests\mocks\core\MockCallable to show what parameters are passed to it ?

Please sign in to comment.
Something went wrong with that request. Please try again.