2014-11-06 87 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