2014-11-06 81 views
0

我有以下两个项目插入到集合中“帧”:MongoDB的聚集与多个阵列

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" } } }, 
    ]) 

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

回答

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运营商给你一个“内联”的方式来处理数组和实际上在单个文档中的“集”。这比使用$unwind更有效,特别是在涉及大型数组的情况下。

JavaScript表示法我知道(主要在注释中),但它基本上与python相同。

+0

谢谢@NeilLunn!看起来像是一个最佳选择。会试试看! – 2014-11-06 14:12:52

+0

测试了解决方案,它适用于我的示例。尽管我更喜欢用对整个对象数据进行任意查询的更一般的解决方案。 – 2014-11-07 14:41:22

+0

@Neil Lunn - 你可以请帮忙写这个查询在java驱动程序。谢谢 – PVH 2015-03-19 05:20:07

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"}}}) 
+0

展开多个数组的笛卡尔问题。意思是你刚刚复制了其中一个元素 – 2014-11-06 13:35:09

+0

@NeilLunn感谢我的建议我将致力于解决这个问题 – Yogesh 2014-11-06 13:38:56

+0

继续。这个问题以前已经解决了很多次,我确定我自己已经多次回答过它。但这不是OP要求的。他们的代码很好,他们只是要求一些更优化的东西,并且可能的解释是为什么他们正在做的事情是必要的。 – 2014-11-06 13:42:26