具有多个阵列的MongoDB聚合

时间:2014-11-06 13:08:17

标签: mongodb mongodb-query aggregation-framework

我将以下两个项目插入到“框架”集合中:

frame1 = {
          "number": 1,

          "hobjects": [ { "htype": 1, "weight": 50 },
                        { "htype": 2, "weight": 220 },
                        { "htype": 2, "weight": 290 },
                        { "htype": 3, "weight": 450 } ],

          "sobjects": [ { "stype": 1, "size": 10.0 },
                        { "stype": 2, "size": 5.1 },
                        { "stype": 2, "size": 6.5 } ],
          }

frame2 = {
          "number": 2,

          "hobjects": [ { "htype": 1, "weight": 61 },
                        { "htype": 2, "weight": 210 },
                        { "htype": 2, "weight": 250 } ],

          "sobjects": [ { "stype": 1, "size": 12.1 },
                        { "stype": 2, "size": 4.9 },
                        { "stype": 2, "size": 6.2 },
                        { "stype": 2, "size": 5.7 } ],
          }

frames.insert(frame1)
frames.insert(frame2)

现在我想对部分帧数据进行查询:

query = { "hobjects.htype": 3, "sobjects.stype": 2 }
db.frames.find(query)

导致:

{u'_id': ObjectId('545b6ea7b9ad9a03462d743b'), u'hobjects': [{u'htype': 1, u'weight': 50}, {u'htype': 2, u'weight': 220}, {u'htype': 2, u'weight': 290}, {u'htype': 3, u'weight': 450}], u'number': 1, u'sobjects': [{u'stype': 1, u'size': 10.0}, {u'stype': 2, u'size': 5.1}, {u'stype': 2, u'size': 6.5}]}

并不是我想要的。我希望根据查询过滤集合,以便我得到以下结果:

{u'_id': ObjectId('545b6ea7b9ad9a03462d743b'), u'hobjects': [{u'htype': 3, u'weight': 450}], u'number': 1, u'sobjects': [{u'stype': 2, u'size': 5.1}, {u'stype': 2, u'size': 6.5}]}

我找到的唯一解决方案涉及每个集合的展开和分组聚合:

query = { "hobjects.htype": 3, "sobjects.stype": 2 }
db.frames.aggregate([
    { "$match": query },
    { "$unwind": "$hobjects" },
    { "$match": dict((key, value) for key, value in query.iteritems() if "hobjects." in key) },
    { "$group": { "_id": "$_id", "number": { "$first": "$number" } , "hobjects": { "$push": "$hobjects" }, "sobjects": { "$first": "$sobjects" } } },
    { "$unwind": "$sobjects" },
    { "$match": dict((key, value) for key, value in query.iteritems() if "sobjects." in key) },
    { "$group": { "_id": "$_id", "number": { "$first": "$number" } , "hobjects": { "$first": "$hobjects" }, "sobjects": { "$push": "$sobjects" } } },
    ])

我猜这不是一种非常有效和灵活的查询方式。我想知道是否还有其他选择?

2 个答案:

答案 0 :(得分:0)

如果您的服务器是MongoDB 2.6或更高版本,那么您可以随时执行此操作:

db.frames.aggregate([

    // Still helps to match the documents by conditions to filter
    { "$match": {
        "hobjects.htype": 3, "sobjects.stype": 2 
    }},

    // Now filter inline using $map and $setDifference
    { "$project": {
        "number": 1,
        "hobjects": {
            "$setDifference": [
                { "$map": {
                    "input": "$hobjects",
                    "as": "el",
                    "in": {
                        "$cond": [
                            { "$eq": [ "$$el.htype", 3 ] },
                            "$$el",
                            false
                        ]
                    }
                }},
                [false]
            ]
        },
        "sobjects": {
            "$setDifference": [
                { "$map": {
                    "input": "$sobjects",
                    "as": "el",
                    "in": {
                        "$cond": [
                            { "$eq": [ "$$el.stype", 2 ] },
                            "$$el",
                            false
                        ]
                    }
                }},
                [false]
            ]
        } 
    }}
])

这里的问题是基本投影和$elemMatch之类的东西目前只能匹配与条件匹配的数组中的第一个元素。所以要做更多的事情,你需要某种形式的高级操作,这些操作只适用于聚合框架等。

$setDiffence$map运算符会为您提供"内联"处理数组的方式实际上是#34; set"在单个文件中。这比使用$unwind更有效,特别是在涉及大型数组的情况下。

这里的JavaScript符号我知道(主要是在评论中),但它与python基本相同。

答案 1 :(得分:0)

以下聚合可能会对您有所帮助

db.frames.aggregate({"$unwind":"$hobjects"},{"$unwind":"$sobjects"},{"$match":{"hobjects.htype": 3, "sobjects.stype": 2}},{"$group":{"_id":"$_id","u'hobjects":{"$first":"$hobjects"},"u'number":{"$first":"$number"},"u'sobjects":{"$push":"$sobjects"}}})