MongoDBでデータのバージョン管理をどのように実装するのか、考えを共有してください。 (私は Cassandraに関する同様の質問 を尋ねました。そのためにDBの方が良いと思う考えがあれば共有してください)
単純なアドレス帳のレコードをバージョン管理する必要があるとします。 (アドレス帳のレコードは、フラットJSONオブジェクトとして保存されます)。私はその歴史に期待しています:
私は次のアプローチを検討しています:
レコードの履歴またはレコードへの変更を保存する新しいオブジェクトコレクションを作成します。アドレス帳エントリへの参照とともに、バージョンごとに1つのオブジェクトを格納します。このようなレコードは次のようになります。
{ '_id': 'new id'、 'user':user_id、 'timestamp':timestamp、 'address_book_id' : 'アドレス帳レコードのID' 'old_record':{'first_name': 'Jon'、 'last_name': 'Doe' ...} }
このアプローチは、ドキュメントごとにバージョンの配列を保存するように変更できます。しかし、これは利点のないより遅いアプローチのようです。
アドレス帳のエントリに添付されたシリアル化(JSON)オブジェクトとしてバージョンを保存します。そのようなオブジェクトをMongoDBドキュメントにアタッチする方法がわかりません。おそらく文字列の配列として。 ( CouchDBを使用した単純なドキュメントのバージョン管理後にモデル化 )
これに飛び込むときの最初の大きな質問は、「チェンジセットをどのように保存しますか」ですか?
私の個人的なアプローチは、差分を保存することです。これらの差分の表示は実際には特別なアクションなので、差分を別の「履歴」コレクションに入れます。
別のコレクションを使用して、メモリスペースを節約します。通常、単純なクエリの完全な履歴は必要ありません。そのため、オブジェクトの履歴を保持することにより、そのデータが照会されたときに一般的にアクセスされるメモリの履歴を保持することもできます。
私の人生を楽にするために、タイムスタンプ付きの差分の辞書を含む履歴文書を作成します。このようなもの:
{
_id : "id of address book record",
changes : {
1234567 : { "city" : "Omaha", "state" : "Nebraska" },
1234568 : { "city" : "Kansas City", "state" : "Missouri" }
}
}
私の人生を本当に簡単にするために、データにアクセスするために使用するDataObject(EntityWrapperなど)のこの部分を作成します。通常、これらのオブジェクトには何らかの形式の履歴があるため、save()
メソッドを簡単にオーバーライドして、同時にこの変更を行うことができます。
更新:2015-10
JSON diffを処理するための仕様 があるようです。これは、差分/変更を保存するより堅牢な方法のようです。
「Vermongo」と呼ばれるバージョン管理スキームがあります。これは、他の返信では処理されていないいくつかの側面に対処します。
これらの問題の1つは同時更新であり、別の問題はドキュメントの削除です。
Vermongoは完全なドキュメントコピーをシャドウコレクションに保存します。いくつかのユースケースでは、これによりオーバーヘッドが大きくなりすぎる可能性がありますが、多くのことを簡素化すると思います。
現在のバージョンとすべての古いバージョンに単一のドキュメントを使用する別のソリューションを次に示します。
{
_id: ObjectId("..."),
data: [
{ vid: 1, content: "foo" },
{ vid: 2, content: "bar" }
]
}
data
にはallバージョンが含まれます。 data
配列はorderedであり、新しいバージョンは配列の最後まで$Push
edのみを取得します。 data.vid
はバージョンIDであり、増分する番号です。
最新バージョンを取得:
find(
{ "_id":ObjectId("...") },
{ "data":{ $slice:-1 } }
)
vid
:で特定のバージョンを取得
find(
{ "_id":ObjectId("...") },
{ "data":{ $elemMatch:{ "vid":1 } } }
)
指定したフィールドのみを返す:
find(
{ "_id":ObjectId("...") },
{ "data":{ $elemMatch:{ "vid":1 } }, "data.content":1 }
)
新しいバージョンを挿入:(および同時挿入/更新を防止)
update(
{
"_id":ObjectId("..."),
$and:[
{ "data.vid":{ $not:{ $gt:2 } } },
{ "data.vid":2 }
]
},
{ $Push:{ "data":{ "vid":3, "content":"baz" } } }
)
2
は現在の最新バージョンのvid
で、3
は挿入される新しいバージョンです。最新バージョンのvid
が必要なので、次のバージョンのvid
:nextVID = oldVID + 1
を簡単に取得できます。
$and
条件は、2
が最新のvid
であることを保証します。
この方法では、一意のインデックスは必要ありませんが、アプリケーションロジックは挿入時にvid
のインクリメントを処理する必要があります。
特定のバージョンを削除:
update(
{ "_id":ObjectId("...") },
{ $pull:{ "data":{ "vid":2 } } }
)
それでおしまい!
(ドキュメントごとに16MBの制限を覚えておいてください)
すぐに使えるソリューションを探しているなら-
Mongoidにはシンプルなバージョン管理機能が組み込まれています
http://mongoid.org/en/mongoid/docs/extras.html#versioning
mongoid-historyはRubyプラグインであり、監査、取り消し、およびやり直しを行う非常に複雑なソリューションを提供します
私は、データの公開バージョン、ドラフトバージョン、および履歴バージョンに対応するこのソリューションに取り組みました。
{
published: {},
draft: {},
history: {
"1" : {
metadata: <value>,
document: {}
},
...
}
}
ここでモデルをさらに説明します: http://software.danielwatrous.com/representing-revision-data-in-mongodb/
Javaでこのような何かを実装する可能性のある人のために、ここに例があります:
http://software.danielwatrous.com/using-Java-to-work-with-versioned-data/
必要に応じて、フォークできるすべてのコードを含める
Mongooseを使用している場合、次のプラグインが JSON Patch 形式の便利な実装であることがわかりました。
もう1つのオプションは、 mongoose-history プラグインを使用することです。
let mongoose = require('mongoose');
let mongooseHistory = require('mongoose-history');
let Schema = mongoose.Schema;
let MySchema = Post = new Schema({
title: String,
status: Boolean
});
MySchema.plugin(mongooseHistory);
// The plugin will automatically create a new collection with the schema name + "_history".
// In this case, collection with name "my_schema_history" will be created.
私はmeteor/MongoDBプロジェクトに以下のパッケージを使用しましたが、それはうまく機能します。主な利点は、同じドキュメント内の配列内に履歴/改訂を保存するため、追加の出版物やミドルウェアが変更履歴にアクセスする必要がないことです。限られた数の以前のバージョン(例:最後の10バージョン)をサポートでき、変更連結もサポートします(したがって、特定の期間内に発生したすべての変更は1つのリビジョンでカバーされます)。
nicklozon/meteor-collection-revisions
別のサウンドオプションは、Meteor Vermongoを使用することです( here )