当前位置:  开发笔记 > 编程语言 > 正文

仅检索MongoDB集合中对象数组中的查询元素

如何解决《仅检索MongoDB集合中对象数组中的查询元素》经验,为你挑选了9个好方法。

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

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

我怎样才能做到这一点?



1> JohnnyHK..:

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


任何解决方案,如果我希望它返回匹配它的每个元素而不是第一个?

2> Stennie..:

MongoDB 2.2+中的新聚合框架提供了Map/Reduce的替代方案.该$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
}


@JohnnyHK:在这种情况下,`$ elemMatch`是另一种选择.我实际上是通过[Google群组问题](https://groups.google.com/forum/?fromgroups#!topic/mongodb-user/gASUwWbxN3w)来到这里的,其中$ elemMatch无效,因为它只返回每个文件的第一场比赛
@JohnnyHK:不用担心,这个问题现在有三个有用的答案;-)
@calmbird我更新了示例以包含初始$ match阶段.如果您对更有效的功能建议感兴趣,我会观察/ upvote [SERVER-6612:支持在$ elemMatch投影说明符的投影中投影多个数组值](https://jira.mongodb.org/browse/SERVER -6612)在MongoDB问题跟踪器中.

3> Niels van de..:

注意:这个答案提供了一个解决方案,这是有关在那个时候,引入了之前的MongoDB 2.2的新功能和最多.如果您使用的是更新版本的MongoDB,请参阅其他答案.

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

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

如果您确实需要直接从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()



4> anvarik..:

另一种有趣的方法是使用$ redact,这是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 "根据文件本身存储的信息限制文件的内容".所以它只会在文档内部运行.它基本上扫描你的文档顶部到底部,并检查它是否与你的if条件匹配$cond,如果有匹配,它将保留content($$DESCEND)或remove($$PRUNE).

在上面的示例中,首先$match返回整个shapes数组,$ redact将其删除到预期结果.

请注意,这{$not:"$color"}是必要的,因为它也将扫描顶层文档,如果在顶层$redact找不到color字段,则返回false可能会删除我们不想要的整个文档.


辉煌!我不明白$ eq如何在这里工作.我最初把它关了,这对我不起作用.不知何故,它在形状数组中查找匹配,但查询从不指定要查找的数组.例如,如果文档具有形状,例如大小; $ eq会在两个数组中查找匹配吗?$ redact只是在文档中查找与"if"条件匹配的任何内容吗?

5> 小智..:

更好的是你可以使用匹配的数组元素进行查询$slice是否有助于返回数组中的重要对象.

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

$slice当你知道元素的索引时很有用,但有时候你想要哪个数组元素符合你的标准.您可以使用$运算符返回匹配元素.



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

产出

{

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



7> Vicky..:

在mongodb中查找的语法是

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

这将为您提供所需的结果.


@ErikOlson您的建议在上述情况下是正确的,我们需要找到所有红色文档并仅在其上应用投影.但是,如果有人要求找出所有具有蓝色的文档,但它应该只返回那些形状数组中具有红色的元素.在这种情况下,上述查询也可以由其他人引用.

8> Eddy..:

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



9> Vaibhav Pati..:

您只需要运行查询

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

此查询的输出是

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

如你所料,它将从匹配颜色的数组中提供确切的字段:'red'.

推荐阅读
小色米虫_524
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有