1. ホーム
  2. mongodb

[解決済み] MongoDB/NoSQL。ドキュメントの変更履歴を残す

2022-04-24 12:40:43

質問

データベースアプリケーションでかなり一般的な要件は、データベース内の1つまたは複数の特定のエンティティに対する変更を追跡することです。 これは、行のバージョニング、ログテーブル、履歴テーブルなどと呼ばれています(他にも呼び方があると思います)。 RDBMSでは、すべてのソーステーブルからのすべての変更を単一のテーブルに書き出すか(ログというより)、各ソーステーブルに対して個別の履歴テーブルを持つか、などさまざまな方法があります。 また、アプリケーションのコードでログを管理するか、データベースのトリガーで管理するかを選択することができます。

同じ問題をNoSQL/ドキュメントデータベース(具体的にはMongoDB)で解決するとしたら、どのような形になるのか、統一的な方法で解決するのかを考えています。 ドキュメントにバージョン番号をつけ、決して上書きしない、というような単純なものでしょうか。 実際のドキュメントとログに記録されたドキュメントとで、別々のコレクションを作成するのでしょうか? これは、クエリやパフォーマンスにどのような影響を与えますか?

とにかく、これはNoSQLデータベースでよくあるシナリオなのか、もしそうなら、一般的な解決策はあるのか?

解決方法は?

良い質問ですね、私もこの件について調べていました。

変更のたびに新しいバージョンを作成する

に行き着きました。 バージョン管理モジュール のRuby用Mongoidドライバです。私自身は使っていませんが 私が見つけたもの 各文書にバージョン番号が付きます。古いバージョンはドキュメント自体に埋め込まれています。大きな欠点は 変更のたびに文書全体が複製される これは、大きな文書を扱っている場合、多くの重複したコンテンツが保存される結果となります。しかし、この方法は、小さなサイズのドキュメントを扱っている場合や、ドキュメントをあまり頻繁に更新しない場合には、問題ありません。

変更点のみを新しいバージョンに保存する

別のアプローチとして 変更されたフィールドのみを新しいバージョンに保存する . そうすれば、履歴を「平坦化」して、ドキュメントのどのバージョンでも再構築することができます。しかし、これはかなり複雑です。モデルの変更を追跡し、アプリケーションが最新のドキュメントを再構築できるように更新と削除を保存する必要があるからです。これは、フラットなSQLテーブルではなく、構造化されたドキュメントを扱っているため、厄介なことかもしれません。

ドキュメント内に変更を保存する

また、各フィールドは個別に履歴を持つことができます。この方法では、ドキュメントを特定のバージョンに再構築することが非常に簡単になります。アプリケーションでは、明示的に変更を追跡する必要はなく、プロパティの値を変更したときに新しいバージョンを作成するだけでよいのです。ドキュメントは次のようなものになります。

{
  _id: "4c6b9456f61f000000007ba6"
  title: [
    { version: 1, value: "Hello world" },
    { version: 6, value: "Foo" }
  ],
  body: [
    { version: 1, value: "Is this thing on?" },
    { version: 2, value: "What should I write?" },
    { version: 6, value: "This is the new body" }
  ],
  tags: [
    { version: 1, value: [ "test", "trivial" ] },
    { version: 6, value: [ "foo", "test" ] }
  ],
  comments: [
    {
      author: "joe", // Unversioned field
      body: [
        { version: 3, value: "Something cool" }
      ]
    },
    {
      author: "xxx",
      body: [
        { version: 4, value: "Spam" },
        { version: 5, deleted: true }
      ]
    },
    {
      author: "jim",
      body: [
        { version: 7, value: "Not bad" },
        { version: 8, value: "Not bad at all" }
      ]
    }
  ]
}

しかし、あるバージョンでドキュメントの一部を削除したというマークは、まだいくらか厄介です。そこで state フィールドは、アプリケーションから削除/復元できる部分に対して使用します。

{
  author: "xxx",
  body: [
    { version: 4, value: "Spam" }
  ],
  state: [
    { version: 4, deleted: false },
    { version: 5, deleted: true }
  ]
}

これらのアプローチでは、最新かつフラットなバージョンを1つのコレクションに、履歴データを別のコレクションに格納することができます。これは、ドキュメントの最新版だけに興味がある場合、クエリ時間を短縮することができるはずです。しかし、最新版と履歴データの両方が必要な場合は、1回の問い合わせではなく、2回の問い合わせを行う必要があります。したがって、単一のコレクションを使用するか、2 つの別々のコレクションを使用するかは、以下の条件によって決定されます。 アプリケーションが過去のバージョンを必要とする頻度 .

この回答のほとんどは、私の考えをまとめたもので、まだ実際に試したわけではありません。振り返ってみると、重複データのオーバーヘッドがアプリケーションにとって非常に重要でない限り、最初のオプションがおそらく最も簡単で最良の解決策です。2番目のオプションは非常に複雑で、おそらく努力する価値はないでしょう。3番目の選択肢は基本的に2番目の選択肢を最適化したもので、実装はより簡単なはずですが、どうしても1番目の選択肢を採用できない場合以外は、おそらく実装の労力に見合うものではありません。

これに対するフィードバックや、他の人の解決策を楽しみにしています :)