如何使用组优化聚合mongodb查询?

时间:2017-07-05 09:21:36

标签: mongodb optimization mongodb-query aggregation-framework

我有三个查询按货币和价格/利润分组获取所需数据。

一项查询在230 000项上花费约1.3秒。

一个项目如下:

{
    "_id" : ObjectId("590e59fca0404a6e5577302b"),
    "make_name" : "Peugeot",
    "model_name" : "307",
    "car_id" : NumberInt("396554354"),
    "title" : "Sell",
    "description" : "Cool",
    "site_name" : "olx.ua",
    "first_registration" : ISODate("2002-01-01T00:00:00.000Z"),
    "fuel" : "Petrol",
    "mileage" : NumberInt("250000"),
    "category" : "Limousine",
    "horse_power" : null,
    "cubic_capacity" : NumberInt("1600"),
    "transmission" : "Manual",
    "price" : NumberInt("5050"),
    "currency" : "USD",
    "negotiable" : true,
    "profit" : NumberInt("-8"),
    "owners_count" : NumberInt("2"),
    "color" : NumberInt("3"),
    "condition" : NumberInt("4"),
    "updated_at" : ISODate("2017-06-01T03:51:34.000Z"),
    "rear_camera" : false,
    "ABS" : false,
    "four_wheel_drive" : false,
    "bluetooth" : false,
    "board_computer" : false,
    "cd_player" : false,
    "electric_mirrors" : false,
    "electric_windows" : true,
    "parking_assistance" : false,
    "handsfree" : false,
    "guarantee" : false,
    "head_up_display" : false,
    "has_inspection" : false,
    "air_conditioning" : false,
    "alloy_wheel_rims" : false,
    "multi_func_steering_wheel" : false,
    "navigation" : false,
    "non_smoking_car" : false,
    "panorama_roof" : false,
    "particle_filter" : false,
    "rain_sensor" : false,
    "full_service_history" : false,
    "power_steering" : false,
    "sunroof" : false,
    "seat_heating" : false,
    "sports_suspension" : false,
    "sports_seats" : false,
    "pre_heating" : false,
    "start_stop" : false,
    "taxi" : false,
    "tax_paid" : true,
    "cruise_control" : false,
    "xenon_headlights" : true,
    "security" : false,
    "sport_package" : false,
    "business" : true,
    "damaged" : false,
    "price_100" : 5000,
    "profit_100" : 0
},

我的查询是:

db.cars.aggregate([{
    '$match': {
        '$and': [
            { 'first_registration': { '$gte': ISODate("2000-01-01") } },
            { 'first_registration': { '$lte': ISODate("2017-01-01") } },
            { 'price': { '$gte': 0 } },
            { 'price': { '$lte': 60000 } },
            { 'profit': { '$exists': true } },
            { 'profit': { '$gte': -20000 } },
            { 'profit': { '$lte': 30000 } },
            { 'updated_at': { '$gte': ISODate("2017-06-04") } },
            { 'currency': 'USD' },
            { 'damaged': false }]
    }
},
    {
        '$group': {
            '_id': {
                'price': {
                    '$subtract': ['$price',
                        { '$mod': ['$price', 100] }]
                },
                'profit': { '$subtract': ['$profit', { '$mod': ['$profit', 100] }] }
            },
            'car_id': { '$first': '$car_id' },
            'currency': { '$first': '$currency' },
            'price': { '$first': '$price' },
            'profit': { '$first': '$profit' }
        }
    }])

我需要在一组指定的价格/利润中获得第一项。

示例:10辆车的价格/利润为100-160美元,因此只有一辆车会被退回进行此类查询,因为这辆车的组(数据)点价格为100,利润为100.我希望这样做。

首先"匹配"查询大约需要0.012秒才能获得15万个项目。

所以我认为问题出在群组查询中。

我尝试预先构建数学运算减法和mod:

