如何使用MongoDB在多个文档之间获取数组的公共值?

时间:2014-03-27 13:50:08

标签: mongodb aggregation-framework

鉴于这些文件:

{
    values: [
        { attribute: 1 },
        { attribute: 2 },
        { attribute: 3 },
        { attribute: 4 },
    ]
},
{
    values: [
        { attribute: 2 },
        { attribute: 3 },
        { attribute: 4 },
    ]
},
{
    values: [
        { attribute: 2 },
        { attribute: 3 },
    ]
}

我正在尝试获取常见的“属性”值:

[ 2, 3 ]

我正在研究聚合器框架,但我发现没有什么可以真正满足我现在的需求。

我正在使用Mongo 2.4.6。

提前感谢您的回答!

修改

事实上,我的文档可以有重复的属性(但我想每个文档只计算一次)。

鉴于此数据

{
    values: [
        { attribute: 1 },
        { attribute: 2 },
        { attribute: 3 },
        { attribute: 3 },
        { attribute: 4 },
    ]
},
{
    values: [
        { attribute: 2 },
        { attribute: 2 },
        { attribute: 3 },
        { attribute: 4 },
    ]
},
{
    values: [
        { attribute: 2 },
        { attribute: 3 },
    ]
}

然后查询应返回:

{
        "result" : [
                {
                        "values" : 2
                },
                {
                        "values" : 3
                }
        ],
        "ok" : 1
}

Anand,您发布的查询将计算属性“2”4次,而不是3次。 我试图修改它,但这对我来说仍然很神秘......

提前致谢。

2 个答案:

答案 0 :(得分:1)

我不确定我是否完全理解你的问题,但我会对它进行一次拍摄。

如果您只想查找集合中每个文档中存在的属性,一种方法是在单独的查询中获取文档计数,然后使用如下所示的聚合查询。 / p>

db.collection.aggregate([
    // Unwind the values array
    { "$unwind" : "$values"}, 
    // Group by "values.attribute" and get the count for each
    { "$group" : {_id:"$values.attribute", count:{$sum:1}}}, 
    // Filter only those documents where count equals number of docs in the collection (i.e., 3)
    { "$match" : {count:3}}, // Replace 3 with document count
    // Project phase to make the result prettier and in the format you want
    { "$project" :{_id:0, values:"$_id"}}
])

这是您运行上述查询时获得的输出:

{
        "result" : [
                {
                        "values" : 3
                },
                {
                        "values" : 2
                }
        ],
        "ok" : 1
}

我不认为这可以在单个查询中实现(即,没有为文档计数运行单独的查询)。如果有更好的方法,可能有人会在这里发帖。

编辑:对于您所描述的边缘情况,您可以利用每个文档中存在的_id字段,并通过添加额外的$ group阶段在整个集合中是唯一的包括_id

db.collection.aggregate([
    // Unwind the values array
    { "$unwind" : "$values"}, 
    // Group by "_id" and "values.attribute" to pick just one element from the array per document
    { "$group" : {_id:{_id:"$_id", attrValue: "$values.attribute"}}},
    // Group by "values.attribute" and get the count for each
    { "$group" : {_id:"$_id.attrValue", count:{$sum:1}}}, 
    // Filter only those documents where count equals number of docs in the collection (i.e., 3)
    { "$match" : {count:3}}, // Replace 3 with document count
    // Project phase to make the result prettier and in the format you want
    { "$project" :{_id:0, values:"$_id"}}
])

答案 1 :(得分:0)

我们已经提出了这个解决方案:

db.collection.aggregate(
    { $project: { "values.attribute": 1} },
    { $unwind: "$values" },
    { $group: {
        _id : "$_id",
        attribute: {$addToSet:"$values.attribute"}                                   
      }
    },
    { $unwind: "$attribute" },
    { $group: { _id: "$attribute", count: { $sum: 1 } } },
    { "$match" : {count:3}},
)

并且addToSet似乎比复合键上的组更快。

非常感谢Anand,非常感谢您的帮助!