假设您的收藏集中有以下文档:

{  
   "_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})

或者

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

返回匹配的文档 (文件1), ,但始终包含所有数组项目 shapes:

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

但是,我想获取文件 (文件1) 仅包含包含的数组 color=red:

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

我怎样才能做到这一点?

有帮助吗?

解决方案

MongoDB 2.2的新 $elemMatch 投影操作员提供了另一种更改返回文档的方式,仅包含 第一的 匹配 shapes 元素:

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

返回:

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

在2.2中,您也可以使用 $ projection operator, ,在哪里 $ 在投影对象中,字段名称代表该字段的第一个匹配数组元素的索引。以下返回与上述相同的结果:

db.test.find({"shapes.color": "red"}, {_id: 0, 'shapes.$': 1});

MongoDB 3.2更新

从3.2版本开始,您可以使用新的 $filter 聚合操作员在投影期间过滤数组,其优势包括 全部 匹配,而不仅仅是第一个。

db.test.aggregate([
    // Get just the docs that contain a shapes element where color is 'red'
    {$match: {'shapes.color': 'red'}},
    {$project: {
        shapes: {$filter: {
            input: '$shapes',
            as: 'shape',
            cond: {$eq: ['$$shape.color', 'red']}
        }},
        _id: 0
    }}
])

结果:

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

其他提示

新的 聚合框架 在MongoDB中,2.2+提供了地图/减少的替代方法。这 $unwind 操作员可用于分开您的 shapes 数组进入可以匹配的文档流:

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 2.2及以上的新功能之前。如果您使用的是MongoDB的最新版本,请参见其他答案。

字段选择器参数仅限于完整属性。它不能用于选择数组的一部分,而只能选择整个数组。我尝试使用 $位置操作员, ,但这没用。

最简单的方法是过滤形状 在客户中.

如果你真的 需要 直接从mongodb的正确输出,您可以 使用地图还原 过滤形状。

function map() {
  filteredShapes = [];

  this.shapes.forEach(function (s) {
    if (s.color === "red") {
      filteredShapes.push(s);
    }
  });

  emit(this._id, { shapes: filteredShapes });
}

function reduce(key, values) {
  return values[0];
}

res = db.test.mapReduce(map, reduce, { query: { "shapes.color": "red" } })

db[res.result].find()

另一种相交的方法是使用 $ redact, ,这是一个新的聚合特征之一 MongoDB 2.6. 。如果您使用的是2.6,则不需要$放开,如果您有大数组,可能会导致性能问题。

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

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

在上面的示例中,首先 $match 返回整体 shapes 数组和$ redact将其截至预期的结果。

注意 {$not:"$color"} 是必要的,因为它也会扫描顶级文档,如果 $redact 找不到 color 在最高级别的字段将返回 false 这可能会剥离我们不想要的整个文档。

更好的是,您可以在匹配的数组元素中查询 $slice 在数组中返回重要对象有帮助。

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

$slice 当您知道元素的索引时,很有帮助,但是有时您希望符合您的条件的数组元素。您可以用 $ 操作员。

 db.getCollection('aj').find({"shapes.color":"red"},{"shapes.$":1})

输出

{

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

在mongodb中查找的语法是

    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"}}})

这将为您带来理想的结果。

谢谢 Johnnyhk.

在这里,我只想添加一些更复杂的用法。

// Document 
{ 
"_id" : 1
"shapes" : [
  {"shape" : "square",  "color" : "red"},
  {"shape" : "circle",  "color" : "green"}
  ] 
} 

{ 
"_id" : 2
"shapes" : [
  {"shape" : "square",  "color" : "red"},
  {"shape" : "circle",  "color" : "green"}
  ] 
} 


// The Query   
db.contents.find({
    "_id" : ObjectId(1),
    "shapes.color":"red"
},{
    "_id": 0,
    "shapes" :{
       "$elemMatch":{
           "color" : "red"
       } 
    }
}) 


//And the Result

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

您只需要运行查询

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

此查询的输出是

{
    "_id" : ObjectId("562e7c594c12942f08fe4192"),
    "shapes" : [ 
        {"shape" : "circle", "color" : "red"}
    ]
}

正如您所期望的那样,它将提供与颜色相匹配的数组的确切字段:“红色”。

除$项目外,还将更合适的其他明智的匹配元素与文档中的其他元素一起贴上。

db.test.aggregate(
  { "$unwind" : "$shapes" },
  { "$match" : {
     "shapes.color": "red"
  }},
{"$project":{
"_id":1,
"item":1
}}
)
db.test.find( {"shapes.color": "red"}, {_id: 0})
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top