仅检索mongoDB集合中的对象数组中的查询元素
-
10-10-2019 - |
题
假设您的收藏集中有以下文档:
{
"_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})