2012-01-30 18 views
2

私はMongoDBを使用してドキュメントの集合を保持しています。MongoDB - ドキュメントの最終バージョンを取得する最も効率的な方法

各ドキュメントには、ObjectIdである_id(バージョン)があります。各ドキュメントには、異なるバージョン間で共有されるdocumentIdがあります。これも、最初の文書が作成されたときに割り当てられたオブジェクトIDです。

documentIdを指定して、ドキュメントの最新バージョンを見つける最も効率的な方法は何ですか?

I.e. _id = max(_id)とdocumentId = xのレコードを取得したい

MapReduceを使用する必要はありますか?事前に

おかげで、

サム

答えて

6

は、両方のフィールド(文書ID、_id)を含むインデックスを追加し、最大(何のため)を使用していませんか? documentId = xのクエリを使用し、DESCを_idで制限し、limit(1)の結果を最新のものにします。インデックスの適切なソート順について覚えておいてください(DESCも)

そのような何か

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

他のアプローチ(より多くの非正規化は)のようなドキュメントを他のcollecionを使用することです:の

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

使用アトミック操作では、このコレクションを安全に更新できます。適切な索引を追加すると、照会が雷として高速になります。

考慮すべき点が1つあります。ObjectIDを常に最新バージョンのオーダーに安全に使用できるかどうかはわかりません。タイムスタンプを使用すると、より特定のアプローチかもしれません。

+0

パーフェクト、ありがとうダイモン。しかし、2番目のオプションに関して、新しいレコードがメイン文書コレクションに挿入されている場合、非正規化された "インデックス"コレクションをアトミックに挿入して更新するにはどうすればよいですか?最初のドキュメントのインデックスレコードが更新される前に別のドキュメントインスタンスを挿入できますか?それは理にかなっていますか?ドキュメントが変更されていないことを確認するには、findAndModify権限を使用できますか?私はそれが最新の_idを取得しているかと思います。それは要点ですか?再度、感謝します。 – sambomartin

+0

MongoDBはRDBMSのようなトリガーをサポートしていないので、アプリケーション側でそれをしています。 findAndModifyを使用すると、id/timestampが低い文書を見つけ出し、更新して更新することができます。 findAndModifyはアトミックな操作であるため、新しい値が現在のものより若い場合にのみドキュメントを更新します。この方法では、同時更新について心配する必要はありません。 – Daimon

+0

もう一度ありがとうございます。バージョン番号またはタイムスタンプを使用していると仮定すると、別のプロセスが別のドキュメントインスタンスを追加して「インデックス」ドキュメントを更新した場合、findAndModfyは失敗します。 findAndModifyは、バージョンが現在の更新よりも新しい(より大きい) "index" docを更新しようとします。これが起こったら、私は単に最新バージョンを入手してインデックス文書を再度更新しようとしますか?申し訳ありませんが何かを繰り返している場合は、私の心の中でそれを明確にしたい – sambomartin

1

私はダイモンの最初の答えと同じタイプで、sortlimitを使っていました。 _idが生成される方法のために、特に、一部のドライバ(最下位部分のインクリメントの代わりに乱数を使用するドライバ)では、これはお勧めできません。それは第2のもの(最も小さい部分としてミリ秒のようなより小さなものとは対照的に)を持っていますが、最後の数は乱数である可能性があります。したがって、ユーザーが1秒に2回セーブした場合(おそらくそうは思われませんが、気づく価値はありません)、最終的に若干の最新のドキュメントで終わる可能性があります。

ObjectIDの構造の詳細については、http://www.mongodb.org/display/DOCS/Object+IDs#ObjectIDs-BSONObjectIDSpecificationを参照してください。

私はあなたの文書に明示のversionNumberフィールドを追加することをお勧めしますので、あなたはそうのように、そのフィールドを使用して、同様の方法で問い合わせることができます:

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

編集コメントで質問に答えるために

通常のDateTimeをMongoDBに直接格納することはできますが、MongoDBの「DateTime」形式でミリ秒の精度しか格納しません。これで十分なら、それは簡単です。

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); 

NETのDateTime(ティック)/タイムスタンプの精度は、あなたはそれが同じように、仕事を得るためにキャストの束を行うことができます。

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); 

更新を再び!

BsonTimestampの実際の使い方は、2番目の解像度で一意のタイムスタンプを生成するように見えます。だから、私はコードの最後の数行にあるように、あなたは実際にそれらを乱用するつもりはないし、実際には結果の順序を乱すだろう。 TickTime(100ナノ秒)の解像度でDateTimeを保存する必要がある場合は、mongodbでソート可能な64ビットのint "チック"を保存してから、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

ありがとう、バージョンの整数のような意味ですか? – sambomartin

+0

整数カウンタを使用することは可能ですがスケーラブルではありません...高解像度のタイムスタンプを使用する方が良いでしょう - 常に2つのドキュメントが同じタイムスタンプを共有する可能性がありますが、RDBMSより良いアプローチですか? – Daimon

+0

意味をなさないミッションクリティカルではありませんが、明らかに仕事が必要です。 RDBMSのコンセプトを残すのは難しい。あなたの入力をお寄せいただきありがとうございます(両方) – sambomartin

関連する問題