Modi per implementare il versioning dei dati in MongoDB


298

Puoi condividere i tuoi pensieri su come implementeresti il ​​versioning dei dati in MongoDB. (Ho fatto una domanda simile riguardo a Cassandra . Se hai qualche idea su quale db è meglio per favore, condividi)

Supponiamo che io abbia bisogno dei record di versione in una semplice rubrica. (I record della rubrica sono memorizzati come oggetti json piatti). Mi aspetto che la storia:

  • sarà usato raramente
  • sarà usato tutto in una volta per presentarlo in modo "macchina del tempo"
  • non ci saranno più versioni di poche centinaia in un singolo record. la storia non scadrà.

Sto considerando i seguenti approcci:

  • Creare una nuova raccolta di oggetti per archiviare la cronologia dei record o le modifiche ai record. Memorizzerebbe un oggetto per versione con un riferimento alla voce della rubrica. Tali registrazioni sarebbero le seguenti:

    {
     "_id": "nuovo ID",
     'user': user_id,
     "timestamp": timestamp,
     'address_book_id': 'id del record della rubrica' 
     'old_record': {'first_name': 'Jon', 'last_name': 'Doe' ...}
    }
    

    Questo approccio può essere modificato per memorizzare una matrice di versioni per documento. Ma questo sembra essere un approccio più lento senza alcun vantaggio.

  • Memorizzare le versioni come oggetto serializzato (JSON) associato alle voci della rubrica. Non sono sicuro di come allegare tali oggetti ai documenti MongoDB. Forse come una serie di stringhe. ( Modellato sulla versione semplice del documento con CouchDB )


1
Voglio sapere se questo è cambiato da quando è stata data risposta alla domanda? Non ne so molto di oplog, ma all'epoca c'era questo, farebbe la differenza?
Randy L

Il mio approccio è pensare a tutti i dati come a una serie temporale.

Risposte:


152

La prima grande domanda quando ci si immerge in questo è "come si desidera memorizzare i changeset" ?

  1. Diffs?
  2. Intere copie dei dischi?

Il mio approccio personale sarebbe quello di memorizzare le differenze. Poiché la visualizzazione di questi diff è davvero un'azione speciale, inserirei i diff in una diversa raccolta "storica".

Vorrei utilizzare la diversa raccolta per risparmiare spazio di memoria. In genere non si desidera una cronologia completa per una query semplice. Quindi, mantenendo la cronologia fuori dall'oggetto, è anche possibile tenerla fuori dalla memoria a cui si accede comunemente quando vengono interrogati quei dati.

Per semplificarmi la vita, farei in modo che un documento di storia contenga un dizionario di differenze con data e ora. Qualcosa come questo:

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

Per semplificarmi la vita, renderei questa parte dei miei DataObjects (EntityWrapper, qualunque cosa) che utilizzo per accedere ai miei dati. Generalmente questi oggetti hanno una qualche forma di storia, in modo da poter facilmente sostituire il save()metodo per apportare questa modifica allo stesso tempo.

AGGIORNAMENTO: 2015-10

Sembra che ora ci sia una specifica per la gestione dei diff JSON . Questo sembra un modo più robusto per memorizzare le differenze / modifiche.


