如何在$ group中对$ addToSet进行排序?

时间:2015-10-05 09:57:23

标签: php mongodb sorting mongodb-query aggregation-framework

** 更新 **

Hello MongoDB-Experts,

我是一名软件开发人员,我对MongoDB没有多少经验。

我想在$ addToSet字段中对某些值进行排序。我基本上想要对$ group中的“firmen”(公司)进行排序。我怎么能这样做?

非常感谢您的帮助。
t.koelpin

这是我使用的PHP代码:

$sumQuery = array(
    array(
        '$match' => array(
            'startzeit' => array(
                '$gte' => new MongoDate(strtotime("2015-01-01 01:00:00")),
                '$lte' => new MongoDate(strtotime("2015-01-01 08:00:00"))
            )
        )    
    ),
    array(
        '$group' => array(
            '_id' => array(
                'portal' => '$portal',
                'protokoll' => '$protokoll'
            ),
            'daten' => array(
                '$addToSet' => array(
                    'firmen' => '$firma'
                )
            ),
            'count' => array(
                '$sum' => 1
            )
        )
    ),
    array(
        '$sort' => array(
            '_id' => 1          
        )
    ),
    array(
        '$project' => array(
            '_id' => 0,
            'portal' => '$_id.portal',
            'protokoll' => '$_id.protokoll',
            'firmen' => '$daten.firmen',
            'connections' => '$count'
        )
    )
);

这是我未分类的PHP输出:

unsorted PHP-output

2 个答案:

答案 0 :(得分:1)

如@Neil answer以及此Jira Ticket sets中所述,将无序。

延伸到Neil的答案,这可以通过以下步骤来实现:

  1. Unwind集合。
  2. Sort基于该字段。
  3. Group字段$push
  4. 由于unwindinggrouping不会更改结果的顺序,唯一的挑战是保留Connections的值。这可以使用$first$max运算符来实现。

    这是修改后的PHP代码来实现这一目标。

        $sumQuery = array(
            array(
                '$match' => array(
                    'startzeit' => array(
                        '$gte' => new MongoDate(strtotime("2015-01-01 01:00:00")),
                        '$lte' => new MongoDate(strtotime("2015-01-01 08:00:00"))
                    )
                )    
            ),
            array(
                '$group' => array(
                    '_id' => array(
                        'portal' => '$portal',
                        'protokoll' => '$protokoll'
                    ),
                    'daten' => array(
                        '$addToSet' => array(
                            'firmen' => '$firma'
                        )
                    ),
                    'count' => array(
                        '$sum' => 1
                    )
                )
            ),
    
            //Unwinding daten.firmen
            array('$unwind' =>  '$daten.firmen'),
    
            //Sorting the values
            array(
                '$sort' => array(
                    'daten.firmen' => 1          
                )
            ),
    
            //$push creates an array so the sorted order is preserved
            array(
                '$group' => array(
                    '_id' => array(
                        'portal' => '$_id.portal',
                        'protokoll' => '$_id.protokoll'
                    ),
                    'daten' => array(
                        '$push' => array(
                            'firmen' => '$daten.firmen'
                        )
                    ),
                    'count' => array(
                        '$max' => '$count'
                    )
                )
            ),
            array(
                '$sort' => array(
                    '_id' => 1          
                )
            ),
            array(
                '$project' => array(
                    '_id' => 0,
                    'portal' => '$_id.portal',
                    'protokoll' => '$_id.protokoll',
                    'firmen' => '$daten.firmen',
                    'connections' => '$count'
                )
            )
        );
    

    PS:我不确定PHP的语法。请验证并修复任何小错误。

答案 1 :(得分:1)

真正的情况是,当然“套装”不会被认为是以任何方式进行排序,所以如果您希望订购这些元素,那么您需要以不是“集合”的方式对待它们。

优于“展开”由$addToSet生成的数组,对于要包含在数组中的值,最初只是$group,作为分组键的一部分。根据定义,分组键中的任何内容都是“不同”值,因此,如果您先$group,那么您可以在$group处理元素{{1}之后再次$sort来“卷起”数组}。

这将节省大量开销,首先使用$addToSet汇总数组,然后再次“去规范化”以便对内容进行排序,因为这可能非常昂贵:

$sumQuery = array(
    array(
        '$match' => array(
            'startzeit' => array(
                '$gte' => new MongoDate(strtotime("2015-01-01 01:00:00")),
                '$lte' => new MongoDate(strtotime("2015-01-01 08:00:00"))
            )
        )    
    ),
    array(
        '$group' => array(
            '_id' => array(
                'portal' => '$portal',
                'protokoll' => '$protokoll',
                'daten' => '$firmen'
            ),
            'count' => array(
                '$sum' => 1
            )
        )
    ),
    array(
        '$sort' => array(
            '_id' => 1
        )
    ),
    array(
        '$group' => array(
            '_id' => array(
                'portal' => '$_id.portal',
                'protokoll' => '$_id.protokoll'
            ),
            'daten' => array( '$push' => '$_id.daten'),
            'count' => array(
                '$sum' => '$count'
            )
        )
    ),
    array(
        '$project' => array(
            '_id' => 0,
            'portal' => '$_id.portal',
            'protokoll' => '$_id.protokoll',
            'firmen' => '$daten',
            'connections' => '$count'
        )
    )
);

另请注意,如果您期望来自$project阶段的某个“字段顺序”,则之前的阶段中不会出现任何字段名称,否则MongoDB只会将该输出“复制”到较早的文档中作为优化的位置。

通常建议在您的管道末尾使用这样的$project阶段,并接受返回的结果。这样做会为返回的结果添加一个额外的传递,以便进行字段重命名。因此,如果这里不需要计算,那么为了重新设置字段命名,你只需要花费大量的内存和CPU周期。

通常在收到结果的客户端代码中处理的比通过强制聚合管道执行它更好。但是,如果您$group分两个阶段,首先处于更广泛的级别并且中间有$sort,那么这是返回“不同”列表的最佳方式,而不会导致性能损失{{ 1}}然后再次重新分组结果。