仅检索 MongoDB 集合中对象数组中的查询元素
假设我的集合中有以下文档:
{
"_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":
[
{"shape": "square", "color": "blue"},
{"shape": "circle", "color": "red"}
]
}
但是,我会想要仅使用包含 color=red
的数组来获取文档(文档 1):
{ "shapes":
[
{"shape": "circle", "color": "red"}
]
}
我该怎么做?
Suppose you have the following documents in my collection:
{
"_id":ObjectId("562e7c594c12942f08fe4192"),
"shapes":[
{
"shape":"square",
"color":"blue"
},
{
"shape":"circle",
"color":"red"
}
]
},
{
"_id":ObjectId("562e7c594c12942f08fe4193"),
"shapes":[
{
"shape":"square",
"color":"black"
},
{
"shape":"circle",
"color":"green"
}
]
}
Do query:
db.test.find({"shapes.color": "red"}, {"shapes.color": 1})
Or
db.test.find({shapes: {"$elemMatch": {color: "red"}}}, {"shapes.color": 1})
Returns matched document (Document 1), but always with ALL array items in shapes
:
{ "shapes":
[
{"shape": "square", "color": "blue"},
{"shape": "circle", "color": "red"}
]
}
However, I'd like to get the document (Document 1) only with the array that contains color=red
:
{ "shapes":
[
{"shape": "circle", "color": "red"}
]
}
How can I do this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(20)
MongoDB 2.2 的新
$elemMatch
投影运算符提供了另一种方法来更改返回的文档以仅包含 第一个 匹配的shapes
元素:返回:
在 2.2 中,您还可以使用 < a href="http://docs.mongodb.org/manual/reference/operator/projection/positional/#proj._S_" rel="noreferrer">
$ 投影运算符
,其中投影对象字段名称中的$
表示查询中该字段的第一个匹配数组元素的索引。以下返回与上面相同的结果:MongoDB 3.2 更新
从 3.2 版本开始,您可以使用新的
$filter
聚合运算符,用于在投影期间过滤数组,其优点是包含全部 匹配,而不仅仅是第一个。结果:
MongoDB 2.2's new
$elemMatch
projection operator provides another way to alter the returned document to contain only the first matchedshapes
element:Returns:
In 2.2 you can also do this using the
$ projection operator
, where the$
in a projection object field name represents the index of the field's first matching array element from the query. The following returns the same results as above:MongoDB 3.2 Update
Starting with the 3.2 release, you can use the new
$filter
aggregation operator to filter an array during projection, which has the benefit of including all matches, instead of just the first one.Results:
MongoDB 2.2+ 中新的聚合框架提供了 Map/Reduce 的替代方案。
$unwind
运算符可用于分隔您的shapes
数组放入可匹配的文档流中:结果:
The new Aggregation Framework in MongoDB 2.2+ provides an alternative to Map/Reduce. The
$unwind
operator can be used to separate yourshapes
array into a stream of documents that can be matched:Results in:
另一种有趣的方法是使用 $redact,这是一个MongoDB 2.6 的新聚合功能。如果您使用 2.6,则不需要 $unwind,如果您有大型数组,这可能会导致性能问题。
$redact
“根据文档本身存储的信息限制文档的内容”。因此它只会在文档内部运行。它基本上会从上到下扫描您的文档,并检查它是否与您在$cond
中的if
条件匹配,如果匹配,它将保留内容($$DESCEND
) 或删除($$PRUNE
)。在上面的示例中,首先
$match
返回整个shapes
数组,然后 $redact 将其精简为预期结果。请注意,
{$not:"$color"}
是必需的,因为它也会扫描顶部文档,并且如果$redact
未找到color顶层的
字段将返回false
,这可能会删除我们不想要的整个文档。Another interesing way is to use $redact, which is one of the new aggregation features of MongoDB 2.6. If you are using 2.6, you don't need an $unwind which might cause you performance problems if you have large arrays.
$redact
"restricts the contents of the documents based on information stored in the documents themselves". So it will run only inside of the document. It basically scans your document top to the bottom, and checks if it matches with yourif
condition which is in$cond
, if there is match it will either keep the content($$DESCEND
) or remove($$PRUNE
).In the example above, first
$match
returns the wholeshapes
array, and $redact strips it down to the expected result.Note that
{$not:"$color"}
is necessary, because it will scan the top document as well, and if$redact
does not find acolor
field on the top level this will returnfalse
that might strip the whole document which we don't want.字段选择器参数仅限于完整属性。它不能用于选择数组的一部分,只能用于选择整个数组。我尝试使用 $ 位置运算符,但这并没有工作。
最简单的方法是在客户端中过滤形状。
如果您确实需要直接从 MongoDB 获得正确的输出,您可以使用map-reduce来过滤形状。
The field selector parameter is limited to complete properties. It cannot be used to select part of an array, only the entire array. I tried using the $ positional operator, but that didn't work.
The easiest way is to just filter the shapes in the client.
If you really need the correct output directly from MongoDB, you can use a map-reduce to filter the shapes.
更好的是,您可以使用
$slice
查询匹配的数组元素,这有助于返回数组中的重要对象。当您知道元素的索引时,
$slice
很有帮助,但有时您想要无论哪个数组元素符合您的条件。可以返回匹配的元素
与
$
运算符。Better you can query in matching array element using
$slice
is it helpful to returning the significant object in an array.$slice
is helpful when you know the index of the element, but sometimes you wantwhichever array element matched your criteria. You can return the matching element
with the
$
operator.输出
OUTPUTS
mongodb 中 find 的语法是
您编写的第二个查询,即
您在查询部分使用了 $elemMatch 运算符,而如果您在投影部分使用此运算符,那么您将得到想要的结果。您可以写下您的查询,
这将为您提供所需的结果。
The syntax for find in mongodb is
and the second query that you have written, that is
in this you have used the
$elemMatch
operator in query part, whereas if you use this operator in the projection part then you will get the desired result. You can write down your query asThis will give you the desired result.
感谢JohnnyHK。
这里我只是想添加一些更复杂的用法。
Thanks to JohnnyHK.
Here I just want to add some more complex usage.
您只需要运行
此查询的查询输出即可,
正如您所期望的那样,它将给出数组中与颜色匹配的确切字段:'red'。
You just need to run query
output of this query is
as you expected it'll gives the exact field from array that matches color:'red'.
同样你可以找到多个
Likewise you can find for the multiple
与
$project
一起使用会更合适,其他匹配元素将与文档中的其他元素组合在一起。Along with
$project
it will be more appropriate other wise matching elements will be clubbed together with other elements in document.虽然这个问题是 9.6 年前提出的,但这对很多人都有很大的帮助,我就是其中之一。感谢大家的所有疑问、提示和回答。从这里的一个答案中选取..我发现以下方法也可以用于投影父文档中的其他字段。这可能对某人有帮助。
对于以下文档,需要查明员工(emp #7839)是否设置了 2020 年的休假历史记录。休假历史记录作为父级 Employee 文档中的嵌入文档实现。
Although the question was asked 9.6 years ago, this has been of immense help to numerous people, me being one of them. Thank you everyone for all your queries, hints and answers. Picking up from one of the answers here.. I found that the following method can also be used to project other fields in the parent document.This may be helpful to someone.
For the following document, the need was to find out if an employee (emp #7839) has his leave history set for the year 2020. Leave history is implemented as an embedded document within the parent Employee document.
使用聚合函数和
$project
获取文档中的特定对象字段结果:
Use aggregation function and
$project
to get specific object field in documentresult:
如果您想同时进行过滤、设置和查找。
if you want to do filter, set and find at the same time.
这个答案并没有完全回答这个问题,但它是相关的,我把它写下来,因为有人决定关闭 另一个问题将这个问题标记为重复(实际上不是)。
就我而言,我只想过滤数组元素,但仍返回数组的完整元素。以前的所有答案(包括问题中给出的解决方案)在将它们应用于我的特定情况时都让我头疼,因为:
$unwind
+$match
+$group
导致丢失根文档而不匹配数组元素,在我的情况下我不想这样做因为事实上我只是想过滤掉不需要的元素。$project
>$filter
导致丢失其余字段或根文档,或者迫使我在投影中指定所有这些字段,这是不可取的。所以最后我用
$addFields
> 解决了所有这些问题。$filter
像这样:说明:
shapes
的字段,在本例中,它将替换以相同方式调用的原始字段。shapes
的新值,请$filter
原始$shapes
数组的元素,暂时将每个数组元素命名为shape
以便稍后我们可以检查$$shape.color
是否为红色。shapes
数组仅包含所需的元素。This answer does not fully answer the question but it's related and I'm writing it down because someone decided to close another question marking this one as duplicate (which is not).
In my case I only wanted to filter the array elements but still return the full elements of the array. All previous answers (including the solution given in the question) gave me headaches when applying them to my particular case because:
$unwind
+$match
+$group
resulted in losing root documents without matching array elements, which I didn't want to in my case because in fact I was only looking to filter out unwanted elements.$project
>$filter
resulted in loosing the rest of the fields or the root documents or forced me to specify all of them in the projection as well which was not desirable.So at the end I fixed all of this problems with an
$addFields
>$filter
like this:Explanation:
shapes
, which in this case will replace the original field called the same way.shapes
,$filter
the elements of the original$shapes
array, temporarily naming each of the array elements asshape
so that later we can check if the$$shape.color
is red.shapes
array only contains the desired elements.对于新版本的 MongoDB,情况略有不同。
对于
db.collection.find
,您可以使用find的第二个参数,键为projection
您还可以使用
.project()
方法.然而,它不是原生的 MongoDB 方法,它是大多数 MongoDB 驱动程序(如 Mongoose、MongoDB Node.js 驱动程序等)提供的方法。
如果你想使用
findOne
,它与 find 相同,但是findOne 没有
.project()
方法。For the new version of MongoDB, it's slightly different.
For
db.collection.find
you can use the second parameter of find with the key beingprojection
You can also use the
.project()
method.However, it is not a native MongoDB method, it's a method provided by most MongoDB driver like Mongoose, MongoDB Node.js driver etc.
And if you want to use
findOne
, it's the same that with findBut findOne doesn't have a
.project()
method.有关更多详细信息,请参阅=
mongo db 官方参考
for more details refer =
mongo db official referance
如果您想返回第一个匹配的
形状
,find()
就能解决问题(如此处详述),但如果您想返回与原始文档匹配的每个形状
,您可以执行以下聚合
:使用它在 mongo 游乐场。这是过滤
shapes
属性并保留其他属性(而不是使用$project
删除其他属性,并且需要额外的步骤来获取文档)。附加的
$match
是可选的,它会删除具有空shapes
数组的文档。If you want to return the first matching
shapes
,find()
will do the trick (as detailed here), but if you want to return every matchingshapes
with the original document you can do the followingaggregate
:Play with it in mongo playground. This is filtering the
shapes
attribute in place and keeping the others (instead of using$project
which remove others attributes, and need an extra step to get the doc back).The additional
$match
is optionnal, it removes documents with emptyshapes
array.