Mongo使用两级未知父键

时间:2018-01-16 10:58:35

标签: mongodb mongodb-query aggregation-framework

我们使用MongoDB存储与https://www.mongodb.com/blog/post/schema-design-for-time-series-data-in-mongodb

中显示的模式类似的时间序列传感器数据

我们确实通过时间段获得了良好的数据查询性能。 关于我们的架构设计的说明: “v”是传感器读数的父键,使用Minutes和Seconds将时间转换为嵌套数组。我们使用“m”(分钟)作为子父键,然后“s”(秒)作为分钟阅读的子键。传感器读数位于“s”级别, field1 field2 ,..., field10 为传感器数据值。

现在我们正在尝试实施一些数据分析工具,并希望通过传感器数据读取值来查询数据。是否有一种有效的查询方法,而不在查询中使用嵌套的for循环?

例如:

  1. 具有传感器读数的项目:“field1”> 2
  2. 具有传感器读数的项目:“field1”> 2 “field3”> 5
  3. 万分感谢。

    记录看起来像下面的例子。

    {
       "_id": ObjectId("5a5dd49f74bbaefd1ac89fc8"),
       "c_id": "1017",
       "c_id_s": NumberInt(1017),
       "c_t": NumberInt(1516096800),
       "type": "hour",
       "v": {
         "m1": {
           "s54": {
             "field1": 7.373158,
             "entry_id": NumberInt(4635),
             "field3": 0.19,
             "field2": NumberInt(88) 
          } 
        },
         "m31": {
           "s54": {
             "field1": 5.981918,
             "entry_id": NumberInt(4637),
             "field3": 0.04 
          },
           "s55": {
             "field2": NumberInt(89),
             "entry_id": NumberInt(4639),
             "field5": NumberInt(-67) 
          } 
        } 
      },
       "entry_id": NumberInt(4639) 
    }, 
    {
       "_id": ObjectId("5a5dd1a174bbaefd1ac89fc1"),
       "c_id": "1024",
       "c_id_s": NumberInt(1024),
       "c_t": NumberInt(1516096800),
       "type": "hour",
       "v": {
         "m3": {
           "s22": {
             "field3": 210.479996,
             "entry_id": NumberInt(30297) 
          },
           "s23": {
             "field1": 3.271534,
             "entry_id": NumberInt(30300),
             "field8": 7.1875,
             "field2": NumberInt(94) 
          } 
        },
         "m8": {
           "s23": {
             "field3": 150.639999,
             "entry_id": NumberInt(30304),
             "field1": 2.948425,
             "field8": 7.125,
             "field2": NumberInt(94) 
          } 
        },
         "m13": {
           "s23": {
             "field3": 99.799995,
             "entry_id": NumberInt(30308),
             "field1": 2.849621,
             "field8": 7.0625,
             "field2": NumberInt(95) 
          } 
        },
         "m18": {
           "s23": {
             "field3": 59.099998,
             "entry_id": NumberInt(30312),
             "field1": 2.681393,
             "field8": 6.9375,
             "field2": NumberInt(95) 
          } 
        },
         "m19": {
           "s8": {
             "field5": NumberInt(-87),
             "entry_id": NumberInt(30313) 
          } 
        } 
      },
       "entry_id": NumberInt(30313) 
    }
    

1 个答案:

答案 0 :(得分:4)

Map reduce允许您处理命名键,但聚合是获得高效查询的方法。

您必须将数据建模为聚合框架的嵌入文档数组。

我为你提供了两个选择。您可以针对您的数据集测试它们,看看哪种方法更适合您。

这样的东西
"v":[
  {
    "minute":1,
    "seconds":[
      {
        "second":54,
        "data":{
         "field1":7.373158,
         "entry_id":4635,
         "field3":0.19,
         "field2":88
       }
      }
    ]
  },
  {
    "minute":2,
    "seconds":...
  }
]

现在,您可以轻松查询具有传感器读数的项目:“field1”> 2。

db.col.aggregate(
  [{"$match":{"v.seconds.data.field1":{"$gt":2}}},
   {"$unwind":"$v"}, 
   {"$match":{"v.seconds.data.field1":{"$gt":2}}},
   {"$unwind":"$v.seconds"}, 
   {"$match":{"v.seconds.data.field1":{"$gt":2}}},
   {"$project":{"data":"$v.seconds.data"}}]
)

或者,您可以按分钟拆分文档。像

这样的东西
"v":[
  {
    "second":1,
    "data":{
       "field1":7.373158,
       "entry_id":4635,
       "field3":0.19,
       "field2":88
     }
  },
  {
     "second":2,
     "data":...
  }
]

您现在可以查询(使用v.data.field1上的索引)

db.col.aggregate(
  [{"$match":{"v.data.field1":{"$gt":2}}},
   {"$unwind":"$v"}, 
   {"$match":{"v.data.field1":{"$gt":2}}},
   {"$project":{"data":"$v.data"}}]
)

您可以查询具有传感器读数的项目:“field1”> 2和“field3”> 5

使用第一个结构

db.col.aggregate(
  [{"$match":{"v":{"$elemMatch":{"seconds": {$elemMatch:{"field1":{$gt":2},"field3":{$gt":5}}}}}}},
  {"$unwind":"$v"}, 
    {"$match":{"v.seconds": {$elemMatch:{"field1":{$gt":2},"field3":{$gt":5}}}}},
  {"$unwind":"$v.seconds"}, 
  {"$project":{"data":"$v.seconds.data"}}]
)

使用第二种结构

db.col.aggregate(
  [{"$match":{"v.data":{$elemMatch:{"field1":{$gt":2},"field3":{$gt":5}}}}},
  {"$unwind":"$v"}, 
  {"$match":{"v.data.field1":{"$gt":2},"v.data.field3":{"$gt":5} }},
  {"$project":{"data":"$v.data"}}]
)

Mongo Update 3.6

带有$match

$expr接受聚合表达式。

$gt > 0 - 聚合表达式,用于检查一分钟内所有匹配秒标准的总和大于0

$objectToArray将命名键转换为键值对,然后输入条件为$filter秒,输出no匹配秒记录。

db.testcol.aggregate(
{"$match":{
  "$expr":{
    "$gt":[
      {"$sum":{
        "$map":{
          "input":{"$objectToArray":"$v"},
          "as":"secondsofminute",
          "in":{
            "$size":{
              "$filter":{
                "input":{"$objectToArray":"$$secondsofminute.v"},
                "as":"seconds",
                "cond":{"$gt":["$$seconds.v.field2",2]}
              }
            }
          }
        }
      }},
    0]
  }
}})

Mongo Update 3.4 - 将$expr替换为$redact

db.col.aggregate(
 {"$redact":{
  "$cond":{
    "if":{
      "$gt":[
        {"$sum":{
          "$map":{
            "input":{"$objectToArray":"$v"},
            "as":"secondsofminute",
            "in":{
              "$size":{
                "$filter":{
                  "input":{"$objectToArray":"$$secondsofminute.v"},
                  "as":"seconds",
                  "cond":{"$gt":["$$seconds.v.field2",2]}
                }
              }
            }
          }
        }},
        0]
    },
   "then":"$$KEEP",
   "else":"$$PRUNE"
  }
}})