2014-10-31 24 views
9

是否可以將位置操作符'$'與深度嵌套文檔數組上的查詢結合使用?

考慮下面的嵌套的文件定義一個「用戶」:

{ 
    username: 'test', 
    kingdoms: [ 

     { 
      buildings: [ 

       { 
        type: 'castle' 

       }, 
       { 
        type: 'treasury' 
       }, 

       ... 

      ] 

     }, 

     ... 

    ] 
} 

我們想例如返回「城堡」爲特定用戶在一種形式:

{ 
    kingdoms: [{ 

     buildings: [{ 

      type: 'castle' 

     }] 

    }] 
} 

因爲你不能使用$操作符兩次(https://jira.mongodb.org/browse/server-831)我知道,我也無法查詢特定的國度,所以我試圖寫的第n個王國的發現聲明。

在更新深度嵌套的子文檔(Mongodb update deeply nested subdocument)時,這似乎很有意義,但我在查找查詢方面的成功較少。

我可以查詢返回的第一個王國的建築:

db.users.findOne(
    { username: 'test' }, 
    { kingdoms: {$slice: [0, 1]}, 'kingdom.buildings': 1 } 
); 

但這返回所有那個王國的建築。

下面我試圖位置操作的單級例子這樣的查詢:

db.users.findOne(
    { username: 'test', 'kingdoms.buildings.type': 'castle' }, 
    { kingdoms: {$slice: [n, 1]}, 'kingdom.buildings.$': 1 } 
); 

,以便形式:

db.collection.find({ <array.field>: <value> ...}, { "<array>.$": 1 }) 

如文檔http://docs.mongodb.org/manual/reference/operator/projection/positional/#proj.S描述

但是這個失敗,出現錯誤:

Positional operator does not match the query specifier 

推測是因爲kingdoms.buildings不被視爲一個數組。我也試着kingdoms.0.buildings

這是令人困惑,因爲這似乎是(根據Mongodb update deeply nested subdocument

有我剛剛得到了語法錯誤的更新工作,或者這是不是支持?如果有,有什麼辦法可以實現類似的目標?

回答

3

您從

db.users.findOne(
    { username: 'test', 'kingdoms.buildings.type': 'castle' }, 
    { kingdoms: {$slice: [n, 1]}, 'kingdom.buildings.$': 1 } 
); 

得到一個錯誤,因爲有一個拼寫錯誤( 「kingdom.buildings。$」 應爲 「國小號 .buildings。$」)。
但是,這種方式不能實現你所期望的。
$始終瞄準王國kingdoms.buildings路徑 - 第一陣列。

這是一種應該能夠解決問題的方法。
(V2。6+所需)

db.c.aggregate([ { 
    $match : { 
     username : 'test', 
     'kingdoms.buildings.type' : 'castle' 
    } 
}, { 
    $project : { 
     _id : 0, 
     kingdoms : 1 
    } 
}, { 
    $redact : { 
     $cond : { 
      "if" : { 
       $or : [ { 
        $gt : [ "$kingdoms", [] ] 
       }, { 
        $gt : [ "$buildings", [] ] 
       }, { 
        $eq : [ "$type", "castle" ] 
       } ] 
      }, 
      "then" : "$$DESCEND", 
      "else" : "$$PRUNE" 
     } 
    } 
} ]).pretty(); 

要僅保留的王國的第一要素,

db.c.aggregate([ { 
    $match : { 
     username : 'test', 
     'kingdoms.buildings.type' : 'castle' 
    } 
}, { 
    $redact : { 
     $cond : { 
      "if" : { 
       $or : [ { 
        $gt : [ "$kingdoms", [] ] 
       }, { 
        $gt : [ "$buildings", [] ] 
       }, { 
        $eq : [ "$type", "castle" ] 
       } ] 
      }, 
      "then" : "$$DESCEND", 
      "else" : "$$PRUNE" 
     } 
    } 
}, { 
    $unwind : "$kingdoms" 
}, { 
    $group : { 
     _id : "$_id", 
     kingdom : { 
      $first : "$kingdoms" 
     } 
    } 
}, { 
    $group : { 
     _id : "$_id", 
     kingdoms : { 
      $push : "$kingdom" 
     } 
    } 
}, { 
    $project : { 
     _id : 0, 
     kingdoms : 1 
    } 
} ]).pretty(); 
+0

謝謝精靈 - 它可以指定不同層次的簡單層次結構的這些條件呢?說一個王國有一個關鍵的類型:「城堡」,那麼即使它不符合建築物的類型,它也會$$消除。 (或類似的情況下,如果這個例子無效) – Hugheth 2014-11-02 10:03:39

+0

@Hugheth,不太清楚你的觀點。你可以發表文件來描述它嗎? – Wizard 2014-11-02 10:07:48

+0

這個答案和_my_用例之間的主要區別在於它不會過濾到單個王國 - 而是返回用戶的所有城堡 – Hugheth 2014-11-02 10:13:01