我有一个系统接受来自各种独特来源的状态更新,每个状态更新都会在以下结构中创建一个新文档:
{
"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配置中reduce_limit=false
。
感觉必须有一种更有效的方法来做到这一点。更新同一文档不是一个选项 - 历史很重要,即使在这种情况下我不需要它。处理数据客户端也不是一个选项,因为这是一个CouchApp,系统中的文档数量实际上非常大,并且无法通过网络发送它们。
提前致谢。
答案 0 :(得分:3)
CouchDB map / reduce是 incremental ,这基本上意味着结果总是被缓存,因此对相同视图的后续请求(即使使用不同的搜索参数)也会“免费”运行(或以对数时间运行)
但是,对于减少组,这并不完全正确。有时必须在运行中重新减少部分结果。也许这就是你要打的。
相反,如果发出这样的行的地图视图(即没有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
注意源“clump”的所有时间戳如何组合在一起。 (实际上,他们collate。)要查找"truck1231"
的最新时间戳,只需请求“clump”中的最后一行。为此,请使用limit=1
参数从末尾执行降序查询。要指定“结束”,请使用{}
“高键”值作为键中的第二个元素(有关详细信息,请参阅排序链接)。
?descending=true&limit=1&startkey=["truck1231",{}]
(实际上,由于你的时间戳是整数,你可以发出它们的否定,例如-13023123123
。这会简化你的查询,但是我不知道 - 这似乎对我起火了。)
要生成这些行,我们使用这样的map函数:
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
}
}
答案 1 :(得分:3)
您可以使用_stats
built-in reduce function获取每个来源的最新时间戳,然后执行另一个查询来获取文档。以下是观点:
"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); }"
}
}
首先使用latest_update
查询group=true
,然后status_update
查询(正确的网址编码):
keys=[["truck123",TS123],["truck234",TS234],...]&include_docs=true
其中TS123和TS234是max
返回的latest_update
的值。
答案 2 :(得分:1)
我怀疑它只是因为你发出整个文档而很慢,这意味着需要存储大量数据并移动来计算你的最终值。请尝试发出时间戳:
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]
对,而不会太慢,或者必须在视图中存储太多数据。
在客户端上有标识符列表后,使用批量GET API发送第二个请求:
_all_docs?keys=[id1,id2,id3,...,idn]&include_docs=true
这将在一个请求中获取所有文档。