8

我正在使用 mongoose.js 对 mongodb 进行查询,但我认为我的问题并非特定于 mongoose.js。

假设我在收藏中只有一条记录:

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

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

我想选择语言字段等于“en”的所有曲目,所以我尝试了两种变体:

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'}]}

选定的专辑曲目仅包含一个标题为“track0”的曲目元素(但应该同时存在“track0”和“track1”):

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

我究竟做错了什么?

4

1 回答 1

17

就像@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
}
于 2013-03-14T19:24:33.790 回答