【问题标题】:Server Side Looping服务器端循环
【发布时间】:2014-05-24 12:23:29
【问题描述】:

我已经解决了这个问题,但正在寻找更好的方法在 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,但不知道如何实现这种循环。有什么指点吗?

【问题讨论】:

    标签: mongodb mapreduce aggregation-framework


    【解决方案1】:

    如果你真的想要每个产品的最后一个订单,那么 the 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 })
    

    因此,即使您需要在某个时间点为 $lt 使用额外的 $match 条件进行迭代,那么这样会更好,否则您可以修改“分组”以包含“日期时间”并在 $match 中保留一个集合。

    无论如何,这似乎更好,所以也许这至少有助于改变你的想法。


    如果我正确地读出了你的笔记,你似乎只是在寻找改变它的头脑并找到每个时间点的最后一个产品。所以声明并没有太大的不同:

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

    从理论上讲,这取决于您提出问题的方式。我有一种感觉,尽管您正在抽象这一点,并且“日期时间”可能是作为日期对象类型的实际时间戳。

    所以你可能不知道你可以申请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" }
        }}
    ])
    

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

    您的整体解决方案可能是想法的组合,但正如我所说,您的问题似乎是关于在特定时间范围内匹配最后一个条目,所以最后一个示例可能与过滤某些产品相结合是您需要的,而不是循环 .findOne() 请求。

    【讨论】:

    • 谢谢尼尔。但这不是我想要的。通过将 datetime
    • 编辑了我原来的问题,使其更清楚。对此感到抱歉
    • @user3671663 添加了一些看起来像您需要的更多信息,因为您似乎在特定时间间隔寻找最后的项目。
    • 谢谢尼尔,我认为这走错了方向,但约会的内容很有趣。我需要所有产品的最后一组。这是您的第一个答案。但是我希望能够在多个时间点获得相同的输出。如果没有来自客户的多次电话,我仍然看不到我怎么能做到这一点。虽然我已经发现了 eval() 命令,所以可能可以将它与服务器端 javascript 一起使用来为请求中的每个时间点调用聚合函数。
    猜你喜欢
    • 2021-04-16
    • 1970-01-01
    • 2015-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-24
    • 2012-05-13
    • 1970-01-01
    相关资源
    最近更新 更多