基于多个连接的MongoDB查询

时间:2017-07-18 18:18:25

标签: javascript mongodb mongoose

关于连接和MongoDB有很多问题,但是很多问题已经过时了,考虑到Mongo 3.x之后没有采用功能的答案。我的问题是你如何查询具有链接元素条件的表?

这是一个极其简化的例子

const Person =  new mongoose.Schema({
  gender: String
});

const Dog =  new mongoose.Schema({
  breed: String
});

const Team =  new mongoose.Schema({
  trainer: {
        type: mongoose.Schema.ObjectId,
        ref: 'Person'
  },
  members: [{
        type: mongoose.Schema.ObjectId,
        ref: 'Dog'
  }]
})

想象一下,这已经在制作中,并且不可能改变架构。

如何检索所有至少有一只“Poodle”品种的成员狗以及,其中培训师的性别是“男性”

1 个答案:

答案 0 :(得分:2)

以不同的方式提出问题:如何在mongoDB中加入两个以上的集合?

假设您针对模型名称的集合名称为dogsteamspeople(Mongoose复数约定),以下是实现所需结果的方法之一:

Dog.aggregate([{
        $match: {
            breed: "Poodle"
        }
    },
    {
        $lookup: {
            from: "teams",
            localField: "_id",
            foreignField: "members",
            as: "team"
        }
    },
    {
        $unwind: "$team"
    },
    {
        $lookup: {
            from: "people",
            localField: "team.trainer",
            foreignField: "_id",
            as: "trainer"
        }
    },

    {
        $unwind: "$trainer"
    },

    {
        $match: {
            "trainer.gender": "male"
        }
    },
    {
        $project: {
            breed: 1,
            trainer: 1,
            team: {
                _id: 1
            }
        }
    }
], function(err, teams) {
   console.log(teams)
});

在汇总管道中,我们执行以下操作:

  1. Dog为起点并匹配品种
  2. 然后使用$lookup将结果与团队一起加入,并获取包含成员引用的团队" Poodle"
  3. 2的结果集包含团队数组(您可以删除$ lookup下面的所有步骤以查看结果的状态)。要将此数组拆分为另一个文档,我们使用$unwind运算符(说三个元素的团队将成为三个文档,其中父字段全部被复制)
  4. 在新结果集上,再次应用$ lookup,这次加入人员。这使人们成为教练阵列。
  5. 再次展开分裂训练师
  6. 匹配trainer.gender"男"
  7. 的结果集
  8. $project(选择)您需要的字段
  9. 最终结果将如下所示:

    {
        "_id" : ObjectId("596e5500b5174986059958a8"),
        "breed" : "Poodle",
        "team" : {
            "_id" : ObjectId("596e564fb5174986059958de")
        },
        "trainer" : {
            "_id" : ObjectId("596e54bfb51749860599589c"),
            "gender" : "male"
        }
    }
    
    
    {
        "_id" : ObjectId("596e5500b5174986059958a8"),
        "breed" : "Poodle",
        "team" : {
            "_id" : ObjectId("596e564fb5174986059958e6")
        },
        "trainer" : {
            "_id" : ObjectId("596e54bfb51749860599589c"),
            "gender" : "male"
        }
    }
    
    
    {
        "_id" : ObjectId("596e5500b5174986059958b2"),
        "breed" : "Poodle",
        "team" : {
            "_id" : ObjectId("596e564fb5174986059958de")
        },
        "trainer" : {
            "_id" : ObjectId("596e54bfb51749860599589c"),
            "gender" : "male"
        }
    }
    
    
    {
        "_id" : ObjectId("596e5500b5174986059958b2"),
        "breed" : "Poodle",
        "team" : {
            "_id" : ObjectId("596e564fb5174986059958e6")
        },
        "trainer" : {
            "_id" : ObjectId("596e54bfb51749860599589c"),
            "gender" : "male"
        }
    }
    

    基本上,我们搜索了Dog并加入并匹配了更多的收藏品。最终文档中的根_id是狗而不是团队的_id,因此从技术上讲,结果集包含包含团队和培训师的狗,但您可以将其视为"团队"文献。你可以反过来,从Person开始到达Dog

    此外,结果的结构并不完美。您可能想要一个结构良好的格式,因为人口与包含嵌入式teamstrainer的{​​{1}}类似。通过聚合管道中的一些调整,我确信可以实现良好的结构。

    最后,这与Mongoose人口不同,这是另一个答案。主要的区别在于,在这种情况下,您已将完成所需文档的任务委派给mongo服务器,显然是一气呵成。在人口中,同样需要过多的客户端处理和对db的许多请求。但是$lookup适用于未加密的收集,在这种情况下,您可能更喜欢人口或考虑this回答。