【问题标题】:Translate from SQL to mongo从 SQL 转换为 mongo
【发布时间】:2014-11-12 14:49:25
【问题描述】:

这在 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”,这似乎已将所有产品都计算在内。

【问题讨论】:

  • 您能否提供一个示例,说明您的 MongoDB 文档的外观以及您的预期输出,而不仅仅是您要翻译的 SQL?

标签: php mongodb mongodb-query aggregation-framework doctrine-odm


【解决方案1】:

与许多 ODM 实现一样,学说的核心(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,它提供了一个静态值 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" 或者是“分组依据”语句的一部分。 SQL 也是如此。

这里的其他“管道”阶段基本上是$match,它完成了“后分组”以便与“HAVING”子句具有相同的效果,最后是$project,它只是清理输出以具有所需的字段名称,而不是在所需的 _id ("group by") 键中复合。

这就是您在 MongoDB 中进行聚合的方式。

有关常见 SQL 操作的更多信息,请参阅核心文档中的SQL to Aggregation Mapping Chart。

【讨论】:

    【解决方案2】:

    聚合框架提供了一种处理记录和返回计算结果的简单方法。
    在 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;
    

    你可以read the documentation.

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-12-02
      • 2020-01-15
      • 2018-02-21
      • 2012-07-04
      • 2017-05-01
      • 2021-06-14
      相关资源
      最近更新 更多