如何在嵌入式mongo文档中选择特定属性

时间:2018-11-13 22:38:01

标签: mongodb

我有一个类似于以下结构的mongo文档:

   {
  id: '111eef8b94d3e91f4c7d22a37deb4aad',
  description: 'Secret Project',
  title: 'secret project',

  students: [
    { _id: '123', name: 'Alex', primary_subject: 'Math', address: 'xxxxx', dob: '1989-10-10', gender: 'F', nationality: 'German' },
    { _id: '124', name: 'Emanuel', primary_subject: 'Physics', address: 'yyyyyy', dob: '1988-05-07', gender: 'M', nationality: 'French' },
    { _id: '242', name: 'Mike', primary_subject: 'Chemistry', address: 'zzzz', dob: '1990-02-02', gender: 'M', nationality: 'English' }
  ]
}

我需要获取特定属性。例如,只想获取名称,primary_subject,国籍属性。 使用下面的mongo查询,我可以获取所有属性。

db.student_projects.aggregate({
  $project: {
    "students": {
      $filter: {
        input: "$students",
        as: "st",
        cond: {
          $eq: [ "$$st._id", "242" ]
        }
      },
    }
  }
},
{ $unwind: { path: "$students", preserveNullAndEmptyArrays: false } }
).pretty();

以上查询将获取匹配学生的所有属性。但就我而言,我只需要3个属性。

1 个答案:

答案 0 :(得分:0)

使用$map重塑输出数组的形状:

db.student_projects.aggregate({
  $project: {
    "students": {
      $map: {
        input: {
          $filter: {
            input: "$students",
            as: "st",
            cond: {
              $eq: [ "$$st._id", "242" ]
            }
          }
        },
        in: {
          name: "$$this.name",
          primary_subject: "$$this.primary_subject",
          nationality: "$$this.nationality"
        }
      }
    }
  }
},
{ $unwind: { path: "$students", preserveNullAndEmptyArrays: false } }
).pretty();

就像$map一样,“重塑” 数组就像其他语言一样。

如果您想获得比“排除”更长的“包含”字段列表的“花哨”,那么从更高版本的MongoDB 3.6及更高版本中可以找到一些现代的运算符,可以在这里提供帮助:

db.student_projects.aggregate({
  $project: {
    "students": {
      $map: {
        input: {
          $filter: {
            input: "$students",
            as: "st",
            cond: {
              $eq: [ "$$st._id", "242" ]
            }
          }
        },
        in: {
          $arrayToObject: {
            $filter: {
              input: { $objectToArray: "$$this" },
              cond: {
                "$not": {
                  "$in": [ "$$this.k", [ "_id", "address", "dob" ] ]
                }
              }
            }
          }
        }
      }
    }
  }
},
{ $unwind: "$students" }
).pretty();

$objectToArray转换为kv的“键/值”对,代表对象键和值。从这个“数组”中,您可以$filterk值上为不需要的结果。 $in允许与“列表”进行比较,而$not则将比较值取反。

最后,您可以通过$arrayToObject将“数组”转换回对象形式。

当然,您总是可以在$project之后简单地$unwind

db.student_projects.aggregate({
  $project: {
    "students": {
      $filter: {
        input: "$students",
        as: "st",
        cond: {
          $eq: [ "$$st._id", "242" ]
        }
      },
    }
  }
},
{ $unwind: "$students" },
{ $project: {
  "students": {
    "name": "$students.name",
    "primary_subject": "$students.primary_subject",
    "nationality": "$students.nationality"
  }
}
).pretty();

如果不想使用"students"键,只需将其删除:

{ $project: {
  "name": "$students.name",
  "primary_subject": "$students.primary_subject",
  "nationality": "$students.nationality"
}

或使用原始$map版本的$replaceRoot

db.student_projects.aggregate({
  $project: {
    "students": {
      $map: {
        input: {
          $filter: {
            input: "$students",
            as: "st",
            cond: {
              $eq: [ "$$st._id", "242" ]
            }
          }
        },
        in: {
          name: "$$this.name",
          primary_subject: "$$this.primary_subject",
          nationality: "$$this.nationality"
        }
      }
    }
  }
},
{ $unwind: "$students" },
{ $replaceRoot: { newRoot: "$students" } }
).pretty();

但是,为此,您也可以在$match之后执行$unwind,而不是甚至使用$filter。不过,通常就地使用数组更有效,而且很多时候根本不需要$unwind,因此习惯习惯于操纵数组的方法是一种很好的做法。

当然,如果使用$replaceRoot或类似的结果确实是您想要的结果,那么最好根本不使用数组中的嵌入式文档。如果您打算使用的访问模式通常在大多数时候“独立”地使用那些嵌入文档,那么您应该考虑将它们保留在自己的集合中。这样可以避免“聚合开销”,并且是返回数据的简单查询和投影。

  

NB $unwind运算符“默认”为preserveNullAndEmptyArrays: false。因此,原始格式不需要指定的格式,也不需要path键。除非您专门打算保留那些空值和空值,否则用这种方式编写的时间会较短。