2
Non ti preoccuperesti che tale documento History (l'oggetto modifiche) crescerà in tempo e gli aggiornamenti diventino inefficienti? O MongoDB gestisce la crescita dei documenti facilmente?
Piotr Czapla,

5
Dai un'occhiata alla modifica. L'aggiunta a changesè davvero semplice: db.hist.update({_id: ID}, {$set { changes.12345 : CHANGES } }, true)questo eseguirà un upsert che cambierà solo i dati richiesti. Mongo crea documenti con "spazio buffer" per gestire questo tipo di modifica. Controlla inoltre come cambiano i documenti in una raccolta e modifica le dimensioni del buffer per ogni raccolta. Quindi MongoDB è progettato esattamente per questo tipo di modifica (aggiungi nuova proprietà / push all'array).
Gates VP

2
Ho fatto alcuni test e in effetti la prenotazione dello spazio funziona abbastanza bene. Non sono riuscito a rilevare la perdita di prestazioni quando i record sono stati riallocati alla fine del file di dati.
Piotr Czapla,

4
Puoi usare github.com/mirek/node-rus-diff per generare diff (compatibili MongoDB) per la tua cronologia.
Mirek Rusin,

1
La patch JSON RFC fornisce un modo per esprimere difffs. Ha implementazioni in diverse lingue .
Jérôme

31

Esiste uno schema di controllo delle versioni chiamato "Vermongo" che affronta alcuni aspetti che non sono stati trattati nelle altre risposte.

Uno di questi problemi sono gli aggiornamenti simultanei, un altro è l'eliminazione dei documenti.

Vermongo archivia copie complete di documenti in una collezione shadow. Per alcuni casi d'uso ciò potrebbe causare un sovraccarico eccessivo, ma penso che semplifichi anche molte cose.

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


5
Come lo usi effettivamente?
hadees,

6
Non c'è documentazione su come questo progetto viene effettivamente utilizzato. È qualcosa che vive con Mongo in qualche modo? È una libreria Java? È semplicemente un modo di pensare al problema? Non viene data alcuna idea e nessun suggerimento.
ftrotter,

1
Questa è in realtà un'app java e il codice
relavant

20

Ecco un'altra soluzione che utilizza un singolo documento per la versione corrente e tutte le versioni precedenti:

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

datacontiene tutte le versioni. L' dataarray è ordinato , le nuove versioni verranno edite solo $pushalla fine dell'array. data.vidè l'ID versione, che è un numero crescente.

Ottieni la versione più recente:

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

Ottieni una versione specifica di vid:

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

Restituisce solo i campi specificati:

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

Inserisci nuova versione: (e impedisce l'inserimento / aggiornamento simultaneo)

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

2è l' vidattuale versione più recente ed 3è la nuova versione che viene inserita. Poiché hai bisogno delle versioni più recenti vid, è facile ottenere le versioni successive vid:nextVID = oldVID + 1 .

La $andcondizione garantirà, che 2è l'ultimavid .

In questo modo non è necessario un indice univoco, ma la logica dell'applicazione deve occuparsi di incrementare l' vidinserimento on.

Rimuovi una versione specifica:

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

Questo è tutto!

(ricorda il limite di 16 MB per documento)


Con l'archiviazione mmapv1, ogni volta che una nuova versione viene aggiunta ai dati, esiste la possibilità che il documento venga spostato.
raok1997,

Sì, è giusto. Ma se aggiungi nuove versioni ogni tanto, questo dovrebbe essere trascurabile.
Benjamin M,


9

Ho lavorato su questa soluzione che ospita versioni pubblicate, bozze e storiche dei dati:

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

Spiego ulteriormente il modello qui: http://software.danielwatrous.com/representing-revision-data-in-mongodb/

Per quelli che potrebbero implementare qualcosa di simile in Java , ecco un esempio:

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

Incluso tutto il codice che puoi fork, se vuoi

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


Roba fantastica :)
Jonathan il


4

Un'altra opzione è quella di utilizzare il plugin 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.

1

Ho usato il pacchetto seguente per un progetto meteor / MongoDB e funziona bene, il vantaggio principale è che memorizza la cronologia / le revisioni all'interno di un array nello stesso documento, quindi non è necessario aggiungere pubblicazioni o middleware per accedere alla cronologia delle modifiche . Può supportare un numero limitato di versioni precedenti (es. Ultime dieci versioni), supporta anche la concatenazione di modifiche (quindi tutte le modifiche avvenute in un determinato periodo saranno coperte da una revisione).

nicklozon / meteora-raccolta-revisioni

Un'altra opzione audio è utilizzare Meteor Vermongo ( qui )

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.