【问题标题】:mongodb aggregation framework match by nested documentsmongodb聚合框架匹配嵌套文档
【发布时间】:2014-03-04 15:25:01
【问题描述】:

我有以下文件清单:

{
    "_id" : "Tvq579754r",
    "name": "Tom",
    "forms": {
           "PreOp":{
             "status":"closed"          
           },

           "Alert":{
             "status":"closed"          
           },

           "City":{
              "status":"closed"         
           },

          "Country":{
             "status":"closed"          
          } 
    }
},
....
{
    "_id" : "Tvq444454j",
    "name": "Jim",
    "forms": {
          "Jorney":{
             "status":"closed"          
           },

          "Women":{
             "status":"void"            
          },

         "Child":{
            "status":"closed"           
         },

         "Farm":{
           "status":"closed"            
         }  
     }
}

我想通过它们的“状态”字段(“forms.name_of_form.status”)过滤它们。我需要获取所有不具有 'forms.name_of_form.status' 等于 'void' 的文档。

预期结果是(没有无效表单状态的文档):

{
    "_id" : "Tvq579754r",
    "name": "Tom",
    "forms": {
           "PreOp":{
             "status":"closed"          
           },

           "Alert":{
             "status":"closed"          
           },

           "City":{
              "status":"closed"         
           },

          "Country":{
             "status":"closed"          
          } 
    }
}

【问题讨论】:

  • 您需要获取所有文档还是所有_ids?此外,您需要过滤掉状态为“void”或any状态为“void”的特定表单的文档?
  • 我想取出所有文件,其中每个文件的所有表格都处于“关闭”状态而不是“无效”
  • 表单名称列表是否有限(指定)?
  • 不,以后可以更改表单名称。我不必依赖表单名称。
  • 你能编辑一个预期的结果吗?这有助于人们回答。

标签: mongodb mongodb-query aggregation-framework


【解决方案1】:

如果事先不知道所有可能的forms 名称并在查询中使用它们,就无法查询此结构以获得所需的结果。无论如何,这将是非常混乱的。话虽如此,请继续阅读,我将解释它是如何完成的。

这些文档的结构存在问题,会妨碍您进行任何合理的查询分析。就目前而言,您必须知道所有可能的表单名称字段才能过滤掉任何内容。

您当前的结构具有包含子文档的表单,其中每个键都包含另一个具有单个属性status 的子文档。这很难遍历,因为您的 forms 元素对于您创建的每个文档都有任意结构。这意味着模式将下降到您想要比较集合中每个文档的更改的status 信息。

这就是我所说的路径。要获得任何元素的状态,您必须执行以下操作

表单 -> PreOp -> 状态

表单 -> 警报 -> 状态

第二个元素一直在变化。 没有办法通配符这样的东西,因为命名被认为是明确的。

这可能被认为是一种从您的表单序列化数据的简单方法,但我看到了一种更灵活的替代方法。您需要的是可以以标准模式遍历的文档结构。这始终是设计中值得考虑的事情。采取以下措施:

{
    "_id" : "Tvq444454j",
    "name": "Jim",
    "forms": [
        {
             "name": "Jorney",
             "status":"closed"          
        },
        {
            "name": "Women",
            "status":"void"            
        },
        {
            "name": "Child",
            "status":"closed"           
        },
        {
            "name": "Farm",
            "status":"closed"            
        }  
    ]
}

因此更改文档的结构以使forms 元素成为一个数组,而不是将状态字段放在命名“表单字段”的键下,我们将数组的每个成员作为子文档包含“表单域”namestatus。所以标识符和状态仍然配对在一起,但现在只是表示为一个子文档。这最重要的是改变了这些键的访问路径,就像现在对于 both 字段名称和我们可以做的状态

表格 -> 状态

表格 -> 名称

this的意思是可以查询到form中所有字段的名称,或者form中所有status字段的名称,甚至所有带有某个name 字段和某个status。这比使用原始结构可以实现的效果好多

现在,在您的特定情况下,您希望获取所有字段不是void的文档。现在没有办法在单个查询中执行此操作,因为没有运算符可以以这种方式比较数组中的所有元素并查看它们是否相同。但是您可以采取两种方法:

第一个并且可能效率不高的是查询包含formsstatus 为“void”的元素的所有 文档。使用生成的文档 ID,您可以发出另一个查询,返回具有指定 ID 的文档。

db.forms.find({ "forms.status": "void" },{ _id: 1})

db.forms.find({ _id: $not: { $in: [<Object1>,<Object2>,<Object3>,... ] } })

考虑到结果大小,这可能是不可能的,并且通常不是一个好主意,因为排除运算符$not 基本上是强制对集合进行全扫描,因此您不能使用索引.

另一种方法是使用聚合管道,如下所示:

db.forms.aggregate([
    { "$unwind": "$forms" },
    { "$group": { "_id": "$_id", "status": { "$addToSet": "$forms.status" }}},
    { "$unwind": "$status" },
    { "$sort": { "_id": 1, "status": -1 }},
    { "$group": { "_id": "$_id", "status": { "$first": "$status"}}},
    { "$match":{ "status": "closed" }}
])

当然,这只会返回匹配文档的 _id,但您可以使用 $in 发出查询并返回整个匹配文档。这比以前使用的排除运算符要好,现在我们可以使用索引来避免全集合扫描。

作为最后一种方法并出于最佳性能考虑,您可以再次更改文档,以便在顶层保持表单中是否有任何字段为“无效”的“状态”或“关闭”。因此,在顶层,只有当所有项目都“关闭”时,价值才会被关闭,如果某些东西是无效的,则“无效”,依此类推。

最后一个将意味着进一步的程序更改,并且对forms 项目的所有更改也需要更新此字段以保持“状态”。然而,这是查找所需文件的最有效方式,可能值得考虑。


编辑

除了把文档改成master状态之外,修改后的结构上最快的查询形式其实是:

db.forms.find({ "forms": { "$not": { "$elemMatch": { "status": "void" } } } })

【讨论】:

  • 我在使用聚合方法时省略了 (facepalm) 的一件事,您可以将整个文档 $project 作为_id,因为它已经是唯一的,这是关键用于分组。这样,通过添加最终的 $project 并以原始形式从 _id 重新建立密钥,您可以将文档恢复到原始状态并消除发出另一个查询的需要使用_id 键。忘记了,因为我太习惯于聚合转换。
  • 非常感谢。我花了很多时间寻找这种方法。
猜你喜欢
  • 1970-01-01
  • 2013-05-29
  • 1970-01-01
  • 1970-01-01
  • 2020-03-08
  • 1970-01-01
  • 1970-01-01
  • 2019-02-16
  • 1970-01-01
相关资源
最近更新 更多