我对mongodb的map-reduce有一个快速的问题。我有以下文件结构
{
"_id": "ffc74819-c844-4d61-8657-b6ab09617271",
"value": {
"mid_tag": {
"0": {
"0": "Prakash Javadekar",
"1": "Shastri Bhawan",
"2": "Prime Minister's Office (PMO)",
"3": "Narendra Modi"
},
"1": {
"0": "explosion",
"1": "GAIL",
"2": "Andhra Pradesh",
"3": "N Chandrababu Naidu"
},
"2": {
"0": "Prime Minister",
"1": "Narendra Modi",
"2": "Bharatiya Janata Party (BJP)",
"3": "Government"
}
},
"total": 3
}
}
当我在此文档集合上执行地图精简代码时,我想在此命令中将total指定为排序字段
db.ana_mid_big.mapReduce(map, reduce,
{
out: "analysis_result",
sort: {"value.total": -1}
}
);
但这似乎不起作用。如何指定嵌套用于排序的键?请帮忙。
- - - - - - - - - - - - 编辑 - - - - - - - - - - - - - -------
根据评论,我在这里发布我的整个问题。我从一个包含超过350万个文档的集合开始(这只是实时文档的旧快照,已经超过了5.5 M),看起来像这样
{
"_id": ObjectId("53b394d6f9c747e33d19234d"),
"autoUid": "ffc74819-c844-4d61-8657-b6ab09617271"
"createDate": ISODate("2014-07-02T05:12:54.171Z"),
"account_details": {
"tag_cloud": {
"0": "FIFA World Cup 2014",
"1": "Brazil",
"2": "Football",
"3": "Argentina",
"4": "Belgium"
}
}
}
因此,可能有许多具有相同autoUid但具有不同(或部分相同或什至相同)tag_cloud的文档。
我已将以下map-reduce编写为生成一个中间集合,该中间集合看起来像问题开头的那个。因此,显然所有tag_clouds的集合在一个文档中都属于一个人。为此,我使用的MR代码如下所示
var map = function(){
final_val = {
tag_cloud: this.account_details.tag_cloud,
total: 1
};
emit(this.autoUid, final_val)
}
var reduce = function(key, values){
var fv = {
mid_tags: [],
total: 0
}
try{
for (i in values){
fv.mid_tags.push(values[i].tag_cloud);
fv.total = fv.total + 1;
}
}catch(e){
fv.mid_tags.push(values)
fv.total = fv.total + 1;
}
return fv;
}
db.my_orig_collection.mapReduce(map, reduce,
{
out: "analysis_mid",
sort: {createDate: -1}
}
);
当有人有一个以上的记录并且遵循归约功能时,就会出现问题1。但是,当某人只有一个而不是将其命名为“ mid_tag”时,它将保留名称“ tag_cloud”。我了解到reduce代码存在一些问题,但找不到什么。
现在,我想得出一个最终的结果,看起来像
{"_id": "ffc74819-c844-4d61-8657-b6ab09617271",
"value": {
"tags": {
"Prakash Javadekar": 1,
"Shastri Bhawan": 1,
"Prime Minister's Office (PMO)": 1,
"Narendra Modi": 2,
"explosion": 1,
"GAIL": 1,
"Andhra Pradesh": 1,
"N Chandrababu Naidu": 1,
"Prime Minister": 1,
"Bharatiya Janata Party (BJP)": 1,
"Government": 1
}
}
最终,这是每个人的一个文件,代表他们使用的标签密度。我要使用的MR代码(尚未测试)看起来像这样-
var map = function(){
var val = {};
if ("mid_tags" in this.value){
for (i in this.value.mid_tags){
for (j in this.value.mid_tags[i]){
k = this.value.mid_tags[i][j].trim();
if (!(k in val)){
val[k] = 1;
}else{
val[k] = val[k] + 1;
}
}
}
var final_val = {
tag: val,
total: this.value.total
}
emit(this._id, final_val);
}else if("tag_cloud" in this.value){
for (i in this.value.tag_cloud){
k = this.value.tag_cloud[i].trim();
if (!(k in val)){
val[k] = 1;
}else{
val[k] = val[k] + 1;
}
}
var final_val = {
tag: val,
total: this.value.total
}
emit(this._id, final_val);
}
}
var reduce = function(key, values){
return values;
}
db.analysis_mid.mapReduce(map, reduce,
{
out: "analysis_result"
}
);
最后一段代码尚未经过测试。那就是我要做的。请帮忙
您的PHP背景似乎正在显示。您表示的数据结构未以典型的JSON表示法显示数组,但是在mapReduce代码中注意到了对“推”的调用,至少在“临时文档”中,这些值实际上是数组。您似乎已经以相同的方式“标注”了它们,因此假设它们是合理的。
实际阵列是在此处存储的最佳选择,特别是考虑到所需的结果。因此,即使没有,您的原始文档也应该像这样,因为它们将在shell中表示:
{
"_id": ObjectId("53b394d6f9c747e33d19234d"),
"autoUid": "ffc74819-c844-4d61-8657-b6ab09617271"
"createDate": ISODate("2014-07-02T05:12:54.171Z"),
"account_details": {
"tag_cloud": [
"FIFA World Cup 2014",
"Brazil",
"Football",
"Argentina",
"Belgium"
]
}
}
使用类似的文档,或者如果您将其更改为类似的文档,那么执行此操作的正确工具就是聚合框架。它可以在本机代码中运行,并且不需要JavaScript解释,因此速度更快。
获得最终结果的聚合语句如下所示:
db.collection.aggregate([
// Unwind the array to "de-normalize"
{ "$unwind": "$account_details.tag_cloud" },
// Group by "autoUid" and "tag", summing totals
{ "$group": {
"_id": {
"autoUid": "$autoUid",
"tag": "$account_details.tag_cloud"
},
"total": { "$sum": 1 }
}},
// Sort the results to largest count per user
{ "$sort": { "_id.autoUid": 1, "total": -1 }
// Group to a single user with an array of "tags" if you must
{ "$group": {
"_id": "$_id.autoUid",
"tags": {
"$push": {
"tag": "$_id.tag",
"total": "$total"
}
}
}}
])
输出略有不同,但处理起来简单得多,速度也快得多:
{
"_id": "ffc74819-c844-4d61-8657-b6ab09617271",
"tags": [
{ "tag": "Narendra Modi", "total": 2 },
{ "tag": "Prakash Javadekar", "total": 1 },
{ "tag": "Shastri Bhawan", "total": 1 },
{ "tag": "Prime Minister's Office (PMO)", "total": 1 },
{ "tag": "explosion", "total": 1 },
{ "tag": "GAIL", "total": 1 },
{ "tag": "Andhra Pradesh", "total": 1 },
{ "tag": "N Chandrababu Naidu", "total": 1 },
{ "tag": "Prime Minister", "total": 1 },
{ "tag": "Bharatiya Janata Party (BJP)", "total": 1 },
{ "tag": "Government", "total": 1 }
]
}
还可以根据用户的“标签相关性得分”对标签进行排序,以确保达到良好的效果,但是您可以根据实际情况考虑删除该标签,甚至删除最后两个阶段。
到目前为止,最好的选择仍然是。了解如何使用聚合框架。如果您的“输出”仍然是“大”(超过16MB),那么请尝试迁移到MongoDB 2.6或更高版本。聚合语句可以产生一个“游标”,该游标可以被迭代而不是一次提取所有结果。也有$out
像mapReduce一样可以创建集合的运算符。
如果您的数据实际上是子文档的“哈希”格式,则您要对此表示法表示(遵循数组的PHP“转储”约定),则您需要使用mapReduce,因为聚合框架无法遍历“散列键”的表示方式。不是最佳的结构,如果是这种情况,则应更改它。
您的方法仍然有一些更正,实际上,这实际上是对最终结果的一步操作。同样,最终输出将包含“标签”的“数组”,因为将“数据”用作“键”名称确实不是一个好习惯:
db.collection.mapReduce(
function() {
var tag_cloud = this.account_details.tag_cloud;
var obj = {};
for ( var k in tag_cloud ) {
obj[tag_cloud[k]] = 1;
}
emit( this.autoUid, obj );
},
function(key,values) {
var reduced = {};
// Combine keys and totals
values.forEach(function(value) {
for ( var k in value ) {
if (!reduced.hasOwnProperty(k))
reduced[k] = 0;
reduced[k] += value[k];
}
});
return reduced;
},
{
"out": { "inline": 1 },
"finalize": function(key,value) {
var output = [];
// Mapped to array for output
for ( var k in value ) {
output.push({
"tag": k,
"total": value[k]
});
}
// Even sorted just the same
return output.sort(function(a,b) {
return ( a.total < b.total ) ? -1 : ( a.total > b.total ) ? 1 : 0;
});
}
}
)
或者,如果它实际上是原始文档中“标签”的“数组”,但是最终输出将太大,并且您无法升级到最新版本,那么初始数组处理将有所不同:
db.collection.mapReduce(
function() {
var tag_cloud = this.account_details.tag_cloud;
var obj = {};
tag_cloud.forEach(function(tag) {
obj[tag] = 1;
});
emit( this.autoUid, obj );
},
function(key,values) {
var reduced = {};
// Combine keys and totals
values.forEach(function(value) {
for ( var k in value ) {
if (!reduced.hasOwnProperty(k))
reduced[k] = 0;
reduced[k] += value[k];
}
});
return reduced;
},
{
"out": { "replace": "newcollection" },
"finalize": function(key,value) {
var output = [];
// Mapped to array for output
for ( var k in value ) {
output.push({
"tag": k,
"total": value[k]
});
}
// Even sorted just the same
return output.sort(function(a,b) {
return ( a.total < b.total ) ? -1 : ( a.total > b.total ) ? 1 : 0;
});
}
}
)
一切基本上都遵循相同的原则以达到最终结果:
在这里的mapReduce方法中,除了比您看上去想的要干净以外,这里要考虑的另一个主要问题是reducer需要“输出”与映射器完全相同的“输入”。原因实际上是有据可查的,因为实际上可以多次调用“ reducer”,基本上是“ reducing”输出,而该输出已经通过reduce处理。
通常,这就是mapReduce处理“大输入”的方式,其中给定的“键”有很多值,而“归约器”一次只能处理这么多值。例如,一个缩减器实际上可能只获取使用同一密钥发出的30个左右的文档,将这30个文档中的两组缩减为2个文档,然后最终将单个密钥还原为单个输出。
这里的最终结果与上面显示的其他输出相同,但mapReduce的区别在于所有内容都在“值”键下,因为这就是它的工作方式。
因此,有两种方法可以根据您的数据进行操作。尽量使用聚合框架,因为它要快得多,现代版本可以消耗和输出与mapReduce一样多的数据。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句