我正在使用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'}]}
选择的album.tracks只包含一个标题为'track0'的轨道元素(但应该同时包含'track0','track1'):
{tracks:[{title: 'track0', language: 'en'}, {title: 'track1', language: 'en'}]}
我做错了什么?
答案 0 :(得分:12)
就像@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
}