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

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

我该怎么做呢?


当前回答

对于MongoDB的新版本,略有不同。

对于db.collection.find,可以使用find的第二个参数,键为projection

db.collection.find({}, {projection: {name: 1, email: 0}});

你也可以使用.project()方法。 然而,它不是原生的MongoDB方法,它是大多数MongoDB驱动程序(如Mongoose, MongoDB Node.js驱动程序等)提供的方法。

db.collection.find({}).project({name: 1, email: 0});

如果你想用findOne,这和find是一样的

db.collection.findOne({}, {projection: {name: 1, email: 0}});

但是findOne没有.project()方法。

其他回答

你只需要运行query

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

此查询的输出为

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

正如你所期望的那样,它会从数组中给出匹配颜色的精确字段:'red'。

虽然这个问题是9.6年前问的,但这对很多人都有很大的帮助,我就是其中之一。感谢大家的提问、提示和回答。从这里的一个答案中…我发现下面的方法也可以用来投影父文档中的其他字段。这可能对某些人有帮助。

对于下面的文档,需要查明员工(emp #7839)是否将其休假历史设置为2020年。休假历史记录被实现为父雇员文档中的嵌入式文档。

db.employees.find( {"leave_history.calendar_year": 2020}, 
    {leave_history: {$elemMatch: {calendar_year: 2020}},empno:true,ename:true}).pretty()


{
        "_id" : ObjectId("5e907ad23997181dde06e8fc"),
        "empno" : 7839,
        "ename" : "KING",
        "mgrno" : 0,
        "hiredate" : "1990-05-09",
        "sal" : 100000,
        "deptno" : {
                "_id" : ObjectId("5e9065f53997181dde06e8f8")
        },
        "username" : "none",
        "password" : "none",
        "is_admin" : "N",
        "is_approver" : "Y",
        "is_manager" : "Y",
        "user_role" : "AP",
        "admin_approval_received" : "Y",
        "active" : "Y",
        "created_date" : "2020-04-10",
        "updated_date" : "2020-04-10",
        "application_usage_log" : [
                {
                        "logged_in_as" : "AP",
                        "log_in_date" : "2020-04-10"
                },
                {
                        "logged_in_as" : "EM",
                        "log_in_date" : ISODate("2020-04-16T07:28:11.959Z")
                }
        ],
        "leave_history" : [
                {
                        "calendar_year" : 2020,
                        "pl_used" : 0,
                        "cl_used" : 0,
                        "sl_used" : 0
                },
                {
                        "calendar_year" : 2021,
                        "pl_used" : 0,
                        "cl_used" : 0,
                        "sl_used" : 0
                }
        ]
}

感谢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"
    }
]}

注意:这个答案提供的解决方案在当时是相关的,在MongoDB 2.2及更高版本的新特性引入之前。如果您使用的是最新版本的MongoDB,请参阅其他答案。

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

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

如果你真的需要直接从MongoDB得到正确的输出,你可以使用map-reduce来过滤形状。

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()

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
}