diff --git a/UPGRADE-2.0.md b/UPGRADE-2.0.md index 6899075d17..3ec6b09257 100644 --- a/UPGRADE-2.0.md +++ b/UPGRADE-2.0.md @@ -251,6 +251,10 @@ ocramius. If you are checking for proxies, the following changed: * Running `geoNear` commands through the `geoNear` helper in the query builder is no longer supported. Please refactor your queries to use the aggregation framework and `$geoNear` pipeline operator. +* Running `group` and `mapReduce` commands through the query builder is no + longer supported. Please either refactor your queries to use the aggregation + framework or use the MongoDB library (`mongodb/mongodb`) to execute these + commands. * The `Doctrine\ODM\MongoDB\Query\FieldExtractor` class was dropped entirely. * The `getIterator` method in `Doctrine\ODM\MongoDB\Query\Query` returns an iterator of type `Doctrine\ODM\MongoDB\Iterator\Iterator` instead of a MongoDB diff --git a/docs/en/reference/map-reduce.rst b/docs/en/reference/map-reduce.rst deleted file mode 100644 index 26e0cc1a56..0000000000 --- a/docs/en/reference/map-reduce.rst +++ /dev/null @@ -1,119 +0,0 @@ -Map Reduce -========== - -The Doctrine MongoDB ODM fully supports the `map reduce`_ functionality via its -:doc:`Query Builder API `. - -.. note:: - - From the MongoDB manual: - - Map-reduce is a data processing paradigm for condensing large volumes of - data into useful aggregated results. In MongoDB, map-reduce operations use - custom JavaScript functions to map, or associate, values to a key. If a key - has multiple values mapped to it, the operation reduces the values for the - key to a single object. - -Imagine a situation where you had an application with a document -named ``Event`` and it was related to a ``User`` document: - -.. code-block:: php - - createQueryBuilder(Documents\Event::class) - ->field('type') - ->equals('sale') - ->map('function() { emit(this.user.$id, 1); }') - ->reduce('function(k, vals) { - var sum = 0; - for (var i in vals) { - sum += vals[i]; - } - return sum; - }'); - $query = $qb->getQuery(); - $results = $query->execute(); - - foreach ($results as $user) { - printf("User %s had %d sale(s).\n", $user['_id'], $user['value']); - } - -.. note:: - - The query builder also has a ``finalize()`` method, which may be used to - specify a `finalize function`_ to be executed after the reduce step. - -When using map reduce with Doctrine, the results are not hydrated into objects. -Instead, the raw results are returned directly from MongoDB. - -The preceding example is equivalent to executing the following command via the -PHP driver directly: - -.. code-block:: php - - selectDB('my_db'); - - $map = new MongoDB\BSON\Javascript('function() { emit(this.user.$id, 1); }'); - $reduce = new MongoDB\BSON\Javascript('function(k, vals) { - var sum = 0; - for (var i in vals) { - sum += vals[i]; - } - return sum; - }'); - - $result = $db->command( - [ - 'mapreduce' => 'events', - 'map' => $map, - 'reduce' => $reduce, - 'query' => ['type' => 'sale'], - ] - ); - - foreach ($result['results'] as $user) { - printf("User %s had %d sale(s).\n", $user['_id'], $user['value']); - } - -.. _`map reduce`: https://docs.mongodb.com/manual/core/map-reduce/ -.. _`finalize function`: https://docs.mongodb.com/master/reference/command/mapReduce/#mapreduce-finalize-cmd diff --git a/docs/en/reference/query-builder-api.rst b/docs/en/reference/query-builder-api.rst index 6873736867..d8628afff9 100644 --- a/docs/en/reference/query-builder-api.rst +++ b/docs/en/reference/query-builder-api.rst @@ -18,8 +18,6 @@ The ``Query`` object supports several types of queries - INSERT - UPDATE - REMOVE -- GROUP -- MAP_REDUCE - DISTINCT_FIELD - GEO_LOCATION @@ -330,47 +328,6 @@ in the order you call the method: $query->sort('featured', 'desc'); -Map Reduce -~~~~~~~~~~ - -You can also run map reduced find queries using the ``Query`` -object: - -.. code-block:: php - - dm->createQueryBuilder(Event::class) - ->field('type')->equals('sale') - ->map('function() { emit(this.userId, 1); }') - ->reduce("function(k, vals) { - var sum = 0; - for (var i in vals) { - sum += vals[i]; - } - return sum; - }"); - $query = $qb->getQuery(); - $results = $query->execute(); - -.. note:: - - When you specify a ``map()`` and ``reduce()`` operation - the results will not be hydrated and the raw results from the map - reduce operation will be returned. - -If you just want to reduce the results using a javascript function -you can just call the ``where()`` method: - -.. code-block:: php - - createQueryBuilder(User::class) - ->where("function() { return this.type == 'admin'; }"); - -You can read more about the `$where operator `_ in the Mongo docs. - Conditional Operators ~~~~~~~~~~~~~~~~~~~~~ @@ -923,38 +880,3 @@ Here is an example where we remove users who have never logged in: ->field('num_logins')->equals(0) ->getQuery() ->execute(); - -Group Queries -------------- - -.. note:: - - Due to deprecation of ``group`` command in MongoDB 3.4 the ODM - also deprecates its usage through Query Builder in 1.2. Please - use :ref:`$group stage ` of the - Aggregation Builder instead. - -The last type of supported query is a group query. It performs an -operation similar to SQL's GROUP BY command. - -.. code-block:: php - - dm->createQueryBuilder(\Documents\User::class) - ->group([], ['count' => 0]) - ->reduce('function (obj, prev) { prev.count++; }') - ->field('a')->gt(1) - ->getQuery() - ->execute(); - -This is the same as if we were to do the group with the raw PHP -code: - -.. code-block:: php - - ['$gt' => 1]]; - $result = $collection->group([], ['count' => 0], $reduce, $condition); diff --git a/docs/en/sidebar.rst b/docs/en/sidebar.rst index a30c963b3d..3c127010ab 100644 --- a/docs/en/sidebar.rst +++ b/docs/en/sidebar.rst @@ -40,7 +40,6 @@ reference/find-and-update reference/filters reference/priming-references - reference/map-reduce reference/capped-collections reference/storage-strategies reference/custom-collections diff --git a/lib/Doctrine/ODM/MongoDB/Query/Builder.php b/lib/Doctrine/ODM/MongoDB/Query/Builder.php index 7db5a83c35..bfe060ab70 100644 --- a/lib/Doctrine/ODM/MongoDB/Query/Builder.php +++ b/lib/Doctrine/ODM/MongoDB/Query/Builder.php @@ -488,31 +488,6 @@ public function field(string $field) : self return $this; } - /** - * Set the "finalize" option for a mapReduce or group command. - * - * @param string|Javascript $finalize - * - * @throws BadMethodCallException If the query is not a mapReduce or group command. - */ - public function finalize($finalize) : self - { - 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 finalize()'); - } - - return $this; - } - /** * Change the query type to find and optionally set and change the class being queried. */ @@ -661,10 +636,6 @@ public function getNewObj() : array */ public function getQuery(array $options = []) : Query { - if ($this->query['type'] === Query::TYPE_MAP_REDUCE) { - $this->hydrate = false; - } - $documentPersister = $this->dm->getUnitOfWork()->getDocumentPersister($this->class->name); $query = $this->query; @@ -888,69 +859,6 @@ public function lte($value) : self return $this; } - /** - * Change the query type to a mapReduce command. - * - * The "reduce" option is not specified when calling this method; it must - * be set with the {@link Builder::reduce()} method. - * - * The "out" option defaults to inline, like {@link Builder::mapReduce()}. - * - * @see http://docs.mongodb.org/manual/reference/command/mapReduce/ - * - * @param string|Javascript $map - */ - public function map($map) : self - { - $this->query['type'] = Query::TYPE_MAP_REDUCE; - $this->query['mapReduce'] = [ - 'map' => $map, - 'reduce' => null, - 'out' => ['inline' => true], - 'options' => [], - ]; - return $this; - } - - /** - * Change the query type to a mapReduce command. - * - * @see http://docs.mongodb.org/manual/reference/command/mapReduce/ - * - * @param string|Javascript $map - * @param string|Javascript $reduce - * @param array|string $out - * @param array $options - * - * @return $this - */ - public function mapReduce($map, $reduce, $out = ['inline' => true], array $options = []) : self - { - $this->query['type'] = Query::TYPE_MAP_REDUCE; - $this->query['mapReduce'] = [ - 'map' => $map, - 'reduce' => $reduce, - 'out' => $out, - 'options' => $options, - ]; - return $this; - } - - /** - * Set additional options for a mapReduce command. - * - * @throws BadMethodCallException If the query is not a mapReduce command. - */ - public function mapReduceOptions(array $options) : self - { - if ($this->query['type'] !== Query::TYPE_MAP_REDUCE) { - throw new BadMethodCallException('This method requires a mapReduce command (call map() or mapReduce() first)'); - } - - $this->query['mapReduce']['options'] = $options; - return $this; - } - /** * Updates the value of the field to a specified value if the specified value is greater than the current value of the field. * @@ -1101,23 +1009,6 @@ public function notIn(array $values) : self return $this; } - /** - * Set the "out" option for a mapReduce command. - * - * @param array|string $out - * - * @throws BadMethodCallException If the query is not a mapReduce command. - */ - public function out($out) : self - { - if ($this->query['type'] !== Query::TYPE_MAP_REDUCE) { - throw new BadMethodCallException('This method requires a mapReduce command (call map() or mapReduce() first)'); - } - - $this->query['mapReduce']['out'] = $out; - return $this; - } - /** * Remove the first element from the current array field. * @@ -1252,31 +1143,6 @@ public function readOnly(bool $bool = true) : self return $this; } - /** - * Set the "reduce" option for a mapReduce or group command. - * - * @param string|Javascript $reduce - * - * @throws BadMethodCallException If the query is not a mapReduce or group command. - */ - public function reduce($reduce) : self - { - 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; - } - public function references(object $document) : self { $this->expr->references($document); diff --git a/lib/Doctrine/ODM/MongoDB/Query/Query.php b/lib/Doctrine/ODM/MongoDB/Query/Query.php index 74a619dceb..dd88734630 100644 --- a/lib/Doctrine/ODM/MongoDB/Query/Query.php +++ b/lib/Doctrine/ODM/MongoDB/Query/Query.php @@ -45,8 +45,6 @@ class Query implements IteratorAggregate public const TYPE_INSERT = 4; public const TYPE_UPDATE = 5; public const TYPE_REMOVE = 6; - public const TYPE_GROUP = 7; - public const TYPE_MAP_REDUCE = 8; public const TYPE_DISTINCT = 9; public const TYPE_COUNT = 11; @@ -125,8 +123,6 @@ public function __construct(DocumentManager $dm, ClassMetadata $class, Collectio case self::TYPE_INSERT: case self::TYPE_UPDATE: case self::TYPE_REMOVE: - case self::TYPE_GROUP: - case self::TYPE_MAP_REDUCE: case self::TYPE_DISTINCT: case self::TYPE_COUNT: break; @@ -239,8 +235,6 @@ public function getIterator() : Iterator { switch ($this->query['type']) { case self::TYPE_FIND: - case self::TYPE_GROUP: - case self::TYPE_MAP_REDUCE: case self::TYPE_DISTINCT: break; diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Query/BuilderTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Query/BuilderTest.php index c1ce91c008..b39b86da32 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Query/BuilderTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Query/BuilderTest.php @@ -199,101 +199,6 @@ public function provideArrayUpdateOperatorsOnReferenceOne() yield [ChildC::class, 'featurePartial']; } - public function testMapReduceQueryWithSingleMethod() - { - $map = 'function() { - for(i = 0; i <= this.options.length; i++) { - emit(this.name, { count: 1 }); - } - }'; - - $reduce = 'function(product, values) { - var total = 0 - values.forEach(function(value){ - total+= value.count; - }); - return { - product: product, - options: total, - test: values - }; - }'; - - $finalize = 'function (key, value) { return value; }'; - - $out = ['inline' => true]; - - $qb = $this->getTestQueryBuilder() - ->mapReduce($map, $reduce, $out, ['finalize' => $finalize]); - - $expectedMapReduce = [ - 'map' => $map, - 'reduce' => $reduce, - 'out' => ['inline' => true], - 'options' => ['finalize' => $finalize], - ]; - - $this->assertEquals(Query::TYPE_MAP_REDUCE, $qb->getType()); - $this->assertEquals($expectedMapReduce, $qb->debug('mapReduce')); - } - - public function testMapReduceQueryWithMultipleMethodsAndQueryArray() - { - $map = 'function() { - for(i = 0; i <= this.options.length; i++) { - emit(this.name, { count: 1 }); - } - }'; - - $reduce = 'function(product, values) { - var total = 0 - values.forEach(function(value){ - total+= value.count; - }); - return { - product: product, - options: total, - test: values - }; - }'; - - $finalize = 'function (key, value) { return value; }'; - - $qb = $this->getTestQueryBuilder() - ->map($map) - ->reduce($reduce) - ->finalize($finalize) - ->field('username')->equals('jwage'); - - $expectedQueryArray = ['username' => 'jwage']; - $expectedMapReduce = [ - 'map' => $map, - 'reduce' => $reduce, - 'options' => ['finalize' => $finalize], - 'out' => ['inline' => true], - ]; - - $this->assertEquals(Query::TYPE_MAP_REDUCE, $qb->getType()); - $this->assertEquals($expectedQueryArray, $qb->getQueryArray()); - $this->assertEquals($expectedMapReduce, $qb->debug('mapReduce')); - } - - /** - * @expectedException BadMethodCallException - */ - public function testFinalizeShouldThrowExceptionForUnsupportedQueryType() - { - $qb = $this->getTestQueryBuilder()->finalize('function() { }'); - } - - /** - * @expectedException BadMethodCallException - */ - public function testReduceShouldThrowExceptionForUnsupportedQueryType() - { - $qb = $this->getTestQueryBuilder()->reduce('function() { }'); - } - public function testThatOrAcceptsAnotherQuery() { $qb = $this->getTestQueryBuilder(); @@ -596,24 +501,6 @@ public function providePoint() ]; } - /** - * @expectedException BadMethodCallException - */ - public function testMapReduceOptionsRequiresMapReduceCommand() - { - $qb = $this->getTestQueryBuilder(); - $qb->mapReduceOptions([]); - } - - /** - * @expectedException BadMethodCallException - */ - public function testOutRequiresMapReduceCommand() - { - $qb = $this->getTestQueryBuilder(); - $qb->out('collection'); - } - /** * @dataProvider provideSelectProjections */