Cara untuk mengimplementasikan versi data dalam MongoDB

298

Bisakah Anda membagikan pemikiran Anda bagaimana Anda mengimplementasikan versi data dalam MongoDB. (Saya sudah bertanya pertanyaan serupa tentang Cassandra . Jika Anda memiliki pemikiran yang mana db lebih baik untuk itu silakan berbagi)

Misalkan saya perlu membuat catatan versi dalam buku alamat yang sederhana. (Catatan buku alamat disimpan sebagai objek flat json). Saya berharap sejarah:

  • akan jarang digunakan
  • akan digunakan sekaligus untuk menyajikannya dalam mode "mesin waktu"
  • tidak akan ada lebih dari beberapa ratus versi untuk satu catatan. sejarah tidak akan kedaluwarsa.

Saya sedang mempertimbangkan pendekatan berikut:

  • Buat koleksi objek baru untuk menyimpan sejarah catatan atau perubahan pada catatan. Itu akan menyimpan satu objek per versi dengan referensi ke entri buku alamat. Catatan tersebut akan terlihat sebagai berikut:

    {
     '_id': 'id baru',
     'pengguna': user_id,
     'timestamp': timestamp,
     'address_book_id': 'id dari catatan buku alamat' 
     'old_record': {'first_name': 'Jon', 'last_name': 'Doe' ...}
    }
    

    Pendekatan ini dapat dimodifikasi untuk menyimpan berbagai versi per dokumen. Tapi ini tampaknya pendekatan yang lebih lambat tanpa keuntungan apa pun.

  • Simpan versi sebagai objek serial (JSON) yang dilampirkan pada entri buku alamat. Saya tidak yakin bagaimana cara melampirkan objek tersebut ke dokumen MongoDB. Mungkin sebagai array string. ( Dimodelkan setelah Versi Dokumen Sederhana dengan CouchDB )

Piotr Czapla
sumber
1
Saya ingin tahu apakah ini telah berubah sejak pertanyaan dijawab? Saya tidak tahu banyak tentang oplog tetapi apakah ini ada pada saat itu, apakah akan ada bedanya?
Randy L
Pendekatan saya adalah menganggap semua data sebagai rangkaian waktu.

Jawaban:

152

Pertanyaan besar pertama ketika terjun ke ini adalah "bagaimana Anda ingin menyimpan perubahan" ?

  1. Berbeda?
  2. Salinan rekaman utuh?

Pendekatan pribadi saya adalah menyimpan diff. Karena tampilan diff ini benar-benar tindakan khusus, saya akan menempatkan diffs dalam koleksi "sejarah" yang berbeda.

Saya akan menggunakan koleksi yang berbeda untuk menghemat ruang memori. Anda biasanya tidak ingin riwayat lengkap untuk kueri sederhana. Jadi dengan menjaga histori dari objek, Anda juga bisa menyimpannya keluar dari memori yang biasa diakses ketika data itu ditanyakan.

Untuk membuat hidup saya mudah, saya akan membuat dokumen sejarah yang berisi kamus dengan perbedaan waktu. Sesuatu seperti ini:

{
    _id : "id of address book record",
    changes : { 
                1234567 : { "city" : "Omaha", "state" : "Nebraska" },
                1234568 : { "city" : "Kansas City", "state" : "Missouri" }
               }
}

Untuk membuat hidup saya sangat mudah, saya akan membuat bagian dari DataObjects saya (EntityWrapper, apa pun) yang saya gunakan untuk mengakses data saya. Umumnya objek-objek ini memiliki beberapa bentuk sejarah, sehingga Anda dapat dengan mudah mengganti save()metode untuk melakukan perubahan ini pada saat yang sama.

UPDATE: 2015-10

Sepertinya sekarang ada spesifikasi untuk menangani JSON diffs . Ini sepertinya cara yang lebih kuat untuk menyimpan diffs / perubahan.

Gates VP
sumber
2
Tidakkah Anda khawatir bahwa dokumen Riwayat tersebut (objek perubahan) akan tumbuh dalam waktu dan pembaruan menjadi tidak efisien? Atau apakah MongoDB menangani dokumen tumbuh dengan mudah?
Piotr Czapla
5
Lihatlah hasil edit. Menambah changessangat mudah: db.hist.update({_id: ID}, {$set { changes.12345 : CHANGES } }, true)Ini akan melakukan upert yang hanya akan mengubah data yang diperlukan. Mongo membuat dokumen dengan "ruang penyangga" untuk menangani jenis perubahan ini. Ia juga menyaksikan bagaimana dokumen dalam koleksi berubah dan memodifikasi ukuran buffer untuk setiap koleksi. Jadi MongoDB dirancang untuk jenis perubahan ini (tambahkan properti baru / push to array).
Gates VP
2
Saya telah melakukan beberapa pengujian dan memang reservasi ruang berfungsi dengan cukup baik. Saya tidak dapat menangkap kehilangan kinerja ketika catatan dialokasikan kembali ke akhir file data.
Piotr Czapla
4
Anda dapat menggunakan github.com/mirek/node-rus-diff untuk menghasilkan (MongoDB kompatibel) perbedaan untuk riwayat Anda.
Mirek Rusin
1
The JSON patch RFC menyediakan cara untuk mengekspresikan difffs. Ini memiliki implementasi dalam beberapa bahasa .
Jérôme
31

