【问题标题】:Couchdb map/reduce to return the first in a stream, then order by timeCouchdb map/reduce 返回流中的第一个,然后按时间排序
【发布时间】:2011-05-17 17:32:53
【问题描述】:

我有一个存放一系列事件的 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
           });
    }
}

还有我的reduce函数

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&group=true&group_level=2 从每个流中获取第一条消息,但是,它似乎不是按时间排序的,而是按所有者和 stream_id 排序的,就像这样

{"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"}
  }
]}

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

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

【问题讨论】:

    标签: couchdb mapreduce


    【解决方案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 的对话。而且你根本不需要任何 reduce 函数。

    【讨论】:

      【解决方案2】:

      如果我对您的理解正确,您并不是要过滤事件集合,而只是对它们进行排序。假设这是正确的,解决方案实际上非常简单,您甚至不需要 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]。我认为没有必要在键中已经存在的值中包含任何内容,这就是我将值缩减为仅消息的原因。

      【讨论】:

      • 我认为你误会了。对于任何一个所有者 + stream_id,最多可以有几千条消息。我想在每个流中显示第一条消息,然后按时间戳对它们进行排序。如果您订购它们 [owner,stream_id,receipt_time],然后使用 group_level 2 和我概述的 reduce 函数,我会通过 owner 和 stream_id 获得每个流的第一条消息,但是它们是无序的到时间。如果我包含receipt_time(即group_level 3),我会收到每条消息,这样就达不到目的了。
      • 你说得对,我确实误会了。我认为可能还有一种更简单的方法可以实际工作,但我必须考虑一下。也许其中一位 Couch 专家会出现并在此期间提供答案。
      【解决方案3】:

      嗯,我认为实际上最简单的方法就是避免这个问题。

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

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

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

      我仍然想知道是否有办法对原始数据执行此操作。

      【讨论】:

        猜你喜欢
        • 2022-01-06
        • 2012-02-28
        • 1970-01-01
        • 1970-01-01
        • 2016-11-07
        • 1970-01-01
        • 2016-01-15
        • 1970-01-01
        • 2021-11-22
        相关资源
        最近更新 更多