2012-04-30 76 views
2

我有接受來自各種獨特的資源狀態更新的系統,每個狀態更新會在以下結構的新文檔類型的最新文件:CouchDB的:返回基於時間戳

{ 
"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減少。這按預期工作,並提供只有最新更新的密鑰結果。

問題是它非常緩慢,需要我在CouchDB配置中的reduce_limit=false

感覺就像這樣做一定要有更高效的方法。更新相同的文檔不是一種選擇 - 歷史很重要,即使在這種情況下我不需要它。處理數據客戶端不是一個選項,因爲這是一個CouchApp,而且系統中的文檔數量實際上相當大,並且不實際可以通過網絡發送它們。

在此先感謝。

+0

如何更新文檔本身,並將「舊」版本作爲附件添加到相關文檔中? (並重復每個新的狀態變化的過程) –

+1

哇,truck1231驅動器每小時1,700英里!不錯。 – JasonSmith

+0

我在那裏等待一個機智的評論;) – radicand

回答

3

你可以使用_stats built-in reduce function每個源的最新時間戳,然後做另一個查詢來獲取日e文件。這裏的觀點:

"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_updategroup=true,然後用類似status_update(正確的URL編碼):

keys=[["truck123",TS123],["truck234",TS234],...]&include_docs=true 

其中TS123和TS234是maxlatest_update返回的值。

+0

+1是的,我忘了_stats。我相信你的第一個觀點,'latest_update'回答OP的問題,即獲得相同的結果,但具有更好的性能。 – JasonSmith

+0

我寫了燉肉來解決這個問題;但是由於CouchDB錯誤(reduce函數不支持require),它還沒有工作。 https://github.com/iriscouch/stew – JasonSmith

+0

這是完美的 - 我不知道_stats函數,它似乎工作得很好。謝謝! – radicand

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 

這將抓住所有的文件在一個請求。

3

CouchDB的地圖/減少是增量這基本上意味着結果總是緩存,對於相同的視圖(即使使用不同的搜索參數)運行「免費」(或對數時間),以便後續請求。

但是,對於減少組來說這並不嚴格。有時部分結果必須在運行中重新減少。也許這就是你打的。

相反,怎麼樣的映射圖(即,沒有降低作用),其發射的行這樣的,以與陣列作爲鍵:

// 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 

通知如何所有時間戳用於源「叢」在一起。 (其實,他們collate。)要找到"truck1231"的最新時間戳,只需要請求「叢塊」中的最後一行。爲此,請使用limit=1參數從尾部開始降序查詢。要指定「結束」,請使用{}「高鍵」值作爲鍵中的第二個元素(有關詳細信息,請參閱排序規則鏈接)。

?descending=true&limit=1&startkey=["truck1231",{}] 

(實際上,因爲你的時間戳是整數,你可以發出自己的否定,如-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 
    } 
} 
+0

重新閱讀你的問題,它發生在我身上,你可能想要一個「報告」類型的結果,爲您提供* all *源的最新更新。如果是這樣,讓我知道。我認爲還有空間來改善你的減少功能。它不應該要求禁用reduce_limit,所以也許我們可以調試一下,而不是這個答案。 – JasonSmith

+0

這就是這種情況 - 我現在擁有的解決方案意味着我只有一個查詢要做,但是我可以在_stats解決方案中使用兩個查詢,如果性能更好,我也可以使用兩個查詢。 – radicand