应该如何在MongoDB中编写?
SELECT field1, field2, product, COUNT(product) as counter
FROM table
GROUP BY product
HAVING counter > 0
我尝试过以下内容,几乎是Doctrine手册的副本。
$query = $this->createQueryBuilder('AcmeBundle:Product')
->group(array('product'), array('count' => 0))
->reduce('function (obj, prev) { prev.count++; }')
->field('product, field1, field2, count')
;
这只给了我1个元素“count = 21”,它似乎已经采取了所有产品并将它们计算在一起。
像许多ODM实现一样,doctrine(mongodDB包装部分)的核心构建在MongoDB的基本本机驱动程序接口之上。这允许暴露于这些驱动程序对象,因此您可以实现本机MongoDB方法来执行查询。
我这说是一般的,因为查询的最佳选择是MongoDB的aggregation framework。它是服务器上本机代码的高性能实现,因此不依赖于其他方法实现的“reduce”类型方法的JavaScript解释。一般来说,DSL不能很好地映射到试图成为“所有东西”的管理器,因此也会生成SQL。概念是完全不同的,因此是最佳的。
事实证明,有一个Collection包装类,您可以使用它而无需深入到本机驱动程序对象。它有自己的.aggregate()
方法包装器。缺乏有关如何访问此对象的详细信息,但您可以跟踪original commit中的引用。
但是获取底层驱动程序对象非常简单:
$mongoCollection = $dm->getConnection()->getMongo()
->selectCollection('collectionName');
$result = $mongoCollection->aggregate(
array(
array( '$group' => array(
'_id' => array(
'field1' => '$field1',
'field2' => '$field2',
'product' => '$product'
),
'counter' => array( '$sum' => 1 )
)),
array( '$match' => array(
'counter' => array( '$gt' => 0 )
)),
array( '$project' => array(
'field1' => '$_id.field1',
'field2' => '$_id.field2,
'product' => '$_id.product',
'counter' => 1
))
)
);
它使用$group
管道运算符通过指定的_id
键进行实际的“分组”。在这种情况下,这可以是“复合键”,因此所有字段都是组合使用的。您不希望“分组依据”的任何字段都与分组运算符一起使用,例如$sum
here,它提供了静态值1
来表示匹配的“计数”。
如果您只想“分组”“产品”字段值,则结果中的其他字段也必须位于grouping operator下。也许像$first
:
$result = $mongoCollection->aggregate(
array(
array( '$group' => array(
'_id' => '$product',
'field1' => array( '$first' => '$field1' ),
'field2' => array( '$first' => '$field2' ),
'counter' => array( '$sum' => 1 )
)),
array( '$match' => array(
'counter' => array( '$gt' => 0 )
)),
array( '$project' => array(
'_id' => 0,
'product' => '$_id',
'field1' => 1,
'field2' => 1,
'counter' => 1
))
)
);
但是你想要的任何字段都必须遵守"grouping operator"或者是“group by”声明的一部分。 SQL也是如此。
这里的其他“管道”阶段基本上是$match
,它是“后分组”,以便与“HAVING”子句具有相同的效果,最后$project
只是清理输出以获得所需的字段名称而不是在所需的_id
(“group by”)键内复合。
这就是你在MongoDB中进行聚合的方式。
有关常见SQL操作的更多信息,请参阅核心文档中的SQL to Aggregation Mapping Chart。
聚合框架提供了一种处理记录和返回计算结果的简便方法。 在YourDocumentNameRepository.php文件中写下以下行:
$qb = $this->createAggregationBuilder('Document\YourDocumentName');
/* First stage: group by product and calculate the number of rows foreach group. */
$qb->group()
->field('id')
->expression('$product')
->field('field1')
->first('$field1)
->field('field2')
->first('$field2')
->field('counter')
->sum(1);
/* Second stage: similar to HAVING counter > 0 in SQL*/
$qb->match()
->field('counter')
->gt(0);
/* Execute the request */
$results = $qb->execute();
return $results;