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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3985214/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 11:50:05  来源:igfitidea点击:

Retrieve only the queried element in an object array in MongoDB collection

mongodbmongodb-queryaggregation-frameworkprojection

提问by Sebtm

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:

返回匹配的文档(Document 1),但始终包含 ALL 数组项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:

但是,我只想使用包含以下内容的数组获取文档(文档 1)color=red

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

How can I do this?

我怎样才能做到这一点?

回答by JohnnyHK

MongoDB 2.2's new $elemMatchprojection operator provides another way to alter the returned document to contain only the firstmatched shapeselement:

MongoDB 2.2 的新$elemMatch投影运算符提供了另一种方法来更改返回的文档以仅包含第一个匹配的shapes元素:

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

Returns:

返回:

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

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:

在 2.2 中,您还可以使用 来执行此操作$ projection operator,其中$投影对象中的字段名称表示该字段在查询中的第一个匹配数组元素的索引。以下返回与上述相同的结果:

db.test.find({"shapes.color": "red"}, {_id: 0, 'shapes.$': 1});

MongoDB 3.2 Update

MongoDB 3.2 更新

Starting with the 3.2 release, you can use the new $filteraggregation operator to filter an array during projection, which has the benefit of including allmatches, instead of just the first one.

从 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
    }}
])

Results:

结果:

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

回答by Stennie

The new Aggregation Frameworkin MongoDB 2.2+ provides an alternative to Map/Reduce. The $unwindoperator can be used to separate your shapesarray into a stream of documents that can be matched:

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

Results in:

结果是:

{
    "result" : [
        {
            "_id" : ObjectId("504425059b7c9fa7ec92beec"),
            "shapes" : {
                "shape" : "circle",
                "color" : "red"
            }
        }
    ],
    "ok" : 1
}

回答by Niels van der Rest

Caution:This answer provides a solution that was relevant at that time, before the new features of MongoDB 2.2 and up were introduced. See the other answers if you are using a more recent version of MongoDB.

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

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.

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

The easiest way is to just filter the shapes in the client.

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

If you really needthe correct output directly from MongoDB, you can use a map-reduceto filter the shapes.

如果你真的需要直接从 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()

回答by anvarik

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,这是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"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 your ifcondition which is in $cond, if there is match it will either keep the content($$DESCEND) or remove($$PRUNE).

$redact“根据文档本身存储的信息限制文档的内容”。所以它只会在文档内部运行。它基本上从上到下扫描您的文档,并检查它是否与您的if条件匹配$cond,如果匹配,它将保留内容($$DESCEND)或删除($$PRUNE)。

In the example above, first $matchreturns the whole shapesarray, and $redact strips it down to the expected result.

在上面的示例中,首先$match返回整个shapes数组,然后 $redact 将其剥离为预期结果。

Note that {$not:"$color"}is necessary, because it will scan the top document as well, and if $redactdoes not find a colorfield on the top level this will return falsethat might strip the whole document which we don't want.

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

回答by Narendran

Better you can query in matching array element using $sliceis it helpful to returning the significant object in an array.

您可以更好地查询匹配的数组元素,使用$slice是否有助于返回数组中的重要对象。

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

$sliceis helpful when you know the index of the element, but sometimes you want whichever array element matched your criteria. You can return the matching element with the $operator.

$slice当您知道元素的索引时很有帮助,但有时您想要任何与您的条件匹配的数组元素。您可以使用$运算符返回匹配的元素。

回答by Viral Patel

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

OUTPUTS

输出

{

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

回答by Vicky

The syntax for find in mongodb is

在 mongodb 中 find 的语法是

    db.<collection name>.find(query, projection);

and the second query that you have written, that is

以及您编写的第二个查询,即

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

in this you have used the $elemMatchoperator 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 as

在这里,您$elemMatch在查询部分使用了运算符,而如果您在投影部分使用此运算符,那么您将获得所需的结果。您可以将查询写为

     db.users.find(
     {"shapes.color":"red"},
     {_id:0, shapes: {$elemMatch : {color: "red"}}})

This will give you the desired result.

这会给你想要的结果。

回答by Eddy

Thanks to JohnnyHK.

感谢JohnnyHK

Here I just want to add some more complex usage.

这里我只想添加一些更复杂的用法。

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

回答by Vaibhav Patil

You just need to run query

你只需要运行查询

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

output of this query is

此查询的输出是

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

as you expected it'll gives the exact field from array that matches color:'red'.

正如您所料,它会从数组中提供与颜色匹配的确切字段:'red'。

回答by shakthydoss

along with $project it will be more appropriate other wise matching elements will be clubbed together with other elements in document.

与 $project 一起使用将更合适,其他明智的匹配元素将与文档中的其他元素一起使用。

db.test.aggregate(
  { "$unwind" : "$shapes" },
  { "$match" : {
     "shapes.color": "red"
  }},
{"$project":{
"_id":1,
"item":1
}}
)