db.cars.find({
    'profit': {'$exists': true}, 
    'price_100': {'$exists': false}, }).snapshot().forEach(function(doc){
         db.cars.update({_id:doc._id}, {$set:{
             "price_100":doc.price - (doc.price % 100),
             "profit_100": doc.profit - (doc.profit % 100)

         }});
    });

然后我的查询开始看起来像:

db.cars.aggregate(
    [
        {
    '$match': {
        '$and': [
            { 'first_registration': { '$gte': ISODate("2000-01-01") } },
            { 'first_registration': { '$lte': ISODate("2017-01-01") } },
            { 'price': { '$gte': 0 } },
            { 'price': { '$lte': 60000 } },
            { 'profit': { '$exists': true } },
            { 'profit': { '$gte': -20000 } },
            { 'profit': { '$lte': 30000 } },
            { 'updated_at': { '$gte': ISODate("2017-06-04") } },
            { 'currency': 'USD' },
            { 'damaged': false }]
    }
},
    {
        '$group': {
            '_id': {
                'price': '$price_100',
                'profit': '$profit_100',
            },
            'car_id': { '$first': '$car_id' },
            'currency': { '$first': '$currency' },
            'price': { '$first': '$price' },
            'profit': { '$first': '$profit' }
        }
    }])

不幸的是,比原来的需要300毫秒

解释我的问题:

{
    "stages" : [
        {
            "$cursor" : {
                "query" : {
                    "$and" : [
                        {
                            "first_registration" : {
                                "$gte" : ISODate("2000-01-01T00:00:00.000Z")
                            }
                        },
                        {
                            "first_registration" : {
                                "$lte" : ISODate("2017-01-01T00:00:00.000Z")
                            }
                        },
                        {
                            "price" : {
                                "$gte" : 0
                            }
                        },
                        {
                            "price" : {
                                "$lte" : 60000
                            }
                        },
                        {
                            "profit" : {
                                "$exists" : true
                            }
                        },
                        {
                            "profit" : {
                                "$gte" : -20000
                            }
                        },
                        {
                            "profit" : {
                                "$lte" : 30000
                            }
                        },
                        {
                            "updated_at" : {
                                "$gte" : ISODate("2017-06-04T00:00:00.000Z")
                            }
                        },
                        {
                            "currency" : "USD"
                        },
                        {
                            "damaged" : false
                        }
                    ]
                },
                "fields" : {
                    "car_id" : NumberInt("1"),
                    "currency" : NumberInt("1"),
                    "price" : NumberInt("1"),
                    "price_100" : NumberInt("1"),
                    "profit" : NumberInt("1"),
                    "profit_100" : NumberInt("1"),
                    "_id" : NumberInt("0")
                },
                "queryPlanner" : {
                    "plannerVersion" : NumberInt("1"),
                    "namespace" : "master_test.cars",
                    "indexFilterSet" : false,
                    "parsedQuery" : {
                        "$and" : [
                            {
                                "currency" : {
                                    "$eq" : "USD"
                                }
                            },
                            {
                                "damaged" : {
                                    "$eq" : false
                                }
                            },
                            {
                                "first_registration" : {
                                    "$lte" : ISODate("2017-01-01T00:00:00.000Z")
                                }
                            },
                            {
                                "price" : {
                                    "$lte" : 60000
                                }
                            },
                            {
                                "profit" : {
                                    "$lte" : 30000
                                }
                            },
                            {
                                "first_registration" : {
                                    "$gte" : ISODate("2000-01-01T00:00:00.000Z")
                                }
                            },
                            {
                                "price" : {
                                    "$gte" : 0
                                }
                            },
                            {
                                "profit" : {
                                    "$gte" : -20000
                                }
                            },
                            {
                                "updated_at" : {
                                    "$gte" : ISODate("2017-06-04T00:00:00.000Z")
                                }
                            },
                            {
                                "profit" : {
                                    "$exists" : true
                                }
                            }
                        ]
                    },
                    "winningPlan" : {
                        "stage" : "COLLSCAN",
                        "filter" : {
                            "$and" : [
                                {
                                    "currency" : {
                                        "$eq" : "USD"
                                    }
                                },
                                {
                                    "damaged" : {
                                        "$eq" : false
                                    }
                                },
                                {
                                    "first_registration" : {
                                        "$lte" : ISODate("2017-01-01T00:00:00.000Z")
                                    }
                                },
                                {
                                    "price" : {
                                        "$lte" : 60000
                                    }
                                },
                                {
                                    "profit" : {
                                        "$lte" : 30000
                                    }
                                },
                                {
                                    "first_registration" : {
                                        "$gte" : ISODate("2000-01-01T00:00:00.000Z")
                                    }
                                },
                                {
                                    "price" : {
                                        "$gte" : 0
                                    }
                                },
                                {
                                    "profit" : {
                                        "$gte" : -20000
                                    }
                                },
                                {
                                    "updated_at" : {
                                        "$gte" : ISODate("2017-06-04T00:00:00.000Z")
                                    }
                                },
                                {
                                    "profit" : {
                                        "$exists" : true
                                    }
                                }
                            ]
                        },
                        "direction" : "forward"
                    },
                    "rejectedPlans" : [ ]
                }
            }
        },
        {
            "$group" : {
                "_id" : {
                    "price" : "$price_100",
                    "profit" : "$profit_100"
                },
                "car_id" : {
                    "$first" : "$car_id"
                },
                "currency" : {
                    "$first" : "$currency"
                },
                "price" : {
                    "$first" : "$price"
                },
                "profit" : {
                    "$first" : "$profit"
                }
            }
        }
    ],
    "ok" : 1
}

