2013-03-14 96 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

相关问题