MongoDB两级未知父键嵌套字段值高效查询方案问询
高效查询MongoDB嵌套时序传感器数据的方案
嘿,我懂你们现在的处境——之前按时间段查时序数据的时候顺得不行,但现在要按传感器读数的数值来查,那嵌套的m*和s*层级确实有点麻烦,谁也不想写嵌套循环对吧?别担心,MongoDB的内置操作符就能帮你搞定这个事儿,不用绕弯子写复杂的循环逻辑。
核心思路:把动态嵌套对象转成数组遍历
因为你的v字段下都是动态命名的m(分钟)和s(秒)键,没法直接用普通的点符号来查询。这时候我们可以用$objectToArray操作符,把这些动态键的对象转换成键值对数组,之后就能用数组操作符来遍历判断条件了。
具体查询示例
1. 查询所有包含field1>2的条目
这个查询会匹配任意分钟任意秒下有field1大于2的文档:
db.sensor_data.find({ $expr: { $anyElementTrue: { $map: { input: { $objectToArray: "$v" }, // 把v下的所有m*转成[{k: "m1", v: {...}}, ...]格式的数组 as: "minuteItem", in: { $anyElementTrue: { $map: { input: { $objectToArray: "$$minuteItem.v" }, // 把当前m下的所有s*转成数组 as: "secondItem", in: { $gt: ["$$secondItem.v.field1", 2] } // 判断当前s条目下的field1是否大于2 } } } } } } })
这里的$anyElementTrue是关键,只要层级里有任意一个条目满足条件,就会返回true,匹配整个文档。
2. 查询满足field1>2且field3>5的条目
这里分两种情况:
情况一:同一秒条目下同时满足两个条件
也就是要找某个s*里同时有field1>2和field3>5的文档:
db.sensor_data.find({ $expr: { $anyElementTrue: { $map: { input: { $objectToArray: "$v" }, as: "minuteItem", in: { $anyElementTrue: { $map: { input: { $objectToArray: "$$minuteItem.v" }, as: "secondItem", in: { $and: [ { $gt: ["$$secondItem.v.field1", 2] }, { $gt: ["$$secondItem.v.field3", 5] } ] } } } } } } } })
情况二:文档中存在任意field1>2且存在任意field3>5(无需同一秒)
如果不需要两个条件在同一个s*条目里,只要文档里有满足各自条件的条目就行,可以拆成两个独立的$expr条件:
db.sensor_data.find({ $and: [ { $expr: { $anyElementTrue: { $map: { input: { $objectToArray: "$v" }, as: "minuteItem", in: { $anyElementTrue: { $map: { input: { $objectToArray: "$$minuteItem.v" }, as: "secondItem", in: { $gt: ["$$secondItem.v.field1", 2] } } } } } } } }, { $expr: { $anyElementTrue: { $map: { input: { $objectToArray: "$v" }, as: "minuteItem", in: { $anyElementTrue: { $map: { input: { $objectToArray: "$$minuteItem.v" }, as: "secondItem", in: { $gt: ["$$secondItem.v.field3", 5] } } } } } } } } ] })
性能优化小贴士
- 先缩小查询范围:如果你的查询经常会结合设备ID(比如
c_id_s)或者时间范围(c_t),一定要先给这些字段建复合索引,比如db.sensor_data.createIndex({c_id_s: 1, c_t: 1})。先过滤出目标设备、目标时间范围的文档,再做嵌套字段的条件判断,能大幅提升查询速度。 - 数据模型调整(可选):如果这类数值查询非常频繁,而且数据量还在持续增长,可以考虑把
v的结构改成数组形式,比如:
这样查询时可以直接用数组操作符,还能给{ "_id": ObjectId("..."), "c_id": "1017", "c_t": 1516096800, "v": [ { "m": 1, "s": 54, "field1":7.37, "field2":88, ... }, { "m":31, "s":54, "field1":5.98, ... }, ... ] }v.field1这类字段建索引(比如db.sensor_data.createIndex({"v.field1": 1}))。不过这个调整要权衡,因为会改变原来按时间查询的结构,得根据业务的查询优先级来决定。
内容的提问来源于stack exchange,提问作者leon
相关产品推荐
相关产品推荐

