如何在 mongoose/mongodb 查询子文档中使用 mapreduce?
Posted
技术标签:
【中文标题】如何在 mongoose/mongodb 查询子文档中使用 mapreduce?【英文标题】:how to use mapreduce in mongoose/mongodb query subdocument? 【发布时间】:2014-01-22 00:54:28 【问题描述】:我在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'的消息
基本上有两种方法可以做到这一点,map-reduce 或聚合。 我尝试了 map-reduce 解决方案。
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 方法需要 28 ms 来查询结果,为了模拟,我的集合有三个文档,每个文档都有一个 msg 数组,包含 4 个子文档。对我来说,28 毫秒对于查询来说有点太长了,是吗,现在我还索引了“用户”字段。
【问题讨论】:
【参考方案1】:你说的,
发出(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);
【讨论】:
这里使用的emit
方法是为MongoDB中的mapReduce
操作定义的。【参考方案2】:
我不确定它对您的效率如何,但为了格式化这将像下面那样工作,我创建了自定义键名 title
、className
和 start
,它们不在集合中。
因此,将 mapReduce 的结果存储在一个新集合中并检索它。 (如果您不打算对每个请求都运行 mapReduce)
db.events.aggregate([
$project:
title: "$value",
className: "$_id.method",
start: "$_id.time",
_id:0
]
)
【讨论】:
【参考方案3】:如果您使用 map-reduce 框架,由于其性能我不推荐,那么您可以使用 finalize
函数与 map
和 reduce
一起重塑最终结果,或者重命名发射函数中的字段。
我建议使用性能更好的聚合框架:
db.collection.aggregate([
$match: "user" : "user1",
$project: "_id": 0, "sender": "$user", "messages": "$msgs"
])
【讨论】:
以上是关于如何在 mongoose/mongodb 查询子文档中使用 mapreduce?的主要内容,如果未能解决你的问题,请参考以下文章
从两个数据库集合(Mongoose/MongoDB)中查询和匹配
Mongoose/mongoDB 查询加入.. 但我来自 sql 背景
Mongoose/mongoDB 查询加入.. 但我来自 sql 背景