2013-03-14 98 views
5

我正在使用mongoose.js對mongodb執行查詢,但我認爲我的問題不是特定於mongoose.js。MongoDB:選擇與子集合相匹配的元素

說我有收集只有一條記錄:

var album = new Album({ 
    tracks: [{ 
     title: 'track0', 
     language: 'en', 

    },{ 
     title: 'track1', 
     language: 'en', 
    },{ 
     title: 'track2', 
     language: 'es', 
    }] 
}) 

我想選擇與語言字段等於「恩」所有的曲目,所以我嘗試兩種型號:

Album.find({'tracks.language':'en'}, {'tracks.$': 1}, function(err, albums){ 

和綁定到與elemMatch投影相同的東西:

Album.find({}, {tracks: {$elemMatch: {'language': 'en'}}}, function(err, albums){ 

在這兩種情況下我得到了同樣的結果:

{tracks:[{title: 'track0', language: 'en'}]} 

選擇album.tracks包含標題爲 'track0' 只有一個線路元件(但應該有兩個 'track0', 'TRACK1'):

{tracks:[{title: 'track0', language: 'en'}, {title: 'track1', language: 'en'}]} 

我在做什麼錯?

+4

你沒有做錯什麼,這只是這些查詢的工作方式 - 他們只包括第一個匹配的元素。我認爲你需要使用聚合框架來做你想做的事情。 – JohnnyHK 2013-03-14 17:27:06

+0

謝謝,你能給我更特別的提示或什麼嗎? – WHITECOLOR 2013-03-14 19:03:24

回答

11

就像@JohnnyHK已經說過的那樣,您必須使用聚合框架才能實現該目標,因爲$$elemMatch只返回第一個匹配項。

方法如下:

db.Album.aggregate(
    // This is optional. It might make your query faster if you have 
    // many albums that don't have any English tracks. Take a larger 
    // collection and measure the difference. YMMV. 
    { $match: {tracks: {$elemMatch: {'language': 'en'}} } }, 

    // This will create an 'intermediate' document for each track 
    { $unwind : "$tracks" }, 

    // Now filter out the documents that don't contain an English track 
    // Note: at this point, documents' 'tracks' element is not an array 
    { $match: { "tracks.language" : "en" } }, 

    // Re-group so the output documents have the same structure, ie. 
    // make tracks a subdocument/array again 
    { $group : { _id : "$_id", tracks : { $addToSet : "$tracks" } }} 
); 

你可能想嘗試,聚集查詢,只有第一個表達式,然後通過行添加表情網上看到的輸出如何變化。瞭解$unwind如何創建後期使用$group$addToSet重新合併的中間文檔特別重要。

結果:

> db.Album.aggregate(
    { $match: {tracks: {$elemMatch: {'language': 'en'}} } }, 
    { $unwind : "$tracks" }, 
    { $match: { "tracks.language" : "en" } }, 
    { $group : { _id : "$_id", tracks : { $addToSet : "$tracks" } }} ); 
{ 
    "result" : [ 
      { 
        "_id" : ObjectId("514217b1c99766f4d210c20b"), 
        "tracks" : [ 
          { 
            "title" : "track1", 
            "language" : "en" 
          }, 
          { 
            "title" : "track0", 
            "language" : "en" 
          } 
        ] 
      } 
    ], 
    "ok" : 1 
} 
+0

非常感謝! – WHITECOLOR 2013-03-14 20:25:11

+0

還有一個相關的問題,如果你不介意如何限制選定的曲目有olny標題字段(沒有其他類似的語言字段)。 – WHITECOLOR 2013-03-14 20:31:22

+0

@mnemosyn我在我的相關問題上試過這個解決方案,它工作。現在我必須回去理解它! – carbontax 2013-05-05 16:34:21

相關問題