2012-01-30 80 views
2

我正在使用MongoDB來保存文檔的集合。MongoDB - 獲取文檔最後一個版本的最有效方式

每個文檔都有一個_id(版本),它是一個ObjectId。每個文檔都有一個在不同版本之間共享的documentId。這也是創建第一個文檔時分配的OjectId。

在給定documentId的情況下查找文檔的最新版本的最有效方法是什麼?

I.e.我想獲得_id = max(_id)和documentId = x的記錄我是否需要使用MapReduce?

由於提前,

山姆

回答

6

添加包含這兩個字段(documentId,_id)指數,不使用MAX(什麼)?使用documentId = x查詢,通過_id命令DESC並限制(1)結果以獲取最新的。記住指數的正確排序順序(DESC也)

類似的東西

db.collection.find({documentId : "x"}).sort({_id : -1}).limit(1) 

其他方式(更非規範化)會使用其他的收集與像文件:

{ 
    documentId : "x", 
    latestVersionId : ... 
} 

使用原子操作將允許安全地更新這個集合。添加適當的索引會使查詢快速閃電。

有一件事需要考慮 - 我不確定是否ObjectID始終可以安全地用於最新版本的排序。使用時間戳可能是更確定的方法。

+0

完美,謝謝Daimon。第二種選擇只是一個問題,如果一個新記錄被插入到主文檔集合中,你如何以原子方式插入和更新非規格化的「索引」集合?在第一個文檔的索引記錄更新之前是否可以插入另一個文檔實例?那有意義嗎?你可以做一個findAndModify權利來確保文檔沒有改變?我猜如果它已經獲得最新的_id並再試一次?這是主意嗎?再次感謝。 – sambomartin 2012-01-30 20:45:35

+0

MongoDB不支持像RDBMS這樣的觸發器,因此您可以在應用程序端執行它。您可以使用findAndModify查找id/timestamp更低的文檔,然後更新並更新它。因爲findAndModify是原子操作,所以只有在新值真的比現在更新的時候纔會更新文檔 - 這樣您就不必擔心併發更新了。 – Daimon 2012-01-30 20:53:11

+0

再次感謝。假設我使用版本號或時間戳,如果另一個進程添加了另一個文檔實例並更新了「索引」文檔,則findAndModfy將失敗。 findAndModify將嘗試更新版本比當前更新更新(更大)的「索引」文檔。如果發生這種情況,我應該只是獲得最新版本,並嘗試再次更新索引文件?對不起,如果我已經重複了任何事情,只想在腦海中清楚地知道它 – sambomartin 2012-01-30 21:04:21

1

我打字的方式與戴蒙的第一個答案相同,使用sortlimit。這可能不被推薦,尤其是對於某些驅動程序(對最不重要的部分使用隨機數而不是增量),因爲生成_id的方式。它的第二個分辨率是最重要的部分,而最後一個數字可以是隨機數。因此,如果您有用戶在一秒鐘內保存兩次(可能不太可能,但值得注意),則最終文檔可能會略有不符。

有關ObjectID結構的更多詳細信息,請參閱http://www.mongodb.org/display/DOCS/Object+IDs#ObjectIDs-BSONObjectIDSpecification

我會建議增加一個明確的VERSIONNUMBER場您的文檔,這樣你就可以使用該場以類似的方式查詢,像這樣:

db.coll.find({documentId: <id>}).sort({versionNum: -1}).limit(1); 

編輯回答在評論問題

您可以直接在MongoDB中存儲常規DateTime,但它只會將毫秒精度以「DateTime」格式存儲在MongoDB中。如果這足夠好,那麼做起來會更簡單。

BsonDocument doc = new BsonDocument("dt", DateTime.UtcNow); 
coll.Insert (doc); 
doc = coll.FindOne(); 
// see it doesn't have precision... 
Console.WriteLine(doc.GetValue("dt").AsUniversalTime.Ticks); 

如果你想。再次

BsonDocument doc = new BsonDocument("dt", new BsonTimestamp(DateTime.UtcNow.Ticks)); 
coll.Insert (doc); 
doc = coll.FindOne(); 
// see it does have precision 
Console.WriteLine(new DateTime(doc.GetValue("dt").AsBsonTimestamp.Value).Ticks); 

更新:NET的DateTime(蜱)/時間戳精度,你可以做一堆石膏來得到它的工作,就好!

看起來像BsonTimestamp的真正用途是在第二個分辨率內生成唯一的時間戳。所以,你不應該像過去幾行代碼那樣濫用它們,它實際上可能會搞砸結果的順序。如果您需要以Tick(100納秒)分辨率存儲DateTime,則可能應該只存儲64位int「ticks」,這些tick會在mongodb中排序,然後在您將其拖出DateTime並將其包含在數據庫再次如下:

BsonDocument doc = new BsonDocument("dt", DateTime.UtcNow.Ticks); 
coll.Insert (doc); 
doc = coll.FindOne(); 
DateTime dt = new DateTime(doc.GetValue("dt").AsInt64); 
// see it does have precision 
Console.WriteLine(dt.Ticks); 
+0

謝謝wes,你的意思就像這個版本的整數? – sambomartin 2012-01-30 20:49:58

+0

使用整數計數器是可能的,但它根本不可擴展......使用高分辨率時間戳會是更好的方法 - 總是有兩個文檔共享相同時間戳的機會 - 但是如果它對您來說是關鍵任務,那麼可能使用RDBMS是更好的方法? – Daimon 2012-01-30 20:59:06

+0

好的。說得通。這不是任務關鍵,但顯然需要工作。很難離開RDBMS概念。感謝您的輸入(兩者) – sambomartin 2012-01-30 21:09:20

相關問題