首页 文章

MongoDB将子数组聚合为组_id

提问于
浏览
2

我在使用MongoDB聚合框架计算数据库中的事件类型时遇到了一些麻烦 . 如何计算 _id.val 字段的每个唯一第三个索引的 value.count 字段的总和?

我的数据的基本结构如下:

{ _id: { evt: "click", val: [ "default", "125", "311", "1" ] }, value: { count: 1 } }
{ _id: { evt: "click", val: [ "default", "154", "321", "2" ] }, value: { count: 2 } }
{ _id: { evt: "click", val: [ "default", "192", "263", "1" ] }, value: { count: 4 } }

val 字段中的值分别表示 ["type","x","y","time"] . 我正在尝试提取 _id.val 键的第3个索引或 time 值 . 我想要实现的输出:

1: 5
 2: 2

我一直试图通过这个PHP来做到这一点:

$ops2 = array(
    array(
      '$match' => $q2
    ),
    array(
        '$group' => array(
          '_id' => array(
            'evt' => '$_id.evt',
            'time' => '$_id.val.3'
          ),
          'count' => array('$sum' => '$value.count' )
        )
    )
);

但它似乎不喜欢组数组中的 3 索引

2 回答

  • 1

    起初,我认为你对Mongo的理解可能有些不对...因为mongo中的每个文档都应该有其唯一的_id,以便从别人身上识别出来 . 所以我为每个对象添加了一个_id,并将你的原点“_id”字段更改为“data” . 现在的结构是:

    /* 1 */
    {
        "_id" : "ubLrDptWvJE7LZqDF",
        "data" : {
            "evt" : "click",
            "val" : [ "default", "125", "311", "1" ]
        },
        "value" : {
            "count" : 1
        }
    }
    
    /* 2 */
    {
        "_id" : "C2QCEhvCsp3xG6EKZ",
        "data" : {
            "evt" : "click",
            "val" : [ "default", "154", "321", "2" ]
        },
        "value" : {
            "count" : 2
        }
    }
    
    /* 3 */
    {
        "_id" : "bT72z7gMKoyX5JfHL",
        "data" : {
            "evt" : "click",
            "val" : [ "default", "192", "263", "1" ]
        },
        "value" : {
            "count" : 4
        }
    }
    

    我不知道如何在PHP中执行此查询,因为我只知道一点PHP ......但我可以给你一个在Javascript中使用聚合的例子,它的代码和输出如下:
    code and output

    这里有一些有用的链接:using mongo in PHP我希望它可以帮助你完美地解决你的问题:-)

  • 1

    您正在使用的数据看起来已经作为mapReduce操作的输出,因为它具有mapReduce产生的特定“_id”和“value”结构 . 因此,您可能最好回到实现该流程的逻辑,并遵循相同的原则来提取和总计您想要的内容,或者至少将其输出形式更改为:

    { 
        _id: { 
            evt: "click", 
            val: { "type": "default", "x": "125", "y": "311", "time": "1" }
        }, 
        value: { count: 1 } 
    },
    { 
        _id: { 
            evt: "click", 
            val: { "type": "default", "x": "154", "y": "321", "time": "2" }
         }, 
         value: { count: 2 }
    },
    { 
        _id: { 
            evt: "click",
            val: { "type": "default", "x": "192", "y": "263", "time": "1" }
        },
        value: { count: 4 }
    }
    

    因为问题是聚合框架"presently"缺乏解决数组的"indexed"位置的能力(真正的"non-associative"数组而不是PHP数组),并且当你尝试这样做时总是会返回 null .

    由于缺乏返回原始源或mapReduce操作的能力,您可以对此数据编写mapReduce操作以获得预期的结果(shell表示,因为它无论如何都是JavaScript):

    db.collection.mapReduce(
        function() {
            emit({ evt: this._id.evt, time: this._id.val[3] }, this.value.count)
        },
        function(key,values) {
            return Array.sum(values)
        },
        { out: { inline: 1 } }
    )
    

    这返回典型的mapReduce输出,如下所示:

    {
        "_id" : {
                "evt" : "click",
                "time" : "1"
        },
        "value" : 5
    },
    {
        "_id" : {
                "evt" : "click",
                "time" : "2"
        },
        "value" : 2
    }
    

    如果您至少能够将当前输出集合转换为上面首先建议的表单,那么您将使用这样的聚合框架(同样常见的代表)运行:

    { "$group": {
            "_id": {
                "evt": "$_id.evt",
                "time": "$_id.val.time"
            },
            "count": { "$sum": "$value.count" }
        }}
    

    这当然会从改变的数据中产生:

    { "_id" : { "evt" : "click", "time" : "2" }, "count" : 2 }
    { "_id" : { "evt" : "click", "time" : "1" }, "count" : 5 }
    

    在MongoDB的未来版本中,将有一个允许数组处理的 $slice 运算符,因此使用当前结构可以改为:

    { "$group": {
            "_id": {
                "evt": "$_id.evt",
                "time": { "$slice": [ "$_id.val", 3,1 ] }
            },
            "count": { "$sum": "$value.count" }
        }}
    

    这允许从数组中选择“第三个”索引元素,尽管这当然仍然会返回一个“数组”作为这样的元素:

    { "_id" : { "evt" : "click", "time" : [ "2" ] }, "count" : 2 }
    { "_id" : { "evt" : "click", "time" : [ "1" ] }, "count" : 5 }
    

    所以现在,如果你可以改变你的初始mapReduce输出,那就去做吧 . 要么是这里显示的形式,要么只是修改初始查询以获得您想要的最终结果 . 修改推荐的表单将至少允许 .aggregate() 命令工作,如此处的第二个示例所示 .

    如果没有,那么mapReduce仍然是目前写作的唯一方式,如“第一”示例所示 .

相关问题