如何在 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】:

我不确定它对您的效率如何,但为了格式化这将像下面那样工作,我创建了自定义键名 titleclassNamestart,它们不在集合中。 因此,将 mapReduce 的结果存储在一个新集合中并检索它。 (如果您不打算对每个请求都运行 mapReduce)

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

【讨论】:

【参考方案3】:

如果您使用 map-reduce 框架,由于其性能我不推荐,那么您可以使用 finalize 函数与 mapreduce 一起重塑最终结果,或者重命名发射函数中的字段。

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

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

【讨论】:

以上是关于如何在 mongoose/mongodb 查询子文档中使用 mapreduce?的主要内容,如果未能解决你的问题,请参考以下文章

从两个数据库集合(Mongoose/MongoDB)中查询和匹配

Mongoose Mongodb 查询对象数组

Mongoose/mongoDB 查询加入.. 但我来自 sql 背景

Mongoose/mongoDB 查询加入.. 但我来自 sql 背景

如何找到field1大于field2的文档? (Nodejs/Mongoose/MongoDB)

mongoose \ mongodb 按一些列表排序? [复制]