mongodb - 嵌套数组的查询元素

时间:2015-08-29 07:16:06

标签: mongodb mongodb-query aggregation-framework

数据&问题

鉴于赛马会议的集合(db.sectionals),如何编写查询以便返回position: [2]

的数组(即马匹)
[
  {
    "race": {
      "date": ["2013-05-08"],
      "meeting": ["canterbury park"],
      "weather": ["fine"],
      "field": {
        "Belladini": {
          "position": [2],
          "distance": [1100]
        },
        "Bobs Destiny": {
          "position": [7],
          "distance": [1100]
        }
      }
    }
  },
  {
    "race": {
      "date": ["2013-03-27"],
      "meeting": ["canterbury park"],
      "weather": ["fine"],
      "field": {
        "Berna": {
          "position": [5],
          "distance": [1550]
        },
        "Excited Prince": {
          "position": [2],
          "distance": [1550]
        }
      }
    }
  }
]

期望的结果

在这个例子中,我希望它返回类似

的东西
       "Belladini": {
          "position": [2],
          "distance": [1100]
        },
       "Excited Prince": {
          "position": [2],
          "distance": [1550]
        }

尝试

我在嵌套数组上找到了the documentation,发现数组元素有点令人困惑,并尝试了各种尝试,例如

db.Sectionals.find({ "race.field.position": 2} )

db.Sectionals.find({ "race.field": { $elemMatch: { position: 2} } } )

db.Sectionals.find({ "race.field": {$all: [{"position": 2}] }} )

没有成功。

其他信息

我正在使用Robomongo。

2 个答案:

答案 0 :(得分:3)

这里出错的地方在于您的文档设计。请看以下部分:

  "field": {
    "Berna": {
      "position": [5],
      "distance": [1550]
    },
    "Excited Prince": {
      "position": [2],
      "distance": [1550]
    }
  }

不要问为什么你会想要内在的元素,比如" position"和"距离"无论如何要成为阵列,这里的关键问题是" field"不是数组。你没有使用数组,而是使用了"对象键"识别不同类型的数据。

您需要了解的是,对于数据库"这是非常糟糕的做法。你的"键"例如" Berna"和#34;兴奋的王子"包含您可以查询的有用数据。但是现在,你不能这样做,因为"键名"而不是"价值观"在数据文件中。数据库实际上只关注"值",特别是当涉及"索引"内容。

所以得到" Horses"现在是不可能的,没有导致代码迭代所有对象键并提取" Horses"。使用mapReduce操作可以做到这一点,但是对于仅提取项目肯定有点过分,而且效率不高。

基本查询参数也不可能,因为现在您必须执行以下操作:

{ "race.field.Excited Prince.position": 2 }

哪个会让你排在第二位?#34;兴奋的王子",但要测试所有"马匹"你基本上需要指定每个可能的关键路径。没有通配符可用于键名,任何条件都会再次返回到JavaScript编码和遍历对象。

总而言之,像你这样的建模是一个“不好的”#34;实践。它不具备性能,缺乏灵活性并且充满问题。

所以至少将结构更改为这样的数组:

  "field": [
      {
        "Horse": "Berna",
        "position": [5],
        "distance": [1550]
      },
      {
        "Horse": "Excited Prince",
        "position": [2],
        "distance": [1550]
      }
 ]

现在您可以基本上查询和匹配文档:

db.Sectionals.find({ "race.field.position": 2 })

至少匹配所有文件。

或者针对您的具体投影,请使用.aggregate()

db.Sectionals.aggregate([
    { "$match": { 
        "race.field.position": 2
    }},
    { "$unwind": "$race.field" },
    { "$match": { 
        "race.field.position": 2
    }},
    { "$project": { 
        "Horse": "$race.field.Horse",
        "position": "$race.field.position",
        "distance": "$race.field.distance"
    }}
])  

返回:

{
        "_id" : ObjectId("55e161545c6a4540fa687037"),
        "Horse" : "Belladini",
        "position" : [
                2
        ],
        "distance" : [
                1100
        ]
}
{
        "_id" : ObjectId("55e161545c6a4540fa687038"),
        "Horse" : "Excited Prince",
        "position" : [
                2
        ],
        "distance" : [
                1550
        ]
}

答案 1 :(得分:2)

您需要更改文档结构的更改,最好的方法是使用"Bulk"操作。

var bulk = db.sectionals.initializeOrderedBulkOp(),  
    count = 0;

db.sectionals.find().forEach(function(doc){
    var field = doc.race['field']; 
    var newField = []; 
    for (var key in field ){    
        newField.push({
            'Horse': key, 
            'position': field[key]['position'], 
            'distance': field[key]['distance'] 
        }); 
    } 
    bulk.find({ "_id": doc._id }).update({
        "$set": { "race.field":  newField }
    }); 
    count++; 
    if (count % 500 == 0) {    
        bulk.execute();    
        bulk = db.sectionals.initializeOrderedBulkOp(); 
    }
})
if (count % 500 != 0)
    bulk.execute();

您的文档如下所示:

{
        "_id" : ObjectId("55e16462d4a8842b9fd90bfa"),
        "race" : {
                "date" : [
                        "2013-05-08"
                ],
                "meeting" : [
                        "canterbury park"
                ],
                "weather" : [
                        "fine"
                ],
                "field" : [
                        {
                                "Horse" : "Belladini",
                                "position" : [
                                        2
                                ],
                                "distance" : [
                                        1100
                                ]
                        },
                        {
                                "Horse" : "Bobs Destiny",
                                "position" : [
                                        7
                                ],
                                "distance" : [
                                        1100
                                ]
                        }
                ]
        }
}

然后你可以使用聚合,因为@BlakesSeven已在他的回答中提及。

db.sectionals.aggregate([
     { "$match": {
         "race.field.position": 2
     }},
     { "$unwind": "$race.field" },
     { "$match": {
         "race.field.position": 2
     }},
     { "$project": {
         "Horse": "$race.field.Horse",
         "position": "$race.field.position",
         "distance": "$race.field.distance"
     }}
])
{ "_id" : ObjectId("55e16462d4a8842b9fd90bfa"), "Horse" : "Belladini", "position" : [ 2 ], "distance" : [ 1100 ] }
{ "_id" : ObjectId("55e16462d4a8842b9fd90bfb"), "Horse" : "Excited Prince", "position" : [ 2 ], "distance" : [ 1550 ] }