简体   繁体   English

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

[英]how to use mapreduce in mongoose/mongodb query subdocument?

I implemented a simple message system in mongoose/mongodb, the schema is like the following我在 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
            } ],
});

all the messages are stored in msg nested array, now I want to query the messages from certain sender, for example,所有消息都存储在 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")
    }
   }

Here I have a document for user 'aa' who has three messages, two messages are from 'user1' and one message is from 'user2'.这里我有一个用户“aa”的文档,他有三条消息,两条消息来自“user1”,一条消息来自“user2”。 And I want to query for the messages from 'user1'我想查询来自“user1”的消息

Basically there are two ways to do it, map-reduce or aggregate.基本上有两种方法可以做到,map-reduce 或聚合。 I tried the map-reduce solution.我尝试了 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);
})

Ultimately, it works with results like最终,它适用于类似的结果

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

The result is what I want, but the format is a bit complex.结果是我想要的,但是格式有点复杂。 How can I change to make output the format like this我怎样才能改变输出这样的格式

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

and how I sort and limit the results, so I have to manually do it the reduce function?以及我如何对结果进行排序和限制,所以我必须手动执行 reduce 函数?

and one last the map reduce methods takes 28 ms to query the result, for the simulation, my collection has three documents, each document has a msg array of 4 subdocument.最后一个 map reduce 方法需要 28 ms 来查询结果,为了模拟,我的集合有三个文档,每个文档有一个 msg 数组,包含 4 个子文档。 for me , 28 ms is a bit of too much for the query, is it , now I also indexed on the 'user' field.对我来说,28 毫秒对于查询来说有点太多了,是吗,现在我还对“用户”字段进行了索引。

I am not sure how efficient it is for you, but for formatting this will work like below I made custom key names title , className and start which are not in the collection.我不确定它对您的效率如何,但是对于格式化这将像下面一样工作,我创建了不在集合中的自定义键名titleclassNamestart So store the result of mapReduce in a new collection and retrive it.因此,将 mapReduce 的结果存储在一个新集合中并检索它。 (if you are not going to run mapReduce on every single request) (如果您不打算对每个请求都运行 mapReduce)

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

If you use the map-reduce framework, which I do not recommend due to its performance, then you can use the finalize function together with the map and reduce to reshape the final result or alternatively, rename the fields during in the emit functions.如果您使用 map-reduce 框架,由于其性能,我不推荐它,那么您可以将finalize函数与mapreduce一起使用来重塑最终结果,或者,在发射函数中重命名字段。

Instead I recommend using the aggregation framework which has much better performance:相反,我建议使用具有更好性能的聚合框架:

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

Where you say,你说的地方,

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

Instead say:而是说:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM