如何在mongose/mongodb查询子文档中使用mapreduce



我在mongoose/mongodb中实现了一个简单的消息系统,模式如下

var schema = new mongoose.Schema({
user: {type:String, required:true},
updated: {type:Date, default:new Date()},       
msgs: [ {m:String, // message itself 
d:Date,   // date of message
s: String,  // message sender
r:Boolean   // read or not
} ],
});

所有的消息都存储在msg嵌套数组中,现在我想查询来自某个发送者的消息,例如

{
"_id" : ObjectId("52c7cbe6d72ecb07f9bbc148"),
'user':'abc'
"msgs" : [{
"m" : "I want to meet you",
"d" : new Date("4/1/2014 08:52:54"),
"s" : "user1",
"r" : false,
"_id" : ObjectId("52c7cbe69d09f89025000005")
}, {
"m" : "I want to meet you",
"d" : new Date("4/1/2014 08:52:56"),
"s" : "user1",
"r" : false,
"_id" : ObjectId("52c7cbe89d09f89025000006")
}, {
"m" : "I want to meet you",
"d" : new Date("4/1/2014 08:52:58"),
"s" : "user2",
"r" : false,
"_id" : ObjectId("52c7cbea9d09f89025000007")
}
}

在这里,我有一个用户"aa"的文档,他有三条消息,两条消息来自"user1",一条来自"user2"。我想查询来自"user1"的消息

基本上有两种方法,映射减少或聚合。我尝试了地图缩减解决方案。

var o = {}; 
o.map = function() { 
this.msgs.forEach(function(msg){ 
if(msg.s == person){  emit( msg.s, {m:msg.m,d:msg.d,r:msg.r}); }
})
}       
o.reduce = function(key, values) {
var msgs = [];
for(var i=0;i<values.length;i++)
msgs.push(values[i]);       
return JSON.stringify(msgs);
}
o.query  = {user:'username'};  
o.scope = {person:'user1'};
model.mapReduce(o,function (err, data, stats) { 
console.log('map reduce took %d ms', stats.processtime)
if(err) callback(err);
else callback(null,data);
})

最终,它能处理这样的结果

[ 
{ _id: 'helxsz',
value: '[
{"m":"I want to meet you","d":"2014-01-04T08:52:54.112Z","r":false}, ....
]
]

结果是我想要的,但是格式有点复杂。我如何更改以使输出的格式像这个

{ sender: 'helxsz',
messages: '[
{"m":"I want to meet you","d":"2014-01-04T08:52:54.112Z","r":false}, ...
]
}

以及我如何对结果进行排序和限制,所以我必须手动执行reduce函数?

最后,map reduce方法需要28ms才能查询结果,对于模拟,我的集合有三个文档,每个文档都有一个由4个子文档组成的msg数组。对我来说,28毫秒对于查询来说有点太多了,是吗,现在我还在"用户"字段上建立了索引。

我不确定它对您的效率有多高,但对于格式化,它将像下面一样工作。我制作了不在集合中的自定义密钥名称titleclassNamestart。因此,将mapReduce的结果存储在一个新的集合中并重试。(如果你不打算在每个请求上运行mapReduce)

db.events.aggregate([{
$project: {
title: "$value",
className: "$_id.method",
start: "$_id.time",
_id:0 }
}]
)

如果您使用map reduce框架,由于其性能,我不建议使用该框架,那么您可以将finalize函数与mapreduce一起使用以重塑最终结果,或者在emit函数中重命名字段。

相反,我建议使用性能更好的聚合框架:

db.collection.aggregate([
{$match: {"user" : "user1"}},
{$project: {"_id": 0, "sender": "$user", "messages": "$msgs"}}
])

您所说的

发射(msg.s,{m:msg.m,d:msg.d,r:msg.r});

改为:

emit( sender: msg.s, messages: {m:msg.m,d:msg.d,r:msg.r});

最新更新