繁体   English   中英

如何将异构数组合并到MongoDb中的单个文档?

[英]How to merge a heterogeneous array to a single document in MongoDb?

我网站的MongoDb为每个用户存储一个文档。 每位用户在访问期间都会回答几个问卷表格。 表单存储在一个数组中,但由于文档不重叠,因此单个文档就足够了。 为了进行分析,我希望制作一张包含所有表格所有答案的平面表。

考虑以下数据结构:

{
    "USER_SESSION_ID": 456,
    "forms": [
        {
            "age": 21,
            "gender": "m"
        },
        {
            "job": "Student",
            "years_on_job": "12"
        },
        {
            "Hobby": "Hiking",
            "Twitter": "@my_account"
        }
    ]
},
{
    "USER_SESSION_ID": 678,
    "forms": [
        {
            "age": 46,
            "gender": "f"
        },
        {
            "job": "Bodyguard",
            "years_on_job": "2"
        },
        {
            "Hobby": "Skiing",
            "Twitter": "@bodyguard"
        }
    ]
}

表单文档看起来都不同,没有冲突的字段,所以我想合并它们,产生一个表格式的扁平结构,如下所示:

{ 'USER_SESSION_ID': 456, 'age': 21, 'gender': 'm', 'job': 'Student', ... 'Twitter': '@my_account' }
{ 'USER_SESSION_ID': 678, 'age': 46, 'gender': 'f', 'job': 'Bodyguard',  ... 'Twitter': '@bodyguard' }

使用Python,这是一个明智的选择,如下所示:

for session in sessions:          # Iterate all docs
    for form in session['forms']: # Iterate all children
        session.update(form)      # Integrate to parent doc
    del session['forms']          # Remove nested child

在MongoDb中,我发现这很难实现。 我正在尝试使用聚合管道,我认为它应该适用于此。

到目前为止,我通过展开我的数据结构来帮助自己,如下所示:

db.sessions.aggregate(
    {
        '$unwind': '$forms'
    },
    { 
        '$project': {
            'USER_SESSION_ID': true,
            'forms': true
        }
    },
    {
        '$group': {
            '_id': '$USER_SESSION_ID',
            'forms': <magic?!>
        }
    }
)

在展开阶段,我创建一个包含每个孩子的父母数据的文档。 这应该大致相当于我的python代码中的double-for循环。 然而,我觉得我在概念上缺少的是分组时的“合并”累加器。 在python中,这是用dict.update() ,在underscore.js中它将是_.extend(destination, *sources)

我如何在MongoDB中实现这一目标?

尝试使用find()游标的嵌套forEach()方法调用来迭代游标结果并使用Object.keys()获取forms数组中元素的对象键:

db.sessions.find().forEach(function (doc){
    doc.forms.forEach(function (e){ 
        var keys = Object.keys(e); 
        keys.forEach(function(key){ doc[key] = e[key] });
    });
    delete doc.forms;
    db.sessions.save(doc);
});

我玩聚合管道已经很久了,直到我尝试了mapReduce命令。 这就是我想出的:

db.sessions.mapReduce(
    function () {
        var merged = {};
        this.forms.forEach(function (form) {
            for(var key in form) {
                merged[key] = form[key];
            }
        });
        emit(this.USER_SESSION_ID, merged);
    },
    function () {},
    {
         "out": {"inline": true}
    }
)

映射步骤组合了元素,因为没有单个$ merge操作符可用作聚合管道步骤。 需要空的reduce函数。 out要么写入不同的集合,要么只返回结果(内联,我在这里做的)。

它看起来很像chridam在他的回答中显示的方法,但实际上使用投影。 他的版本更接近我的python代码的工作方式,但是对于我正在尝试做的事情,投影很好并且不会改变原始集合。 请注意,python代码可以做到这一点,但不是chaning输入集合是非常有用的!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM