首页 文章

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

提问于
浏览
306

假设您的集合中包含以下文档:

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

但是,我想仅使用包含 color=red 的数组获取文档(文档1):

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

我怎样才能做到这一点?

11 回答

  • 7

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

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

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

    当您知道元素的索引时, $slice 会很有用,但有时您需要符合条件的数组元素 . 您可以使用 $ 运算符返回匹配元素 .

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

    产出

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

    MongoDB 2.2中的新Aggregation Framework提供了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
    }
    
  • 2

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

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

    最简单的方法是过滤形状 in the client .

    如果你真的需要直接从MongoDB输出正确的输出,你可以 use a 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()
    
  • 5

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

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

    您只需要运行查询

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

    此查询的输出是

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

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

  • 0

    MongoDB 2.2的新$elemMatch投影运算符提供了另一种方法来更改返回的文档,使其仅包含 first 匹配的 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 Update

    从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"
                }
            ]
        }
    ]
    
  • 334

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

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

  • 11

    另一种有意思的方法是使用$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" . 所以它只会在文档内部运行 . 它基本上扫描你的文档顶部到底部,并检查它是否与 $cond 中的 if 条件匹配,如果匹配,它将保留内容( $$DESCEND )或删除( $$PRUNE ) .

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

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

相关问题