我在Mongo有一套文件。说:
[
{ summary:"This is good" },
{ summary:"This is bad" },
{ summary:"Something that is neither good nor bad" }
]
我想计算每个单词的出现次数(不区分大小写),然后按降序排序。结果应该是这样的:
[
"is": 3,
"bad": 2,
"good": 2,
"this": 2,
"neither": 1,
"nor": 1,
"something": 1,
"that": 1
]
知道怎么做吗?聚合框架将是首选,因为我已经在某种程度上理解它:)
答案 0 :(得分:21)
MapReduce可能非常适合处理服务器上的文档而无需在客户端上进行操作(因为没有在数据库服务器上拆分字符串的功能(open issue)
从map
功能开始。在下面的示例中(可能需要更加健壮),每个文档都会传递给map
函数(作为this
)。代码会查找summary
字段,如果它在那里,则将其缩小,在空格上拆分,然后为找到的每个单词发出1
。
var map = function() {
var summary = this.summary;
if (summary) {
// quick lowercase to normalize per your requirements
summary = summary.toLowerCase().split(" ");
for (var i = summary.length - 1; i >= 0; i--) {
// might want to remove punctuation, etc. here
if (summary[i]) { // make sure there's something
emit(summary[i], 1); // store a 1 for each word
}
}
}
};
然后,在reduce
函数中,它会对map
函数找到的所有结果求和,并为上面emit
的每个单词返回一个离散的总和。
var reduce = function( key, values ) {
var count = 0;
values.forEach(function(v) {
count +=v;
});
return count;
}
最后,执行mapReduce:
> db.so.mapReduce(map, reduce, {out: "word_count"})
您的样本数据的结果:
> db.word_count.find().sort({value:-1})
{ "_id" : "is", "value" : 3 }
{ "_id" : "bad", "value" : 2 }
{ "_id" : "good", "value" : 2 }
{ "_id" : "this", "value" : 2 }
{ "_id" : "neither", "value" : 1 }
{ "_id" : "or", "value" : 1 }
{ "_id" : "something", "value" : 1 }
{ "_id" : "that", "value" : 1 }
答案 1 :(得分:7)
一个基本的MapReduce示例
var m = function() {
var words = this.summary.split(" ");
if (words) {
for(var i=0; i<words.length; i++) {
emit(words[i].toLowerCase(), 1);
}
}
}
var r = function(k, v) {
return v.length;
};
db.collection.mapReduce(
m, r, { out: { merge: "words_count" } }
)
这会将单词计数插入到集合名称words_count中,您可以对其进行排序(和索引)
请注意,它不使用词干,省略标点符号,处理停用词等。
另请注意,您可以通过累积重复的单词出现次数并发出计数来优化地图功能,而不仅仅是1
答案 2 :(得分:3)
您可以使用#split。 尝试以下查询
db.summary.aggregate([
{ $project : { summary : { $split: ["$summary", " "] } } },
{ $unwind : "$summary" },
{ $group : { _id: "$summary" , total : { "$sum" : 1 } } },
{ $sort : { total : -1 } }
]);
答案 3 :(得分:0)
古老的问题,但从4.2开始,现在可以使用$ regexFindAll完成。
db.summaries.aggregate([
{$project: {
occurences: {
$regexFindAll: {
input: '$summary',
regex: /\b\w+\b/, // match words
}
}
}},
{$unwind: '$occurences'},
{$group: {
_id: '$occurences.match', // group by each word
totalOccurences: {
$sum: 1 // add up total occurences
}
}},
{$sort: {
totalOccurences: -1
}}
]);
这将以以下格式输出文档:
{
_id: "matchedwordstring",
totalOccurences: number
}