Mongodb,$lookup聚合查询

15

有两个集合,标签和人。

标签模型:

{
  en: String,
  sv: String
}

人物模型:

{
  name: String,
  projects: [
    title: String,
    tags: [
      {
        type: Schema.ObjectId,
        ref: 'tag'
      }
    ]
  ]

}

我想查询返回在person模型中使用的所有标签。 所有文档。

类似这样的东西

var query = mongoose.model('tag').find({...});

或者我应该以某种方式使用综合方法来解决这个问题吗?


你想要整个应用程序中的所有标签还是某个特定人的所有标签?因为如果你想列出所有标签而不涉及到人,你可以直接在标签集合上查询。 - Puneet Singh
我想要整个应用程序中的标签。项目中所有人的project.tags中存在的所有标签。 - Joe
2个回答

52
对于任何特定的个人文档,您可以使用 populate() 函数,例如:
var query = mongoose.model("person").find({ "name": "foo" }).populate("projects.tags");

如果您想搜索任何拥有'MongoDB'或'Node JS'标签的人,您可以在populate()函数重载中包含查询选项:

var query = mongoose.model("person").find({ "name": "foo" }).populate({
    "path": "projects.tags",
    "match": { "en": { "$in": ["MongoDB", "Node JS"] } }
});

如果您想获取所有人员在“project.tags”中存在的标签,则聚合框架是实现的方法。考虑在person集合上运行此管道,并使用$lookup操作符对tags集合进行左连接:
mongoose.model('person').aggregate([
    { "$unwind": "$projects" },
    { "$unwind": "$projects.tags" },
    {
        "$lookup": {
            "from": "tags",
            "localField": "projects.tags",
            "foreignField": "_id",
            "as": "resultingTagsArray"
        }
    },
    { "$unwind": "$resultingTagsArray" },
    {
        "$group": {
            "_id": null,
            "allTags": { "$addToSet": "$resultingTagsArray" },
            "count": { "$sum": 1 }
        }
    }
 ]).exec(function(err, results){
    console.log(results);
 })

对于任何特定的人,首先应用一个$match管道来过滤文档:

mongoose.model('person').aggregate([
    { "$match": { "name": "foo" } },
    { "$unwind": "$projects" },
    { "$unwind": "$projects.tags" },
    {
        "$lookup": {
            "from": "tags",
            "localField": "projects.tags",
            "foreignField": "_id",
            "as": "resultingTagsArray"
        }
    },
    { "$unwind": "$resultingTagsArray" },
    {
        "$group": {
            "_id": null,
            "allTags": { "$addToSet": "$resultingTagsArray" },
            "count": { "$sum": 1 }
        }
    }
 ]).exec(function(err, results){
    console.log(results);
 })

如果您使用的是MongoDB版本>= 2.6或<= 3.0,并且不支持$lookup运算符,则另一种解决方法是将聚合结果填充为:
mongoose.model('person').aggregate([
    { "$unwind": "$projects" },
    { "$unwind": "$projects.tags" },    
    {
        "$group": {
            "_id": null,
            "allTags": { "$addToSet": "$projects.tags" }
        }
    }
 ], function(err, result) {
    mongoose.model('person')
    .populate(result, { "path": "allTags" }, function(err, results) {
        if (err) throw err;
        console.log(JSON.stringify(results, undefined, 4 ));
    });
});

抱歉!我在帖子中犯了一个错误。projects不是一个对象,而是一个数组。我已经更新了问题。你能再看一下吗? - Joe
你可以通过在更新的答案中添加另一个 { "$unwind": "$projects" } pipeline 来解决这个问题。 - chridam
1
非常强大的东西。总有一天得学习这个聚合框架。谢谢! - Joe
1
感谢您提出populate()选项。 - Shani Kehati

0
如果您正在使用MongoDb 3.2版本,则可以使用$lookup执行左外连接。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接