优化mongoDB查询

时间:2018-09-25 14:41:29

标签: mongodb

我有大量的文档集合,通常来说,这些文档旨在存储数百万个文档。

通常,典型文档可能非常复杂且动态,但是每个文档中都应包含一些常量字段。这些字段包括:GlobalDeviceStatusManualTestsSemiAutomaticTestsAutomaticTests。所有这三种类型的测试均由对象数组表示。每个这样的对象可能包含很多字段,但是又有一些常量字段。它们是componentNamecomponentTestStatus

{
    "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秒)。数据库中有35万个文档。

我想尽可能减少时间。

1 个答案:

答案 0 :(得分:2)

除非您将文档重组为"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]
            }
        }
    }
}