MongoDB MapReduce - 是否有聚合替代方案?

时间:2014-06-19 17:35:23

标签: mongodb mapreduce aggregation-framework

我使用类似这样的模式(一些成员编辑)获得了包含文档的集合:

{
    "_id" : ObjectId("539f41a95d1887b57ab78bea"),
    "answers" : {
        "ratings" : {
            "positivity" : [ 
                2, 
                3, 
                5
            ],
            "activity" : [ 
                4, 
                4, 
                3
            ],
    },
    "media" : [ 
        ObjectId("537ea185df872bb71e4df270"), 
        ObjectId("537ea185df872bb71e4df275"), 
        ObjectId("537ea185df872bb71e4df272")
    ]
}

在此架构中,第一个,第二个和第三个positivity评级分别对应media数组中的第一个,第二个和第三个条目。 activity评级也是如此。我需要针对集合中所有文档中关联的positivity对象计算activitymedia评级的统计信息。现在,我正在使用MapReduce执行此操作。但是,我希望通过Aggregation Pipeline来实现这一目标。

理想情况下,我想同时$unwind mediaanswers.ratings.positivityanswers.ratings.activity数组,以便我最终得到以下结果:基于前一个例子的三个文件:

[
    {
        "_id" : ObjectId("539f41a95d1887b57ab78bea"),
        "answers" : {
            "ratings" : {
                "positivity" : 2,
                "activity" : 4
            }
        },
        "media" : ObjectId("537ea185df872bb71e4df270")
    },
    {
        "_id" : ObjectId("539f41a95d1887b57ab78bea"),
        "answers" : {
            "ratings" : {
                "positivity" : 3
                "activity" : 4
            }
        },
        "media" : ObjectId("537ea185df872bb71e4df275")
    },
    {
        "_id" : ObjectId("539f41a95d1887b57ab78bea"),
        "answers" : {
            "ratings" : {
                "positivity" : 5
                "activity" : 3
            }
        },
        "media" : ObjectId("537ea185df872bb71e4df272")
    }
]

有没有办法实现这个目标?

1 个答案:

答案 0 :(得分:1)

当前的聚合框架不允许您这样做。能够展开多个已知相同大小的数组并为每个数据创建一个文档,这将是一个很好的功能。

如果要使用聚合框架,则需要稍微更改架构。例如,采用以下文档架构:

{
    "_id" : ObjectId("539f41a95d1887b57ab78bea"),
    "answers" : {
        "ratings" : {
            "positivity" : [ 
                {k:1, v:2}, 
                {k:2, v:3}, 
                {k:3, v:5}
            ],
            "activity" : [ 
                {k:1, v:4}, 
                {k:2, v:4}, 
                {k:3, v:3}
            ],
    }},
    "media" : [ 
        {k:1, v:ObjectId("537ea185df872bb71e4df270")}, 
        {k:2, v:ObjectId("537ea185df872bb71e4df275")}, 
        {k:3, v:ObjectId("537ea185df872bb71e4df272")}
    ]
}

通过执行此操作,您实际上是将索引添加到数组内的对象。在此之后,只需要解开所有数组并在键上进行匹配。

db.test.aggregate([{$unwind:"$media"},
{$unwind:"$answers.ratings.positivity"},
{$unwind:"$answers.ratings.activity"},
{$project:{"media":1, "answers.ratings.positivity":1,"answers.ratings.activity":1,
    include:{$and:[
                  {$eq:["$media.k", "$answers.ratings.positivity.k"]},
                  {$eq:["$media.k", "$answers.ratings.activity.k"]}
            ]}}
},
{$match:{include:true}}])

输出是:

[ 
        {
            "_id" : ObjectId("539f41a95d1887b57ab78bea"),
            "answers" : {
                "ratings" : {
                    "positivity" : {
                        "k" : 1,
                        "v" : 2
                    },
                    "activity" : {
                        "k" : 1,
                        "v" : 4
                    }
                }
            },
            "media" : {
                "k" : 1,
                "v" : ObjectId("537ea185df872bb71e4df270")
            },
            "include" : true
        }, 
        {
            "_id" : ObjectId("539f41a95d1887b57ab78bea"),
            "answers" : {
                "ratings" : {
                    "positivity" : {
                        "k" : 2,
                        "v" : 3
                    },
                    "activity" : {
                        "k" : 2,
                        "v" : 4
                    }
                }
            },
            "media" : {
                "k" : 2,
                "v" : ObjectId("537ea185df872bb71e4df275")
            },
            "include" : true
        }, 
        {
            "_id" : ObjectId("539f41a95d1887b57ab78bea"),
            "answers" : {
                "ratings" : {
                    "positivity" : {
                        "k" : 3,
                        "v" : 5
                    },
                    "activity" : {
                        "k" : 3,
                        "v" : 3
                    }
                }
            },
            "media" : {
                "k" : 3,
                "v" : ObjectId("537ea185df872bb71e4df272")
            },
            "include" : true
        }
    ]

这样做会产生大量额外的文档开销,并且可能比当前的MapReduce实现慢。您需要运行测试来检查这一点。为此所需的计算将基于这三个阵列的大小以立方方式增长。这也应该牢记在心。