Ada skema versi yang disebut "Vermongo" yang membahas beberapa aspek yang belum ditangani di balasan lain.

Salah satu masalah ini adalah pembaruan bersamaan, yang lain menghapus dokumen.

Vermongo menyimpan salinan dokumen lengkap dalam koleksi bayangan. Untuk beberapa kasus penggunaan, ini mungkin menyebabkan terlalu banyak overhead, tapi saya pikir itu juga menyederhanakan banyak hal.

https://github.com/thiloplanz/v7files/wiki/Vermongo

Marian
sumber
5
Bagaimana Anda menggunakannya?
hadits
6
Tidak ada dokumentasi tentang bagaimana proyek ini sebenarnya digunakan. Apakah itu sesuatu yang hidup dengan Mongo? Itu adalah perpustakaan Java? Apakah itu hanya cara berpikir tentang masalah? Tidak ada ide dan tidak ada petunjuk yang diberikan.
ftrotter
1
Ini sebenarnya adalah aplikasi java dan kode yang relevan tinggal di sini: github.com/thiloplanz/v7files/blob/master/src/main/java/v7db/…
ftrotter
20

Berikut solusi lain menggunakan satu dokumen untuk versi saat ini dan semua versi lama:

{
    _id: ObjectId("..."),
    data: [
        { vid: 1, content: "foo" },
        { vid: 2, content: "bar" }
    ]
}

databerisi semua versi. The dataarray memerintahkan , versi baru hanya akan mendapatkan $pushed ke akhir array. data.vidadalah id versi, yang merupakan angka yang bertambah.

Dapatkan versi terbaru:

find(
    { "_id":ObjectId("...") },
    { "data":{ $slice:-1 } }
)

Dapatkan versi spesifik dengan vid:

find(
    { "_id":ObjectId("...") },
    { "data":{ $elemMatch:{ "vid":1 } } }
)

Hanya kembalikan bidang yang ditentukan:

find(
    { "_id":ObjectId("...") },
    { "data":{ $elemMatch:{ "vid":1 } }, "data.content":1 }
)

Sisipkan versi baru: (dan mencegah penyisipan / pembaruan bersamaan)

update(
    {
        "_id":ObjectId("..."),
        $and:[
            { "data.vid":{ $not:{ $gt:2 } } },
            { "data.vid":2 }
        ]
    },
    { $push:{ "data":{ "vid":3, "content":"baz" } } }
)

2adalah vidversi terbaru saat ini dan 3merupakan versi baru yang dimasukkan. Karena Anda memerlukan versi terbaru ini vid, sangat mudah untuk mendapatkan versi berikutnya vid: nextVID = oldVID + 1.

The $andKondisi akan memastikan, bahwa 2adalah yang terbaru vid.

Dengan cara ini tidak perlu untuk indeks yang unik, tetapi logika aplikasi harus mengurus penambahan vidsisipan on.

Hapus versi tertentu:

update(
    { "_id":ObjectId("...") },
    { $pull:{ "data":{ "vid":2 } } }
)

Itu dia!

(ingat 16MB per batas dokumen)

Benjamin M
sumber
Dengan penyimpanan mmapv1, setiap kali versi baru ditambahkan ke data, ada kemungkinan dokumen akan dipindahkan.
raok1997
Ya itu betul. Tetapi jika Anda hanya menambahkan versi baru sesekali, ini harus diabaikan.
Benjamin M
9

Saya mengerjakan solusi ini yang mengakomodasi versi data yang dipublikasikan, konsep, dan historis:

{
  published: {},
  draft: {},
  history: {
    "1" : {
      metadata: <value>,
      document: {}
    },
    ...
  }
}

Saya jelaskan model lebih lanjut di sini: http://software.danielwatrous.com/representing-revision-data-in-mongodb/

Bagi yang mungkin menerapkan sesuatu seperti ini di Jawa , berikut ini sebuah contoh:

http://software.danielwatrous.com/using-java-to-work-with-versioned-data/

Termasuk semua kode yang dapat Anda garpu, jika Anda mau

https://github.com/dwatrous/mongodb-revision-objects

Daniel Watrous
sumber
Hal-hal yang luar biasa :)
Jonathan
4

Jika Anda menggunakan luwak, saya telah menemukan plugin berikut untuk menjadi implementasi yang berguna dari format JSON Patch

luwak-tambalan-sejarah

bmw15
sumber
4

Pilihan lain adalah menggunakan plugin luwak-sejarah .

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.
Muhammad Reda
sumber
1

Saya telah menggunakan paket di bawah ini untuk proyek meteor / MongoDB, dan berfungsi dengan baik, keuntungan utama adalah bahwa ia menyimpan sejarah / revisi dalam array dalam dokumen yang sama, karenanya tidak perlu publikasi tambahan atau middleware untuk mengakses perubahan-sejarah . Ini dapat mendukung sejumlah terbatas versi sebelumnya (mis. Sepuluh versi terakhir), juga mendukung perubahan-rangkaian (sehingga semua perubahan yang terjadi dalam periode tertentu akan dicakup oleh satu revisi).

nicklozon / pengumpulan-meteor-revisi

Opsi suara lainnya adalah menggunakan Meteor Vermongo (di sini )

helcode
sumber