仅检索MongoDB集合中对象数组中的查询元素
假设我的收藏中有以下文档: { _id:Objected562E7C594C12942F08FE4192, 形状:[ { 形状:方形, 颜色:蓝色 }, { 形状:圆形, 颜色:红色 } ] }, { _id:Objected562E7C594C12942F08FE4193, 形状:[ { 形状:方形, 颜色:黑色 }, { 形状:圆形, 颜色:绿色 } ] } 请勿查询: db.test.find{shapes.color:red},{shapes.color:1} 或 test.find{shapes:{$elemMatch:{color:red}}},{shapes.color:1} 返回匹配的文档文档1,但始终包含形状中的所有数组项: {形状: [ {形状:正方形,颜色:蓝色}, {形状:圆形,颜色:红色} ] } 但是,我只希望使用包含color=red的数组获取文档1: {形状: [ {形状:圆形,颜色:红色} ] } 我该怎么做 注意:在MongoDB 2.2和更高版本的新特性推出之前,这个答案提供了一个当时相关的解决方案。如果您使用的是更新版本的MongoDB,请参阅其他答案 字段选择器参数仅限于完整属性。它不能用于选择数组的一部分,只能选择整个数组。我试着用这个,但没用 最简单的方法是只过滤客户端中的形状 如果您确实需要直接从MongoDB获得正确的输出,可以使用map reduce来过滤形状仅检索MongoDB集合中对象数组中的查询元素,mongodb,mongodb-query,aggregation-framework,projection,Mongodb,Mongodb Query,Aggregation Framework,Projection,假设我的收藏中有以下文档: { _id:Objected562E7C594C12942F08FE4192, 形状:[ { 形状:方形, 颜色:蓝色 }, { 形状:圆形, 颜色:红色 } ] }, { _id:Objected562E7C594C12942F08FE4193, 形状:[ { 形状:方形, 颜色:黑色 }, { 形状:圆形, 颜色:绿色 } ] } 请勿查询: db.test.find{shapes.color:red},{shapes.color:1} 或
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的替代方案。运算符可用于将形状数组分离为可匹配的文档流:
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的新投影操作符提供了另一种方法来更改返回的文档,使其仅包含第一个匹配的shapes元素:
db.test.find
{shapes.color:red},
{{u id:0,形状:{$elemMatch:{color:red}}};
返回:
{形状:[{形状:圆形,颜色:红色}]}
在2.2中,您还可以使用,其中投影对象字段名中的$表示该字段从查询中匹配的第一个数组元素的索引。下面返回与上面相同的结果:
test.find{shapes.color:red},{u id:0,'shapes.$':1};
MongoDB 3.2更新
从3.2版本开始,您可以使用新的聚合操作符在投影期间过滤数组,这样做的好处是包括所有匹配项,而不仅仅是第一个匹配项
db.test.aggregate[
//只获取包含颜色为“红色”的shapes元素的文档
{$match:{'shapes.color':'red'},
{$项目:{
形状:{$filter:{
输入:“$shapes”,
如:‘形状’,
条件:{$eq:['$$shape.color','red']}
}},
_身份证号码:0
}}
]
结果:
[
{
形状:[
{
形状:圆形,
颜色:红色
}
]
}
]
随着$project的出现,其他明智的匹配元素将与文档中的其他元素结合在一起
db.test.aggregate(
{ "$unwind" : "$shapes" },
{ "$match" : { "shapes.color": "red" } },
{
"$project": {
"_id":1,
"item":1
}
}
)
mongodb中find的语法是
db.<collection name>.find(query, projection);
在本例中,您在查询部分使用了$elemMatch运算符,而如果在投影部分使用此运算符,则将获得所需的结果。您可以将查询记录为
db.users.find(
{"shapes.color":"red"},
{_id:0, shapes: {$elemMatch : {color: "red"}}})
这将为您提供所需的结果。多亏了。
这里我只想添加一些更复杂的用法
// 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.6的新聚合特性之一。如果您使用的是2.6,则不需要$unwind,如果您使用的是大型阵列,则可能会导致性能问题
db.test.aggregate([
{ $match: {
shapes: { $elemMatch: {color: "red"} }
}},
{ $redact : {
$cond: {
if: { $or : [{ $eq: ["$color","red"] }, { $not : "$color" }]},
then: "$$DESCEND",
else: "$$PRUNE"
}
}}]);
$redact根据文档本身存储的信息限制文档的内容。因此,它将只在文档内部运行。它基本上是从上到下扫描文档,并检查它是否与$cond中的if条件匹配,如果匹配,它将保留内容$$descent或删除$$PRUNE
在上面的示例中,first$match返回整个shapes数组,而$redact将其拆分为预期结果
请注意,{$not:$color}是必需的,因为它也会扫描顶层文档,如果$redact在顶层找不到颜色字段,它将返回false,这可能会删除我们不想要的整个文档 更好地使用$slice查询匹配数组元素是否有助于返回数组中的重要对象
db.test.find({"shapes.color" : "blue"}, {"shapes.$" : 1})
当您知道元素的索引时,$slice很有用,但有时您需要
无论哪个数组元素符合您的条件。您可以返回匹配的元素
使用$operator。只需运行查询
db.test.find(
{"shapes.color": "red"},
{shapes: {$elemMatch: {color: "red"}}});
此查询的输出为
{
"_id" : ObjectId("562e7c594c12942f08fe4192"),
"shapes" : [
{"shape" : "circle", "color" : "red"}
]
}
正如您所期望的,它将给出与颜色匹配的数组中的确切字段:'red'
db.getCollection('aj').find({"shapes.color":"red"},{"shapes.$":1})
输出
使用aggrega
函数和$project以获取文档中的特定对象字段
db.getCollection('geolocations').aggregate([ { $project : { geolocation : 1} } ])
结果:
同样,您可以找到多个
db.getCollection('localData').aggregate([
// Get just the docs that contain a shapes element where color is 'red'
{$match: {'shapes.color': {$in : ['red','yellow'] } }},
{$project: {
shapes: {$filter: {
input: '$shapes',
as: 'shape',
cond: {$in: ['$$shape.color', ['red', 'yellow']]}
}}
}}
])
虽然这个问题是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
}
]
}
@在这种情况下,$elemMatch是另一种选择。事实上,我是通过一个$elemMatch不起作用的方法来到这里的,因为它只返回每个文档的第一个匹配项。谢谢,我不知道这个限制,所以很高兴知道。很抱歉删除了您回复的我的评论,我决定发布另一个答案,不想让人困惑。@JohnnyHK:不用担心,这个问题现在有三个有用的答案-对于其他搜索者,除此之外,我还尝试添加{$project:{shapes:1}}-这似乎很有效,如果所包含的文档很大,而您只想查看shapes键值,这将很有帮助。@calmbird我更新了该示例,以包含初始$match阶段。如果您对更高效的功能建议感兴趣,我会在MongoDB问题跟踪器中观看/upvote。如果我希望它返回与之匹配的所有元素而不是第一个元素,有什么解决方案吗?恐怕我使用的是Mongo 3.0.X:-@charliebrownie,然后使用其他使用聚合的答案之一。此查询仅返回数组形状它不会返回其他字段。有人知道如何返回其他字段吗?这同样有效:db.test.find{},{shapes:{$elemMatch:{color:red}};这对我有用。但是,似乎查询参数中的shapes.color:red find方法的第一个参数不是必需的。您可以用{}替换它,得到相同的结果。@ErikOlson您的建议在上述情况下是正确的,我们需要找到所有红色的文档,并且只对它们应用投影。但假设有人需要找出所有蓝色的文档,但它应该只返回形状数组中红色的元素。在这种情况下,上面的查询也可以被其他人引用..这似乎是最简单的,但我不能让它工作。它只返回第一个匹配的子文档。populate不处理这个问题为什么?@MahmoodHussain这个答案已经有7年了,所以可能是版本问题。你能查一下最新的文件吗。我将尝试在最新版本上运行类似的程序,并分享我的发现。你能解释一下你到底想达到什么目的吗?完美的答案。正如你提到的$REWIND将消耗大量内存。所以这个比较起来会更好,我有点怀疑。在本例中,形状是一个数组。$redact会扫描形状数组中的所有对象吗??这对你的表现有多好?不是全部,而是你第一场比赛的结果。这就是为什么您将$match作为第一个聚合阶段KKK的原因。。如果在“颜色”字段上创建索引,那么它将扫描形状数组中的所有对象???哪种方法可以有效地匹配阵列中的多个对象???太棒了!我不明白$eq在这里是如何工作的。我本来就不做了,这对我不起作用。不知何故,它在形状数组中查找匹配项,但查询从未指定要查找的数组。比如,如果文档有形状,例如大小;$eq会在两个数组中查找匹配项吗?$redact是否只是在文档中查找与“if”条件匹配的任何内容?请描述一下这是通过输入和输出集实现的?欢迎使用堆栈溢出!感谢您提供的代码片段,它可能会提供一些有限的、即时的帮助。一个恰当的解释将通过描述为什么这是一个很好的问题解决方案来极大地改进its,并将使它对未来有其他类似问题的读者更有用。请编辑您的答案以添加一些解释,包括您所做的假设。此答案确实是首选的4.x方式:$match以减少空间,然后$filter以保留所需内容,覆盖输入字段使用$filter对字段形状的输出将$project返回到形状。样式说明:最好不要将字段名用作as参数,因为这可能会导致以后与$$shape和$shape混淆。我更喜欢zz作为as字段,因为它非常突出。感谢您的查询,但它只是返回第一个字段,即使条件与数组中的多个元素匹配,有什么建议吗?
db.getCollection('geolocations').aggregate([ { $project : { geolocation : 1} } ])
{
"_id" : ObjectId("5e3ee15968879c0d5942464b"),
"geolocation" : [
{
"_id" : ObjectId("5e3ee3ee68879c0d5942465e"),
"latitude" : 12.9718313,
"longitude" : 77.593551,
"country" : "India",
"city" : "Chennai",
"zipcode" : "560001",
"streetName" : "Sidney Road",
"countryCode" : "in",
"ip" : "116.75.115.248",
"date" : ISODate("2020-02-08T16:38:06.584Z")
}
]
}
db.getCollection('localData').aggregate([
// Get just the docs that contain a shapes element where color is 'red'
{$match: {'shapes.color': {$in : ['red','yellow'] } }},
{$project: {
shapes: {$filter: {
input: '$shapes',
as: 'shape',
cond: {$in: ['$$shape.color', ['red', 'yellow']]}
}}
}}
])
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
}
]
}