【问题标题】:Is It possible to use query projection on the same collection that has a $elemMatch projection?是否可以在具有 $elemMatch 投影的同一集合上使用查询投影?
【发布时间】:2014-07-04 16:59:04
【问题描述】:

我了解您可以使用 $elemMatch 作为投影来限制子集合数组中的项目。当这样使用它时,无论是否还指定了query projections,它都会返回匹配的子文档的所有字段。

是否可以限制匹配子文档中返回的字段?你会怎么做?

使用版本 3.8.9。

鉴于简单的模式:

var CommentSchema = mongoose.Schema({
  body: String,
  created: {
    by: {
      type: String,
      required: true
    },
    date: {
      type: Date,
      default: Date.now
    }
  }
});

var BlogSchema = mongoose.Schema({
  title: String,
  blog: String,
  created: {
    by: {
      type: String,
      required: true
    },
    date: {
      type: Date,
      default: Date.now
    }
  },
  comments: [CommentSchema]
});

var Blog = mongoose.model('Blog',modelSchema);

示例

Blog.findOne({_id: id}, {_id: 1, comments: {$elemMatch: {'created.by': 'Jane'}, body: 1}}, function(err, blog) {
  console.log(blog.toJSON());
});
// outputs:
{
  _id: 532cb63e25e4ad524ba17102,
  comments: [
    _id: 53757456d9c99f00009cdb5b,
    body: 'My awesome comment',
    created: { by: 'Jane', date: Fri Mar 21 2014 20:34:45 GMT-0400 (EDT) }
  ]
}

// DESIRED OUTPUT
{
  _id: 532cb63e25e4ad524ba17102,
  comments: [
    body: 'My awesome comment'
  ]
}

【问题讨论】:

    标签: javascript node.js mongodb mongoose aggregation-framework


    【解决方案1】:

    是的,有两种方法可以做到这一点。因此,您可以像以前一样在投影端使用$elemMatch,只需稍作改动:

    Model.findById(id,
       { "comments": { "$elemMatch": {"created.by": "Jane" } } },
       function(err,doc) {
    

    或者只是添加到查询部分并使用位置$ 运算符:

    Model.findOne(
        { "_id": id, "comments.created.by": "Jane" },
        { "comments.$": 1 },
        function(err,doc) {
    

    任何一种方式都是完全有效的。

    如果你想要更多的东西,你可以使用.aggregate() 方法,它是$project 运算符:

    Model.aggregate([
        // Still match the document
        { "$match": "_id": id, "comments.created.by": "Jane" },
    
        // Unwind the array
        { "$unwind": "$comments" },
    
        // Only match elements, there can be more than 1
        { "$match": "_id": id, "comments.created.by": "Jane" },
    
        // Project only what you want
        { "$project": {
            "comments": {
                "body": "$comments.body",
                "by": "$comments.created.by"
            }
        }},
    
        // Group back each document with the array if you want to
        { "$group": {
            "_id": "$_id",
            "comments": { "$push": "$comments" }
        }}
    ],
    function(err,result) {
    

    因此,聚合框架不仅仅可以用于聚合结果。与使用 .find() 进行投影相比,$project 运算符为您提供了更大的灵活性。它还允许您过滤并返回多个数组结果,这也是.find()中的投影无法完成的事情。

    【讨论】:

    • 感谢您的快速回复,但我认为我没有充分强调我的问题。我了解如何使用这两种方法过滤comments 子集合,但我还想在同一个子集合上使用查询投影来选择要为每个子文档返回的字段 .我已经编辑了我的原始问题,以包含一个更明显的所需输出示例。
    • @JasonCust 我在这个阶段看不到你的问题的编辑,但我认为最后的 aggregate 示例的一些变体是你正在寻找的,根据你的评论.
    • 道歉。我以为我昨晚保存了更新,但显然没有。所以这只是.find()$elemMatch 中预测的限制?我也在研究聚合方法,但担心使用.find() 是否会造成严重的性能损失。我今天会检查一下。再次感谢。
    • @JasonCust 一般的限制是,单独使用.find() 的投影相当有限,虽然您可以执行comments.body 之类的操作并获取所有数组成员,但只能获取body 字段,但您不能执行@987654342 @ 只是为了得到匹配的那个。因此,aggregate 的使用确实会带来一些额外的成本,因为它正在做更多的事情,但除非您的数组非常大,否则这应该可以忽略不计。这也是在数组元素中获得超过 1 个匹配项的唯一方法。
    猜你喜欢
    • 2014-10-21
    • 2015-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多