mongodb - 优化 mongoDB 查询
问题描述
我有一个庞大的文档集合,也就是说,一般来说,意味着存储数百万个文档。
一般来说,典型的文档可能非常复杂和动态,但是每个文档中都应该存在一些常量字段。这些字段包括:GlobalDeviceStatus
, ManualTests
, SemiAutomaticTests
, AutomaticTests
. 所有三种类型的测试都由对象数组表示。每个这样的对象可能包含相当多的字段,但也有一些恒定的字段。这些是componentName
和componentTestStatus
。
{
"data": {
"globalDeviceStatus": false,
"qaOfficerID": 12121,
"ManualTests": [{
"componentName": "camera",
"componentTestStatus": true,
"x": 10
},
{
"componentName": "wifi",
"componentTestStatus": false,
"mnum": 711
}
],
"SemiAutomaticTests": [{
"componentName": "someComponent",
"componentTestStatus": true,
"someParameter": true
},
{
"componentName": "oneMoreComponent",
"componentTestStatus": false
}
],
"AutomaticTests": [{
"componentName": "anotherComponent",
"componentTestStatus": true
},
{
"componentName": "someVeryImportantComponent",
"componentTestStatus": false
}
]
},
"userID": 1
}
每个文档代表一个测试。如果 的值GlobalDeviceStatus
结果是,false
则测试失败。这也意味着它的 json 应该包含至少一个失败的组件(相反,GlobalDeviceStatus
等于的测试true
不包含失败的组件,这是非常合乎逻辑的)。我需要的是计算每个组件的失败次数,作为我的输出,我需要这样的东西:
{
"componentName": 120,
"someOtherComponentName": 31
}
每个 componentName 可能只属于一种测试类型。也就是说,如果在一个文档中它在SemiAutomaticTests
测试中,它就不能迁移到AutomaticTests
另一个文档中。
要进行此类计算,我使用以下 mongo 管道:
COUNT_CRASHES = [
{
"$match": {
"$or": [{
"data.ManualTests.componentTestStatus": false
}, {
"data.AutomaticTests.componentTestStatus": false
}, {
"data.SemiAutomaticTests.componentTestStatus": false
}]
}
}, {
"$project": {
"tests": {
"$concatArrays": [{
"$filter": {
"input": "$data.ManualTests",
"as": "mt",
"cond": {
"$eq": ["$$mt.componentTestStatus", false]
}
}
}, {
"$filter": {
"input": "$data.AutomaticTests",
"as": "at",
"cond": {
"$eq": ["$$at.componentTestStatus", false]
}
}
}, {
"$filter": {
"input": "$data.SemiAutomaticTests",
"as": "st",
"cond": {
"$eq": ["$$st.componentTestStatus", false]
}
}
}]
}
}
}, {
"$unwind": "$tests"
}, {
"$group": {
"_id": "$tests.componentName",
"count": {
"$sum": 1
}
}
}
]
它以与上面指定的格式不同的格式返回数据,但这并不重要,现在真正重要的是它需要大约 7 秒,有时是返回时间的两倍(约 14 秒)。数据库中有 350k 个文档。
我想尽可能减少时间。
解决方案
除非您将文档重组为 where "ManualTests"
,"AutomaticTests"
并且"SemiAutomaticTests"
将成为字段值而不是字段本身(这可能会允许更精简的管道),否则您可能需要创建三个这样的索引来加速$match
:
db.collection.createIndex({ "data.ManualTests.componentTestStatus": 1 })
db.collection.createIndex({ "data.AutomaticTests.componentTestStatus": 1 })
db.collection.createIndex({ "data.SemiautomaticTests.componentTestStatus": 1 })
另请注意,您的投影可以缩短为:
"$project": {
"tests": {
"$filter": {
"input": { "$concatArrays": [ "$data.ManualTests", "$data.AutomaticTests", "$data.SemiAutomaticTests" ] },
"as": "t",
"cond": {
"$eq": ["$$t.componentTestStatus", false]
}
}
}
}
推荐阅读
- ruby-on-rails - Rails 模块与模型类名称冲突
- c# - 通过 UI 自动化清除 RichTextBox 文本?
- python-3.x - 如何使用 Pandas 在数据框中生成缺失的增量行值?
- c++ - gtest - 确保一个方法之前没有被调用,但可以在某个方法调用之后被调用
- python-3.x - BeautifulSoup 连接不同段落中的单词
- python - 确定特定文件中元组中每个元素的 count = 0 - 如果为 0,则标记该元素
- three.js - 如何在不清除以前的渲染通道的情况下应用后处理 EffectComposer 渲染通道?
- javascript - 样式组件末尾的字符串是什么?
- javascript - onkeyup、onkeydown 和 onpaste 事件在 Android 设备上不起作用
- sql - 应用程序设计强制查询结构。查询从 Join 返回重复的结果。如何使用有限的选项修复结果集?