How should this be written in MongoDB?
SELECT field1, field2, product, COUNT(product) as counter
FROM table
GROUP BY product
HAVING counter > 0
I have tried with the following, which is nearly a copy from Doctrine manual.
$query = $this->createQueryBuilder('AcmeBundle:Product')
->group(array('product'), array('count' => 0))
->reduce('function (obj, prev) { prev.count++; }')
->field('product, field1, field2, count')
;
This only gives me 1 element "count = 21" which seems it have taken all products and counted them together.
Like many ODM implementations, the core of doctrine (mongodDB wrapping part) is built "on top of" the basic native driver interface for MongoDB. This allows exposure to those driver objects so you can implement native MongoDB methods to do your queries.
I say this in general as the best option for your query is the aggregation framework of MongoDB. It's a high performance implementation in native code on the server, so does not rely on JavaScript interpretation for "reduce" type methods as is implemented by other methods. Generally speaking, the DSL does not map well to managers that try to be "all things" and therefore generate SQL as well. The concepts are quite different, and therefore optimal.
As it turns out though, there is a Collection wrapper class which you can use without delving right into the native driver object. It has it's own
.aggregate()
method wrapper. Lacking details on how to access this object but you can trace through the reference from the original commit.But getting the underlying driver object is fairly straightforward:
That uses the
$group
pipeline operator to do the actual "grouping" via the_id
key specified. This can be a "compound key" as it is in this case, so all fields are taken in combination. Any fields your don't want to "group by" are used with grouping operators such as$sum
here, which is supplied with a static value of1
to represent the "count" of matches.If you just want to "group by" the "product" field values, then other fields in your result must also be under a grouping operator. Maybe something like
$first
:But any fields you want must be subject to a "grouping operator" or are part of the "group by" statement. The same is true for SQL.
The other "pipeline" stages here are basically
$match
which is done "post grouping" in order to have the same effect as the "HAVING" clause, and finally the$project
which just cleans up the output to have the desired field names rather than being compounded within the required_id
("group by") key.That's how you do aggregation in MongoDB.
For more information on common SQL operations, see the SQL to Aggregation Mapping Chart in the core documentation.