假设我的收藏中有以下文件:

{  
   "_id":ObjectId("562e7c594c12942f08fe4192"),
   "shapes":[  
      {  
         "shape":"square",
         "color":"blue"
      },
      {  
         "shape":"circle",
         "color":"red"
      }
   ]
},
{  
   "_id":ObjectId("562e7c594c12942f08fe4193"),
   "shapes":[  
      {  
         "shape":"square",
         "color":"black"
      },
      {  
         "shape":"circle",
         "color":"green"
      }
   ]
}

做查询:

db.test.find({"shapes.color": "red"}, {"shapes.color": 1})

Or

db.test.find({shapes: {"$elemMatch": {color: "red"}}}, {"shapes.color": 1})

返回匹配的文档(文档1),但总是使用形状中的ALL数组项:

{ "shapes": 
  [
    {"shape": "square", "color": "blue"},
    {"shape": "circle", "color": "red"}
  ] 
}

但是,我想只获得包含color=red的数组的文档(文档1):

{ "shapes": 
  [
    {"shape": "circle", "color": "red"}
  ] 
}

我该怎么做呢?


当前回答

如果你想做筛选,设置和查找同时进行。

let post = await Post.findOneAndUpdate(
          {
            _id: req.params.id,
            tasks: {
              $elemMatch: {
                id: req.params.jobId,
                date,
              },
            },
          },
          {
            $set: {
              'jobs.$[i].performer': performer,
              'jobs.$[i].status': status,
              'jobs.$[i].type': type,
            },
          },
          {
            arrayFilters: [
              {
                'i.id': req.params.jobId,
              },
            ],
            new: true,
          }
        );

其他回答

另一种有趣的方法是使用$编校,这是MongoDB 2.6的新聚合特性之一。如果您使用的是2.6,则不需要$unwind,如果您使用的是大型数组,$unwind可能会导致性能问题。

db.test.aggregate([
    { $match: { 
         shapes: { $elemMatch: {color: "red"} } 
    }},
    { $redact : {
         $cond: {
             if: { $or : [{ $eq: ["$color","red"] }, { $not : "$color" }]},
             then: "$$DESCEND",
             else: "$$PRUNE"
         }
    }}]);

$redact“根据存储在文档本身中的信息限制文档的内容”。所以它只会在文档内部运行。它基本上扫描你的文档从上到下,并检查它是否与你的if条件在$cond中匹配,如果有匹配,它将保留内容($$ descent)或删除($$PRUNE)。

在上面的例子中,第一个$match返回整个形状数组,$编校将其分解为预期的结果。

注意{$not:"$color"}是必要的,因为它也会扫描顶部的文档,如果$ react没有在顶部找到一个颜色字段,这将返回false,这可能会剥离整个文档,这是我们不想要的。

与$project一起,其他明智的匹配元素将与文档中的其他元素组合在一起。

db.test.aggregate(
  { "$unwind" : "$shapes" },
  { "$match" : { "shapes.color": "red" } },
  { 
    "$project": {
      "_id":1,
      "item":1
    }
  }
)

MongoDB 2.2+中的新的聚合框架为Map/Reduce提供了一种替代方案。$unwind操作符可以用来将你的形状数组分离成一个可以匹配的文档流:

db.test.aggregate(
  // Start with a $match pipeline which can take advantage of an index and limit documents processed
  { $match : {
     "shapes.color": "red"
  }},
  { $unwind : "$shapes" },
  { $match : {
     "shapes.color": "red"
  }}
)

结果:

{
    "result" : [
        {
            "_id" : ObjectId("504425059b7c9fa7ec92beec"),
            "shapes" : {
                "shape" : "circle",
                "color" : "red"
            }
        }
    ],
    "ok" : 1
}

mongodb中的find语法是

    db.<collection name>.find(query, projection);

你写的第二个查询

    db.test.find(
    {shapes: {"$elemMatch": {color: "red"}}}, 
    {"shapes.color":1})

在这里,你已经在查询部分使用了$elemMatch操作符,而如果你在投影部分使用这个操作符,那么你将得到想要的结果。您可以将您的查询写成

     db.users.find(
     {"shapes.color":"red"},
     {_id:0, shapes: {$elemMatch : {color: "red"}}})

这会给你想要的结果。

这个答案并没有完全回答这个问题,但它是相关的,我把它写下来,因为有人决定关闭另一个问题,将这个问题标记为重复(这不是)。

在我的例子中,我只想过滤数组元素,但仍然返回数组的完整元素。所有之前的答案(包括问题中给出的解决方案)在应用到我的特定情况时都让我头疼,因为:

我需要我的解决方案能够返回子数组元素的多个结果。 使用$unwind + $match + $group会导致根文档丢失而不匹配数组元素,在我的例子中,我不想这样做,因为实际上我只是想过滤掉不需要的元素。 使用$project > $filter会导致丢失其余的字段或根文档,或者迫使我在投影中指定所有这些字段,这是不可取的。

所以在最后,我用$addFields > $过滤器修复了所有这些问题:

db.test.aggregate([
    { $match: { 'shapes.color': 'red' } },
    { $addFields: { 'shapes': { $filter: {
      input: '$shapes',
      as: 'shape',
      cond: { $eq: ['$$shape.color', 'red'] }
    } } } },
])

解释:

首先将文件与红色形状匹配。 对于这些文档,添加一个名为shapes的字段,在本例中,它将以同样的方式替换原来的字段。 要计算形状的新值,$filter原始$shapes数组的元素,临时将每个数组元素命名为shape,以便稍后可以检查$$shape。颜色是红色。 现在,新的形状数组只包含所需的元素。