CouchDB:返回基于时间戳类型的最新文档

CouchDB:返回基于时间戳类型的最新文档,couchdb,mapreduce,couchapp,Couchdb,Mapreduce,Couchapp,我有一个系统,可以接受来自各种独特来源的状态更新,每次状态更新都会以以下结构创建一个新文档: { "type": "status_update", "source_id": "truck1231", "timestamp": 13023123123, "location": "Boise, ID" } 数据只是一个例子,但它能让人理解这个想法 现在,这些文档每隔一小时左右生成一次。一小时后,我们可能会插入: { "type": "status_update", "source_id

我有一个系统,可以接受来自各种独特来源的状态更新,每次状态更新都会以以下结构创建一个新文档:

{
 "type": "status_update",
 "source_id": "truck1231",
 "timestamp": 13023123123,
 "location": "Boise, ID"
}
数据只是一个例子,但它能让人理解这个想法

现在,这些文档每隔一小时左右生成一次。一小时后,我们可能会插入:

{
 "type": "status_update",
 "source_id": "truck1231",
 "timestamp": 13023126723,
 "location": "Madison, WI"
}
我所感兴趣的就是看到每个独特来源的最新更新。我目前正在通过拍摄以下地图来实现这一点:

function(doc) {
  if (doc.type == "status_update") {
    emit(doc.source_id, doc);
  }
}
以及减少:

function(keys, values, rereduce) {
  var winner = values[0];
  var i = values.length;
  while (i--) {
    var val = values[i];
    if (val.timestamp > winner.timestamp) winner = val;
  }
  return winner;
}
并使用
group=true
查询数据作为reduce。这将按预期工作,并仅提供最新更新的键控结果

问题是它非常慢,需要我在CouchDB配置中
减少\u limit=false

感觉必须有一种更有效的方法来做到这一点。更新同一文档不是一个选项——历史记录很重要,尽管在本例中我不需要它。在客户端处理数据也不是一个选项,因为这是一个CouchApp,而且系统中的文档量实际上相当大,通过网络发送它们并不实际


提前感谢。

我怀疑它很慢,只是因为您发出了整个文档,这意味着需要存储和移动大量数据以计算最终值。请尝试发出时间戳:

function(doc) {
  if (doc.type == "status_update") {
    emit(doc.source_id, [doc._id,doc.timestamp]);
  }
}

function(keys, values, rereduce) {
  var winner = values[0];
  var i = values.length;
  while (i--) {
    var val = values[i];
    if (val[1] > winner[1]) winner = val;
  }
  return winner;
}
这将为每个密钥获得一个
[id,timestamp]
对,而不会太慢或必须在视图中存储太多数据

在客户端上有标识符列表后,使用bulk GET API发送第二个请求:

_all_docs?keys=[id1,id2,id3,...,idn]&include_docs=true 
这将在一个请求中获取所有文档

CouchDB map/reduce是增量的,这基本上意味着结果总是被缓存,因此对同一视图(即使使用不同的搜索参数)的后续请求“免费”(或以对数时间)运行

然而,对于reduce组来说,这并不是完全正确的。有时,必须在运行中重新减少部分结果。也许这就是你要打击的

相反,使用一个数组作为键,生成如下行的地图视图(即无reduce函数)如何:

// Row diagram (pseudo-code, just to show the concept).
// Key                    , Value
// [source_id, timestamp] , null // value is not very important in this example
["truck1231", 13023123123], null
["truck1231", 13023126723], null
["truck5555", 13023126123], null
["truck6666", 13023000000], null
请注意源的所有时间戳是如何“聚集”在一起的。(实际上,它们)要查找
“truck1231”
的最新时间戳,只需请求该“束”中的最后一行。为此,使用
limit=1
参数从末尾执行降序查询。要指定“end”,请使用
{}
“high key”值作为键中的第二个元素(有关详细信息,请参阅排序规则链接)

(事实上,由于时间戳是整数,所以可以发出它们的否定,例如
-13023123123
。这将简化查询,但我不知道这对我来说似乎是在玩火。)

要生成此类行,请使用如下映射函数:

function(doc) {
  // Emit rows sorted first by source id, and second by timestamp
  if (doc.type == "status_update" && doc.timestamp) {
    emit([doc.source_id, doc.timestamp], null) // Using `doc` as the value would be fine too
  }
}

您可以使用获取每个源的最新时间戳,然后执行另一个查询以获取文档。以下是观点:

"views": {
  "latest_update": {
    "map": "function(doc) { if (doc.type == 'status_update') emit(doc.source_id, doc.timestamp); }",
    "reduce": "_stats"
  },
  "status_update": {
    "map": "function(doc) { if (doc.type == 'status_update') emit([doc.source_id, doc.timestamp], 1); }"
  }
}
首先用
group=true
查询
latest\u update
,然后用类似(正确的url编码)的内容查询
status\u update


其中,TS123和TS234是
latest_update

返回的
max
的值,更新文档本身,并将“旧”版本作为附件添加到相关文档中如何?(并对每一次新的状态更改重复该过程)哇,truck1231每小时行驶1700英里!不错。我在那里等着一个诙谐的评论;)重新阅读您的问题,我突然想到,您可能需要一个“报告”类型的结果,它可以在一个大结果中为您提供所有源的最新更新。如果是,请告诉我。我认为您的reduce功能还有改进的余地。它应该不需要禁用reduce_限制,所以我们可以调试一下,而不是这个答案。就是这样——对于我目前的解决方案,这意味着我只需要进行一个查询,但我可以使用两个查询,就像在_stats解决方案中一样,如果性能更好,还可以使用其他查询。+1是的,我忘记了_stats。我相信你的第一个观点,
最新更新
回答了OP的问题,即获得相同的结果,但性能更好;但是由于CouchDB错误(reduce函数不支持require),它还不能工作。这是完美的——我不知道_stats函数,它似乎工作得很好。谢谢一个
\u max
内置函数在这里非常有用,可以最大限度地减少数据传输。。。
"views": {
  "latest_update": {
    "map": "function(doc) { if (doc.type == 'status_update') emit(doc.source_id, doc.timestamp); }",
    "reduce": "_stats"
  },
  "status_update": {
    "map": "function(doc) { if (doc.type == 'status_update') emit([doc.source_id, doc.timestamp], 1); }"
  }
}
keys=[["truck123",TS123],["truck234",TS234],...]&include_docs=true