首页 文章

在Mongodb中聚合嵌套数组

提问于
浏览
2

我有一个mongo集合谎言:

{
   "_id":ObjectId("55f16650e3cf2242a79656d1"),
   "user_id":11,
   "push":[
      ISODate("2015-09-08T11:14:18.285      Z"),
      ISODate("2015-09-08T11:14:18.285      Z"),
      ISODate("2015-09-09T11:14:18.285      Z"),
      ISODate("2015-09-10T11:14:18.285      Z"),
      ISODate("2015-09-10T11:14:18.285      Z")
   ]
}{
   "_id":ObjectId("55f15c78e3cf2242a79656c3"),
   "user_id":12,
   "push":[
      ISODate("2015-09-06T11:14:18.285      Z"),
      ISODate("2015-09-05T11:14:18.285      Z"),
      ISODate("2015-09-07T11:14:18.285      Z"),
      ISODate("2015-09-09T11:14:18.285      Z"),
      ISODate("2015-09-09T11:14:18.285      Z"),
      ISODate("2015-09-10T11:14:18.285      Z"),
      ISODate("2015-09-11T11:14:18.285      Z")
   ]
}

如何在单个查询中找到time_tamps <3且具有日期(timestamp)>(currentDate-5)的user_ids . 我将使用PHP而不想将所有文件都带入内存 .

说明:

user_id : date       : count
11      : 2015-09-08 : 2
          2015-09-09 : 1
          2015-09-10 : 2

12      : 2015-09-05 : 1
          2015-09-06 : 1
          2015-09-07 : 1
          2015-09-09 : 2
          2015-09-10 : 1
          2015-09-11 : 1

如果日期设置为2015-09-09(用户输入),则它将为user_id 11提供3(计数),为user_id 12提供4(计数) . 因此,假设count设置为3(用户输入) . 查询应返回11(user_id) . 如果count设置为2,则没有user_id可用,如果count设置为5,则应返回11和12

1 回答

  • 1

    要解决此问题,您需要一个聚合管道,首先将结果“过滤”到“最近5天”,然后基本上“计算每个合格文档中存在的数组项目的数量”,然后查看“总数”是否“小于”三” .

    MongoDB聚合的$size运算符在这里确实有帮助,$map$setDifference的一些额外过滤对于从 $map 返回的 false 结果,因为执行此"in document first"和$group所需的$group阶段,是处理此问题的最有效方法

    $result = $collection->aggregate(array(
        array( '$match' => array(
            'push' => array( 
                'time' => array( 
                    '$gte' =>  MongoDate( strtotime('-5 days',time()) )
                )
            )     
        )),
        array( '$group' => array(
            '_id' => '$user_id',
            'count' => array(
                '$sum' => array(
                    '$size' => array(
                        '$setDifference' => array(
                            array( '$map' => array(
                                'input' => '$push',
                                'as' => 'time',
                                'in' => array(
                                    '$cond' => array(
                                        array( '$gte' => array(
                                            '$$time',
                                            MongoDate( strtotime('-5 days',time()) )
                                        )),
                                        '$time',
                                        FALSE
                                    )
                                ) 
                            )),
                            array(FALSE)
                        )
                    )
                )
            )
        )),
        array( '$match' => array(
            'count' => array( '$lt' => 3 )
        )) 
    ));
    

    所以在完成所有工作之后,首先找到包含符合条件的"possible"文件,通过$match然后找到 $group 下匹配数组项的"total"大小,然后最终 $match 排除所有小于3的结果尺寸 .


    对于那里的大部分“JavaScript大脑”(像我一样,训练有素),这基本上就是这个结构:

    db.collection.aggregate([
        { "$match": {
            "push": {
                "$gte": new Date( new Date().valueOf() - ( 5 * 1000 * 60 * 60 * 24 ))
            }
        }},
        { "$group": {
            "_id": "$user_id",
            "count": {
                "$sum": {
                    "$size": {
                        "$setDifference": [
                            { "$map": {
                                "input": "$push",
                                "as": "time",
                                "in": {
                                    "$cond": [
                                        { "$gte": [ 
                                            "$$time",
                                            new Date( 
                                                new Date().valueOf() - 
                                                ( 5 * 1000 * 60 * 60 * 24 )
                                            )
                                        ]},
                                        "$$time",
                                        false
                                    ]
                                }
                            }},
                            [false]
                        ]
                    }
                }
            }
        }},
        { "$match": { "count": { "$lt": 3 } } }
    ])
    

    此外,未来版本的MongoDB将提供 $filter ,这简化了整个 $map$setDifference 语句部分:

    db.collection.aggregate([
        { "$match": {
            "push": {
                "$gte": new Date( new Date().valueOf() - ( 5 * 1000 * 60 * 60 * 24 ))
            }
        }},
        { "$group": {
            "_id": "$user_id",
            "count": {
                "$sum": {
                    "$size": {
                        "$filter": {
                            "input": "$push",
                            "as": "time",
                            "cond": {
                                "$gte": [
                                    "$$time",
                                    new Date( 
                                        new Date().valueOf() - 
                                        ( 5 * 1000 * 60 * 60 * 24 )
                                    )                       
                                ]
                            }
                        }
                    }
                }
            }
        }},
        { "$match": { "count": { "$lt": 3 } } }
    ])
    

    同时注意到“日期”可能最好在管道定义“之前”计算为最佳准确度的单独变量 .

相关问题