Couchdb map / reduce返回流中的第一个,然后按时间顺序排序

时间:2011-04-25 23:50:38

标签: couchdb mapreduce

我有一个持有一系列事件的couchdb。每个事件都有一个所有者,一个id,一个发生的时间和一条消息(加上一堆其他与此练习无关的东西)。我想要一个最近按时间排序的事件列表。我仔细查看了这个问题CouchDB - filter latest log per logged instance from a list并尝试使用它与reducer中的比较翻转以保留第一条消息(使用我有一个复杂密钥的表单)。

不幸的是,似乎没有做什么。

这是我的地图功能

function(doc) {
  var owner, id;
  if (doc.owner
      && doc.stream_id
      && doc.message
      && doc.receipt_time)
    {
      emit([doc.owner,doc.stream_id,doc.receipt_time],
           { owner: doc.owner,
             stream_id: doc.stream_id,
             timestamp: doc.receipt_time,
             message: doc.message
           });
    }
}

和我的缩减功能

function(keys, values) {
  var challenger, winner = null;
  for (var a = 0; a < values.length; a++) {
      challenger = values[a];
      if (! winner) {
        winner = challenger;
      } else {
        if (winner.owner !== challenger.owner
            && winner.trace_id !== challenger.trace_id ) {
          return null;
        } else if (challenger.timestamp < winner.timestamp) {
          winner = challenger;
        }
      }
    }
  return winner;
}

然后我调用?descending = true&amp; group = true&amp; group_level = 2来获取每个流的第一条消息,但是,它似乎没有按时间排序,而是由所有者和stream_id来命令,就像这样< / p>

{"rows":[
  {"key":["sam","a"],
   "value":
     {"owner":"sam","stream_id":"a","timestamp":1303754236482,"message":"foo"}
  },
  {"key":["sam","b"],
   "value":
     {"owner":"sam","stream_id":"b","timestamp":1303752578476,"message":"bar"}
  },
  {"key":["jim","j1"],
   "value":
     {"owner":"jim","stream_id":"j1","timestamp":1303625378839,"message":"stuff"}
  },
  {"key":["bob","loblaw"],
   "value":
     {"owner":"bob","stream_id":"loblaw","timestamp":1303328396532,"message":"more stuff"}
  },
  {"key":["anthony","foo"],
   "value":
     {"owner":"anthony","stream_id":"foo","timestamp":1303769699444,"message":"even more"}
  }
]}

(注意最后的条目实际上是最新的条目)。

所以我想要的是最终的观点是现在的样子,但是按时间排序。有没有办法做到这一点?

3 个答案:

答案 0 :(得分:1)

在每封邮件中存储stream_created_at时间戳。因此,对于第一条消息,您需要花时间。对于流中的每个下一条消息,您都要从前一条消息中复制它(创建一个视图以获取stream_created_at_by_stream_id)。

然后创建发出的视图:

[doc.owner,doc.stream_created_at, doc.stream_id, doc.receipt_time]

这会将来自同一个流的消息组合在一起,同时保留时间顺序。 stream.id将确保在同时创建两个流时来自不同流的消息不会混淆。 receipt_time将按时间顺序对流中的消息进行排序。

所以最后你会得到像对话一样的Facebook。而且你根本不需要任何减少功能。

答案 1 :(得分:0)

如果我理解正确,你不是要过滤事件集合,而只是订购它们。假设这是正确的,解决方案实际上非常简单,您甚至不需要reduce函数。在map函数中发出的键用于对视图进行排序,首先是键中的第一个键,然后通过它完成其余部分。换句话说,如果你想按stream_id和receipt_time排序,你对emit的调用将如下所示:

emit([doc.stream_id,doc.receipt_time,doc.owner], doc.message);

当然,如果您想要按receipt_time然后stream_id排序,则密钥将改为[doc.receipt_time,doc.stream_id,doc.owner]。我认为没有必要在键中已经存在的值中包含任何内容,这就是为什么我将值减少到只是消息。

答案 2 :(得分:0)

嗯,我认为最简单的就是避免这个问题。

由于我控制发送事件的软件,我刚刚在流中的第一个文档中添加了"start":true,字段,然后视图函数只发出具有该值的事件。

这意味着我无法获取历史数据,但这没关系,因为这主要是为了检查最近的流。

我尝试的另一个替代方法是添加一个列表函数,该函数在一个视图上发送每个所有者stream_id的第一个实例,该视图的键是[timestamp,owner,stream_id],然而,这会遇到限制它的问题。 t限制最终渲染列表但限制原始视图,因此到目前为止,额外的键效果最好。

我仍然想知道是否有一些方法可以使用原始数据。