通过采用最新文件减少mongodb的输出

时间:2014-06-06 13:53:00

标签: 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"
     }               

           }
}

任何拍摄?

3 个答案:

答案 0 :(得分:2)

在这里有几件事并没有真正帮助你,这实际上使一个简单的操作变得复杂。

您的日期实际上是您应该更改为正确的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。这样做的原因是该声明适用于&#34;聚合&#34;跨多个文档的所有结果。但是,如果你真的只想按照你的&#34;公司&#34;然后您可以将其作为键发出,如:

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

基本上,映射器将每个文档分开,只输出&#34; eqtcorp&#34;的每个子键。作为它自己的文件。然后将它们传递给减速器。

可以多次调用的reducer采用&#34;值的输入数组&#34;对于相同的&#34;关键&#34;并首先使用该数组上的sort处理它们。一旦排序(按升序排列),然后slice离开数组的最后三个项目,并将每个项目添加到缩小的结果中。

正如我所说的,可以多次调用reducer,因此每次传递都不一定能得到整个&#34;每个分组键的值列表。这是&#34;减少&#34;的重要组成部分。逐步地,它逐步地增加&#34;获取每个输入集并返回,最终运行已减少的结果组合,直到只有一个&#34;键&#34;只包含您想要的三个结果的值。

然后只有最终化功能,它清理了一些便利的内务管理,以简化原始子文档密钥对结果的处理。其他的东西只是选择查询和输出的选择,根据您的需要可能是另一个集合。或者您当然可以省略选择查询来处理所有文档。


如前所述,文档结构没有帮助,并且更适合于数组。所以你应该有这样一个文件:

{

    "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"
         }                  
     ]
}

虽然现在只保留日期格式,但这可以使事情变得更加清晰,因为您可以简化处理,并且如果您打算说&#34;找到前三个值,那么确实使用像聚合框架这样的事情来加快处理速度。 34;整个系列。这很简单:

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" }
    }}
])

或者在mapper上修改map reduce,因为我们实际上只是人工生成数组​​:

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

在组合键

时拆分它仍然有意义

当然,如果文档之间没有实际的聚合,你的基本意图就是在每个文档中获取数组的最后三个值,那么明确的方法是&#34; sort&#34;它们随着文档的更新和项目被添加到数组中。因此,您添加新项目的方法将变为:

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

在MongoDB 2.6之前,还需要一个 $slice 修饰符,它基本上会对数组中的项目数施加上限,但不再需要这样。对于早期版本,您可能必须为此提供上限值,例如500或大于预期结果的其他数字,除非您真的想要修剪&#34;修剪&#34;结果在哪种情况下设定了你的限制。

这里的一点是,如果没有任何聚合,那么当你只想从文档中获取该数组的最后三个值时,你只需要使用投影和那里可用的$slice运算符:

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

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


实际上,虽然您可以使用mapReduce处理现有文档,但除非您真的想要聚合结果,否则这是一个慢得多的过程。将数据更改为数组并维护排序顺序将立即为您提供所需的结果,并且查询速度非常快。

即使你的意图是聚合,那么使用数组时可用的选项要宽得多,而且通常更容易做更复杂的事情。

答案 1 :(得分:1)

如果子文档&#34; eqtcorp&#34;存储为如下所述的数组

    {
        "name" : "306113", // assigned it to a node to create an array
        "DATE" : "2014-05-05 16:43:00.000",
        "subsection_name" : "CORPORATE NEWS",
        "time" : "16:43"
    }

更新单个文档

db.collection_name.update(
         { company : "500010"}, 
         { $push : {
            eqtcorp : {
            $each: [ ],
            $sort : { "DATE" : -1},
            $slice : 3
        }
    }
})

更新所有文件

    db.collection_name.update(
    {}, // query all documents
    {
    $push : {
            eqtcorp : {
                $each: [ ],
                $sort : { "DATE" : -1},
                $slice : 3
            }
        }
    }, 
    false, 
    true // update multiple documents
)

答案 2 :(得分:0)

最简单的查询将根据日期对子文档数组进行排序,并使用“切片”运算符通过投影获取所需的数据

db.collection_name.find({"company": "500010"},{ "eqtcorp": { "$slice": -3 } }).sort({"eqtcorp.DATE":-1})