为什么有人会想到3个查询?我的数据库中有3种货币:USD,EUR和PLN,因此我提出了3个请求。目前,我不知道如何统一查询。

NEIL更新:

在实施建议后,我能够将时间从1.3秒减少到1秒。

查询看起来像:

db.cars.aggregate([{
    '$match': {
        '$and': [
            { 'first_registration': { '$gte': ISODate("2000-01-01"), '$lte': ISODate("2017-01-01") } },
            { 'price': { '$gte': 0, '$lte': 60000 } },
            { 'profit': { '$exists': true, '$gte': -20000, '$lte': 30000  } },
            { 'updated_at': { '$gte': ISODate("2017-06-04") } },
            { 'currency': 'USD' },
            { 'damaged': false }]
    }
},
    {
        '$group': {
            '_id': {
                'price': {
                    '$subtract': ['$price',
                        { '$mod': ['$price', 100] }]
                },
                'profit': { '$subtract': ['$profit', { '$mod': ['$profit', 100] }] }
            },
            'car_id': { '$first': '$car_id' },
            'currency': { '$first': '$currency' },
            'price': { '$first': '$price' },
            'profit': { '$first': '$profit' }
        }
    }])

并解释:

{
    "stages" : [
        {
            "$cursor" : {
                "query" : {
                    "$and" : [
                        {
                            "first_registration" : {
                                "$gte" : ISODate("2000-01-01T00:00:00.000Z"),
                                "$lte" : ISODate("2017-01-01T00:00:00.000Z")
                            }
                        },
                        {
                            "price" : {
                                "$gte" : 0,
                                "$lte" : 60000
                            }
                        },
                        {
                            "profit" : {
                                "$exists" : true,
                                "$gte" : -20000,
                                "$lte" : 30000
                            }
                        },
                        {
                            "updated_at" : {
                                "$gte" : ISODate("2017-06-04T00:00:00.000Z")
                            }
                        },
                        {
                            "currency" : "USD"
                        },
                        {
                            "damaged" : false
                        }
                    ]
                },
                "fields" : {
                    "car_id" : NumberInt("1"),
                    "currency" : NumberInt("1"),
                    "price" : NumberInt("1"),
                    "profit" : NumberInt("1"),
                    "_id" : NumberInt("0")
                },
                "queryPlanner" : {
                    "plannerVersion" : NumberInt("1"),
                    "namespace" : "master_test.cars",
                    "indexFilterSet" : false,
                    "parsedQuery" : {
                        "$and" : [
                            {
                                "currency" : {
                                    "$eq" : "USD"
                                }
                            },
                            {
                                "damaged" : {
                                    "$eq" : false
                                }
                            },
                            {
                                "first_registration" : {
                                    "$lte" : ISODate("2017-01-01T00:00:00.000Z")
                                }
                            },
                            {
                                "price" : {
                                    "$lte" : 60000
                                }
                            },
                            {
                                "profit" : {
                                    "$lte" : 30000
                                }
                            },
                            {
                                "first_registration" : {
                                    "$gte" : ISODate("2000-01-01T00:00:00.000Z")
                                }
                            },
                            {
                                "price" : {
                                    "$gte" : 0
                                }
                            },
                            {
                                "profit" : {
                                    "$gte" : -20000
                                }
                            },
                            {
                                "updated_at" : {
                                    "$gte" : ISODate("2017-06-04T00:00:00.000Z")
                                }
                            },
                            {
                                "profit" : {
                                    "$exists" : true
                                }
                            }
                        ]
                    },
                    "winningPlan" : {
                        "stage" : "FETCH",
                        "filter" : {
                            "$and" : [
                                {
                                    "first_registration" : {
                                        "$lte" : ISODate("2017-01-01T00:00:00.000Z")
                                    }
                                },
                                {
                                    "price" : {
                                        "$lte" : 60000
                                    }
                                },
                                {
                                    "profit" : {
                                        "$lte" : 30000
                                    }
                                },
                                {
                                    "first_registration" : {
                                        "$gte" : ISODate("2000-01-01T00:00:00.000Z")
                                    }
                                },
                                {
                                    "price" : {
                                        "$gte" : 0
                                    }
                                },
                                {
                                    "profit" : {
                                        "$gte" : -20000
                                    }
                                },
                                {
                                    "profit" : {
                                        "$exists" : true
                                    }
                                }
                            ]
                        },
                        "inputStage" : {
                            "stage" : "IXSCAN",
                            "keyPattern" : {
                                "updated_at" : 1,
                                "currency" : 1,
                                "damaged" : 1
                            },
                            "indexName" : "updated_at_1_currency_1_damaged_1",
                            "isMultiKey" : false,
                            "multiKeyPaths" : {
                                "updated_at" : [ ],
                                "currency" : [ ],
                                "damaged" : [ ]
                            },
                            "isUnique" : false,
                            "isSparse" : false,
                            "isPartial" : false,
                            "indexVersion" : NumberInt("2"),
                            "direction" : "forward",
                            "indexBounds" : {
                                "updated_at" : [
                                    "[new Date(1496534400000), new Date(9223372036854775807)]"
                                ],
                                "currency" : [
                                    "[\"USD\", \"USD\"]"
                                ],
                                "damaged" : [
                                    "[false, false]"
                                ]
                            }
                        }
                    },
                    "rejectedPlans" : [ ]
                }
            }
        },
        {
            "$group" : {
                "_id" : {
                    "price" : {
                        "$subtract" : [
                            "$price",
                            {
                                "$mod" : [
                                    "$price",
                                    {
                                        "$const" : 100
                                    }
                                ]
                            }
                        ]
                    },
                    "profit" : {
                        "$subtract" : [
                            "$profit",
                            {
                                "$mod" : [
                                    "$profit",
                                    {
                                        "$const" : 100
                                    }
                                ]
                            }
                        ]
                    }
                },
                "car_id" : {
                    "$first" : "$car_id"
                },
                "currency" : {
                    "$first" : "$currency"
                },
                "price" : {
                    "$first" : "$price"
                },
                "profit" : {
                    "$first" : "$profit"
                }
            }
        }
    ],
    "ok" : 1
}

在预建的字段price_100和profit_100上运行仍然是1.3秒,但现在我们对于非预建的查询少了300毫秒,很好!

0 个答案:

没有答案