javascript - 通过获取最新文档来减少mongodb的输出

标签 javascript mongodb mapreduce mongodb-query aggregation-framework

好,这是我的收藏



{

"company" : "500010"

"eqtcorp" : {

    "306113" : {
        "DATE" : "2014-05-05 16:43:00.000",
        "subsection_name" : "CORPORATE NEWS",
        "time" : "16:43"
     },
     "306118" : {
         "DATE" : "2014-05-08 16:43:00.000",
         "subsection_name" : "CORPORATE NEWS",
         "time" : "18:43"
     },
     "306114" : {
         "DATE" : "2014-06-02 16:43:00.000",
        "subsection_name" : "CORPORATE NEWS",
        "time" : "20:43"
     }
     "306116" : {
         "DATE" : "2014-03-02 12:30:00.000",
        "subsection_name" : "CORPORATE NEWS",
        "time" : "20:43"
     }
     "306115" : {
         "DATE" : "2014-08-02 04:45:00.000",
        "subsection_name" : "CORPORATE NEWS",
        "time" : "20:43"
     }
     "306117" : {
         "DATE" : "2014-07-02 10:16:00.000",
        "subsection_name" : "CORPORATE NEWS",
        "time" : "20:43"
     }                  

     .
     .
     .
     .
     .
           }

}


如果我查询像

db.collection_name.find({"company": "500010"})


我会得到全部。由于“ eqtcorp”下有许多子文档,因此我只需要3个最新日期的子文档。只需在“ eqtcorp”下每个子文档中的“ DATE”字段的基础上进行反向排序,并获取前3个。这确实是一个挑战,因为我是Mongodb和mapreduce的新手。

我期望的输出是

{

"company" : "500010"

"eqtcorp" : {        

    "306113" : {
        "DATE" : "2014-05-05 16:43:00.000",
        "subsection_name" : "CORPORATE NEWS",
        "time" : "16:43"
     },
     "306118" : {
         "DATE" : "2014-05-08 16:43:00.000",
         "subsection_name" : "CORPORATE NEWS",
         "time" : "18:43"
     },

     "306116" : {
         "DATE" : "2014-03-02 12:30:00.000",
        "subsection_name" : "CORPORATE NEWS",
        "time" : "20:43"
     }               

           }
}


有拍摄吗?

最佳答案

在这里,有几件事没有真正帮助您,实际上使原本简单的操作变得复杂。

您拥有的日期实际上是字符串,您应该将其更改为正确的BSON日期类型。它将在以后可能需要的地方为您提供帮助,因此应更改它们。幸运的是,它们至少在词法上受“ YYYY-MM-DD”的作用域限制,因此可以进行排序,但是不要期望它们有太多其他用途。

您实际上还应该使用数组,而不是通过键嵌套子文档。这些确实很难查询,因为您需要指定元素的确切路径。因此,您几乎总是受限于JavaScript处理,这比其他方法要慢得多。我会在稍后介绍,但继续:

您可以使用mapReduce进行以下操作:



db.collection.mapReduce(
    function () {
        for ( var k in this.eqtcorp ) {
            this.eqtcorp[k].key = k;
            emit( 1, this.eqtcorp[k] );
        }
    },
    function (key,values) {

        var reduced = {};

        values.sort(function(a,b) {
            return (( a.DATE > b.DATE ) ? -1 : (( a.DATE < b.DATE ) ? 1 : 0));
        }).slice(-3).forEach(function(doc) {
            reduced[doc.key] = doc;
        });

        return reduced;

    },
    { 
        "query": { "company": "50010" },
        "finalize": function(key,vaue) {
            for (var k in value) {
               delete value[k].key;
            }
            return value;
        },
        "out": { "inline": 1 },
    })
)


在映射器中,我当前正在使用发射的键作为1。这样做的原因是,该语句将可以“聚合”多个文档中的所有结果。但是,如果您确实只想按照“公司”值执行此操作,则可以将其作为键发出,如:

            emit( this.company, this.eqtcorp[k] );


本质上,映射器将每个文档分解为仅输出“ eqtcorp”的每个子键作为其自己的文档。然后将这些传递给减速器。

可以多次调用的reducer将其作为“值”的输入数组作为同一个“键”,并使用该数组上的sort首先对其进行处理。排序后(升序),然后slice数组中的最后三项,并将每一项添加到简化结果中。

就像我说的那样,reducer可以被调用多次,所以每次通过不一定都获得每个分组键的“全部”值列表。这是“减少”阶段的基本部分,因为它“递增地”获取每个输入集并返回,最终运行已减少的结果的组合,直到只有一个“关键”值仅包含您所得到的三个结果为止想。

然后只有finalize函数可以清理一些便利整理工作,这些整理工作是通过原始的子文档密钥简化了结果处理的。其他事情仅仅是选择查询和输出选择,这取决于您的需求可能是另一个集合。或者,当然也可以省略选择查询来处理所有文档。



如前所述,文档结构无济于事,更适合于数组。因此,您应该拥有一个像这样的文档:

{

    "company" : "500010",

    "eqtcorp" : [

         { 
            "key": "306113" 
            "DATE" : "2014-05-05 16:43:00.000",
            "subsection_name" : "CORPORATE NEWS",
            "time" : "16:43"
         },
         {
            "key": "306118",
            "DATE" : "2014-05-08 16:43:00.000",
            "subsection_name" : "CORPORATE NEWS",
            "time" : "18:43"
         },
         {
            "key": "306114",
            "DATE" : "2014-06-02 16:43:00.000",
            "subsection_name" : "CORPORATE NEWS",
            "time" : "20:43"
         },
         {
            "key:"306116",
            "DATE" : "2014-03-02 12:30:00.000",
            "subsection_name" : "CORPORATE NEWS",
            "time" : "20:43"
         },
         { 
             "key": "306115",
             "DATE" : "2014-08-02 04:45:00.000",
             "subsection_name" : "CORPORATE NEWS",
             "time" : "20:43"
         },
         {
             "key": "306117",
             "DATE" : "2014-07-02 10:16:00.000",
             "subsection_name" : "CORPORATE NEWS",
            "time" : "20:43"
         }                  
     ]
}


尽管暂时不使用日期格式,但是这可以使事情变得更加整洁,因为您打算在整个集合中“查找最重要的三个值”,因此您可以简化处理过程,甚至可以使用聚合框架之类的方法来加快处理速度。这很简单:

db.collection.aggregate([

    // Unwind the array
    { "$unwind": "$eqtcorp" },

    // Sort the results by the dates
    { "$sort": "eqtcorp.DATE" -1 },

    // Limit the top three results
    { "$limit": 3 },

    // Optionally group back as an array
    { "$group": {
        "_id": null,
        "eqtcorp": { "$push": "$eqtcorp" }
    }}

])


那将是整个系列的结果,并非不可能获得每个公司价值的前三名,而是要更多地参与其中,因为没有比分

db.collection.aggregate([

    // Unwind the array
    { "$unwind": "$eqtcorp" },

    // Sort the results by company and date
    { "$sort": "company": 1, "eqtcorp.DATE" -1 },

    // Group back keeping the top value
    { "$group": {
        "_id": "$company",
        "all": { "$push": "$eqtcorp" },
        "one": { "$first": "$eqtcorp" }
    }},

    // Unwind again
    { "$unwind": "$all" },

    // match the "seen" value
    { "$project": {
        "all": 1,
        "one": 1,
        "seen": {
            "$eq": [ "$all", "$one" ]
        } 
    }},

    // Filter out "seen"
    { "$match": { "seen": false } },

    // Group back keeping the new top
    { "$group": {
        "_id": "$_id",
        "all": { "$push": "$all },
        "one": { "$first": "$one" },
        "two": { "$first": "$all }
    }},

    // Unwind again
    { "$unwind": "$all" },

    // Match the seen value
    { "$project": {
        "all": 1,
        "one": 1,
        "two": 1,
        "seen": {
            "$eq": [ "$all", "$two" ]
        }    
    }},

    // Filter the seen value
    { "$match": { "seen": false } },

    // Group back again 
    { "$group": {
        "_id": "$_id",
        "one": { "$first": "$one" },
        "two": { "$first": "$two },
        "three": { "$first": "$three" }
    }}
])


或在映射器处修改上面的map reduce,因为我们实际上只是人为地生成数组:

    function () {
        this.eqtcorp.forEach(doc) {
            emit( this.company, doc );
        });
    }


组合键时将其拆分仍然有意义



当然,如果文档之间没有进行实际的汇总,并且您的基本意图是仅获取每个文档中数组的最后三个值,那么明确的方法是在文档更新和项目添加时对它们进行“排序”数组。因此,您添加新项目的方法变为:

db.collection.update(
    { _id: document_id },
    {
        "$push": {
            "eqtcorp": { 
                "$each": [ { new document }, { optionally more} ],
                "$sort": { "DATE": 1 }
            }
        }
    }
);


在MongoDB 2.6之前,这还需要一个$slice修饰符,该修饰符基本上会对数组中的项目数施加上限,但不再需要。对于较早的版本,您可能必须为此提供一个上限值,例如500或比预期结果大的其他数字,除非您实际上想“修剪”结果,在这种情况下要设置您的限制。

这里的要点是,不进行任何聚合,那么当您只想从文档中获取该数组的最后三个值时,就可以使用投影和在那里可用的$slice运算符来完成此操作:

db.collection.find({},{ "eqtcorp": { "$slice": -3 } })


由于文档中的数组项已经排序,因此只需获取最后三个值即可。



因此,实际上,虽然可以使用mapReduce处理现有文档,但是除非您真的想汇总结果,否则它的过程要慢得多。通过快速,非常简单的查询,将数据更改为数组并保持排序顺序将立即获得所需的结果。

即使您的意图是聚合,使用数组时您可以使用的选项也要宽得多,并且通常更容易完成更复杂的事情。

关于javascript - 通过获取最新文档来减少mongodb的输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24083512/

相关文章:

javascript - 在 Javascript 中使用 Yahoo 管道

javascript - JQuery 切换问题,需要点击两次

javascript - 将图像序列绑定(bind)到滚动事件

node.js - 使用 Node JS 和 Mongoose 进行分页从 MongoDB 检索数据

javascript - 如何在mongodb中查找数组中的元素?

c# - mongodb中的授权

hadoop - 没有 hadoop 集群的 Windows 上的 MRUnit

javascript - 控制所选值未按预期工作

hadoop - mapreduce作业未执行

hadoop - 将文本文件读入Hbase MapReduce并将其存储到HTable