【问题标题】:$elemMatch on nested Embedded Document嵌套嵌入式文档上的 $elemMatch
【发布时间】:2017-10-22 17:28:50
【问题描述】:

在我的 Mongoose 中,我有以下模式 ..

const RobotSchema = new Schema({
  name: { type: String },
});

const RobotClassRoomSchema = new Schema({
  robot: { type: Schema.Types.ObjectId, ref: 'Robot' },
});

const ClassRoomSchema = new Schema({
  robots: [{ type: Schema.Types.ObjectId, ref: 'RobotClassRoom' }],
});

如何验证我的ClassRoom 中是否有 ID 为 59226c258a4a131c6828841eRobot

尝试

但我有机器人..

【问题讨论】:

    标签: mongodb mongoose mongodb-query aggregation-framework mongoose-schema


    【解决方案1】:

    您的架构正在使用对另一个集合中对象的引用。由于 MongoDB 本身实际上一次只查询一个集合,因此您正在查询的集合中不存在“引用”数据。

    该集合所知道的只是它有一个包含 ObjectId 值的“数组”字段,这实际上对 MongoDB 本身没有任何意义,唯一知道 ObjectId 值实际引用的位置是在您的“客户端代码”中的猫鼬模式。所以服务器当然对此一无所知。

    使用 MongoDB

    在 MongoDB 的现代版本(至少 3.2)中,您可以使用 $lookup 聚合管道运算符在服务器上执行“加入”(或者更确切地说是“查找”)。此语法允许您为在数组中找到的值定义要“查找”的集合,结果将从引用的集合中检索匹配的对象到您的文档中。

    数组的问题在于,根据您的版本,可能需要使用$unwind 处理才能进行匹配。 MongoDB 3.2 要求数组是“未缠绕的”,但在 MongoDB 3.4 中你应该能够做到这一点:

    db.getCollection('classrooms').aggregate([
        { "$lookup": {
            "from": "robotclassrooms",
            "localField": "robots",
            "foreignField": "_id"
            "as": "robots"
        }},
        { "$redact": {
          "$cond": {
            "if": {
               "$anyElementTrue": {
                 "$map": {
                   "input": "$robots",
                   "as": "r",
                   "in": { "$eq": [ "$$r.robot", ObjectId("59226c258a4a131c6828841e") ] }
                 }
               }
            },
            "then": "$$KEEP",
            "else": "$$PRUNE"
          }
        }}
    ])
    

    并使用$unwind

    db.getCollection('classrooms').aggregate([
        { "$unwind": "$robots" },
        { "$lookup": {
            "from": "robotclassrooms",
            "localField": "robots",
            "foreignField": "_id"
            "as": "robots"
        }},
        { "$unwind": "$robots" },
        { "$group": {
          "_id": "_id",
          "updatedAt": { "$first": "$updatedAt" },
          "createdAt": { "$first": "$createdAt" },
          "code": { "$first": "$code" },
          "createdBy": { "$first": "$createdBy" },
          "robots": { "$push": "$robots" }
        }},
        { "$redact": {
          "$cond": {
            "if": {
               "$anyElementTrue": {
                 "$map": {
                   "input": "$robots",
                   "as": "r",
                   "in": { "$eq": [ "$$r.robot", ObjectId("59226c258a4a131c6828841e") ] }
                 }
               }
            },
            "then": "$$KEEP",
            "else": "$$PRUNE"
          }
        }}
    ])
    

    注意$unwind 在这种情况下的双重用法,因为 MongoDB 3.2 不仅要求在处理之前解开数组,而且结果也是一个“数组”,即使它可能只匹配一个值。 $lookup 运算符不区分单个匹配和多个匹配,因为 always 返回一个数组。在任何版本中。

    因此还使用$group 进行后续处理以恢复文档结构。我不会在此处复制您的所有文档属性,而是复制其中的足够多的内容,以便您了解一般的“要点”。


    使用猫鼬

    实际要求“服务器”执行此操作的替代过程是使用“mongoose”将代码变白以执行“填充查询”。这是“连接”的“客户端模拟”,其中实际发生的是向数据库发出的多个请求,结果在代码中“合并”。

    当然,作为“客户端”操作,这有一些限制。最值得注意的是,服务器将返回所有结果,无论它们是否符合您的条件,并且条件在“客户端代码”中进行评估。

    Classroom.find()
      .populate({
        path: 'robots',
        match: { 'robot': '59226c258a4a131c6828841e' }
      }).exec(function(err,docs) {
         // docs has all classrooms but some robots arrays will be empty
    
      });
    

    所以这里的问题是总是返回evert“classrooms”文档,即使条件是只从“robotsclassrooms”中“填充”那些与给定查询条件匹配的对象。

    结果是“教室”对象具有未满足查询的“空数组”。然后,您需要解决这个问题,但是“过滤”生成的课堂文档以查找空数组,如下所示:

    Classroom.find()
      .populate({
        path: 'robots',
        match: { 'robot': '59226c258a4a131c6828841e' }
      }).exec(function(err,docs) {
          docs = docs.filter(function(doc) { return doc.robots.length > 0 })
          // Now docs is only matching documents containing the requested ObjectId value
      });
    

    结论

    这些基本上是您的选择,您使用哪一种取决于您的方法,当然还有可用的服务器功能。

    请注意,这些过程是“查找与数组中的元素匹配的文档”,这与“仅返回数组的匹配元素”不同。有不同的技术来“建立”这里的过程,但基本上也涉及“过滤”数组内容。例如,在聚合框架中,您可以应用 $filter 作为运算符来执行该功能。

    注意事项:

    有关示例中使用的其他运算符,另请参阅 Aggregation Operators 的完整列表。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-09-18
    • 1970-01-01
    • 2014-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多