服务器端循环

时间:2014-05-24 12:23:29

标签: mongodb mapreduce aggregation-framework

我已经解决了这个问题但是在mongodb服务器而不是客户端上寻找更好的方法。

我有一个订单集合,其中包含展示日期时间(iso日期)和产品。

{ _id:1, datetime:“T1”, product:”Apple”} 
{ _id:2, datetime:“T2”, product:”Orange”} 
{ _id:3, datetime:“T3”, product:”Pear”}
{ _id:4, datetime:“T4”, product:”Pear”} 
{ _id:5, datetime:“T5”, product:”Apple”}

目标:在给定时间(或一组时间)之前显示我的产品组中每个产品的最后一个订单。产品是有限的,已知。

例如。查询时间T6将返回:

{ _id:2, datetime:“T2”, product:”Orange”} 
{ _id:4, datetime:“T4”, product:”Pear”} 
{ _id:5, datetime:“T5”, product:”Apple”}

T4将返回:

{ _id:1, datetime:“T1”, product:”Apple”} 
{ _id:2, datetime:“T2”, product:”Orange”} 
{ _id:4, datetime:“T4”, product:”Pear”}

我通过在订单上创建综合索引来实现这一点[datetime:descending,product:ascending] 然后在java客户端上:

    findLastOrdersForTimes(times) {
    for (time: times) { 
        for (product: products) {
                db.orders.findOne(product:product, datetime: { $lt: time}}
        }
    }
}

现在它非常快,因为它命中索引并且只获取我需要的数据。但是我需要查询许多时间点(100000+),这将是网络上的大量呼叫。我的订单表也会很大。那么如何在服务器上一次点击这样做,即返回一组时间>阵列产品?如果它是oracle,则id创建一个存储过程,其中的游标会及时循环并收集每个时间点的结果,并在最后一个时间点之后到达最后一个产品时中断。我已经看过聚合框架和mapreduce,但看不出如何实现这种循环。有什么指针吗?

1 个答案:

答案 0 :(得分:0)

如果您真的想要每个产品的最后一个订单,那么t he aggregation framework会出现:

db.times.aggregate([
    { "$match": {
        "product": { "$in": products },
    }},
    { "$group": {
        "_id": "$product",
        "datetime": { "$max": "$datetime" }
    }}
])

数组为products的示例:

var products = ['Apple', 'Orange', 'Pear'];

{ "_id" : "Pear", "datetime" : "T4" }
{ "_id" : "Orange", "datetime" : "T2" }
{ "_id" : "Apple", "datetime" : "T5" }

或者,如果原始文档中的_id对您很重要,请使用$sort代替$last

db.times.aggregate([
    { "$match": {
        "product": { "$in": products },
    }},
    { "$sort": { "datetime": 1 } },
    { "$group": {
        "_id": "$product",
        "id": { "$last": "$_id" },
        "datetime": { "$last": "$datetime" }
    }}
])

这就是你最不希望在最后两种情况下做的事情。但你真正想要的指数是"产品":

db.times.ensureIndex({ "product": 1 })

因此,即使您需要使用 $match 条件对 $lt 特定时间点进行迭代,那么这样做会更好或者更好你可以修改"分组"包括" datetime"以及在 $match 中保留一组。

无论如何它似乎更好,所以这或许至少可以改变你的想法。


如果我正确地读出您的笔记,您似乎只是想找到它的头部并找到每个时间点的最后一个产品。所以声明没有太大区别:

db.times.aggregate([
    { "$match": {
        "datetime": { "$in": ["T4","T5"] },
    }},
    { "$sort": { "product": 1, "datetime": 1 } },
    { "$group": {
        "_id": "$datetime",
        "id": { "$last": "$_id" },
        "product": { "$last": "$product" }
    }}
])

理论上,这就像基于你如何呈现问题一样。我有这样的感觉,虽然你正在抽象这个" datetime"可能是实际时间戳作为日期对象类型。

所以你可能不知道你可以应用date aggregation operators,例如获得每小时的边界:

db.times.aggregate([
    { "$group": {
        "_id": {
            "year": { "$year": "$datetime" },
            "dayOfYear": { "$dayOfYear": "$datetime" },
            "hour": { "$hour": "$datetime" }
        },
        "id": { "$last": "$_id" },
        "datetime": { "$last": "$datetime" },
        "product": { "$last": "$product" }
    }}
])

如果是基于纪元的时间戳

,甚至可以使用日期数学而不是运算符
db.times.aggregate([
    { "$group": {
        "_id": {
            "$subtract": [ 
                { "$subtract": [ "$datetime", new Date("1970-01-01") ] },
                { "$mod": [ 
                    { "$subtract": [ "$datetime", new Date("1970-01-01") ] },
                    1000*60*60 
                ]}
            ]
        },
        "id": { "$last": "$_id" },
        "datetime": { "$last": "$datetime" },
        "product": { "$last": "$product" }
    }}
])

当然,您可以使用 $match $gt < $lt 为日期添加范围查询/ strong>运算符将数据保持在您特别关注的范围内。

您的整体解决方案可能是各种想法的组合,但正如我所说,您的问题似乎是在某些时间边界上匹配最后的条目,因此最后的示例可能与过滤某些产品相结合是您需要的而不是循环。findOne()请求。