web-dev-qa-db-fra.com

Méthodes d'implémentation du versioning des données dans MongoDB

Pouvez-vous partager votre opinion sur la mise en œuvre du contrôle de version des données dans MongoDB? (J'ai demandé question similaire concernant Cassandra . Si vous avez des idées qui db est meilleur pour cela s'il vous plaît partager)

Supposons que j'ai besoin de la version des enregistrements dans un simple carnet d'adresses. (Les enregistrements du carnet d’adresses sont stockés sous forme d’objets json plats). J'attends que l'histoire:

  • sera utilisé rarement
  • sera utilisé tout à la fois pour le présenter dans une "machine à remonter le temps"
  • il n'y aura pas plus de versions que quelques centaines à un seul enregistrement. l'histoire n'expire pas.

J'envisage les approches suivantes:

  • Créez une nouvelle collection d'objets pour stocker l'historique des enregistrements ou les modifications apportées aux enregistrements. Il stockerait un objet par version avec une référence à l'entrée du carnet d'adresses. Ces enregistrements se présenteraient comme suit:

     {
     '_id': 'nouvel identifiant', 
     'utilisateur': id_utilisateur, 
     'horodatage': horodatage, 
     'adresse_book_id_id' : 'id de l'enregistrement du carnet d'adresses' 
     'old_record': {'first_name': 'Jon', 'last_name': 'Doe' ...} 
    } 
    

    Cette approche peut être modifiée pour stocker un tableau de versions par document. Mais cela semble être une approche plus lente sans aucun avantage.

  • Stocker les versions en tant qu'objet sérialisé (JSON) attaché aux entrées du carnet d'adresses. Je ne suis pas sûr de savoir comment attacher de tels objets à des documents MongoDB. Peut-être comme un tableau de chaînes. ( modélisé d'après le versioning simple de document avec CouchDB )

282
Piotr Czapla

La première grande question lors de la plongée dans ceci est "comment voulez-vous stocker les changesets" ?

  1. Diffs?
  2. Des copies de disques entières?

Mon approche personnelle serait de stocker des diffs. Parce que l'affichage de ces diff est vraiment une action spéciale, je les placerais dans une collection "historique" différente.

Je voudrais utiliser la collection différente pour économiser de l'espace mémoire. Généralement, vous ne voulez pas d'un historique complet pour une requête simple. Ainsi, en gardant l'historique hors de l'objet, vous pouvez également le garder en dehors de la mémoire couramment utilisée lorsque ces données sont interrogées.

Pour me faciliter la vie, je voudrais qu'un document d’historique contienne un dictionnaire de diffs horodatés. Quelque chose comme ça:

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

Pour rendre ma vie vraiment facile, je voudrais faire de cette partie de mes DataObjects (EntityWrapper, peu importe) que j'utilise pour accéder à mes données. Généralement, ces objets ont une forme d’historique, de sorte que vous pouvez facilement remplacer la méthode save() pour effectuer cette modification en même temps.

UPDATE: 2015-10

On dirait qu’il ya maintenant ne spécification pour la gestion des différences JSON . Cela semble être un moyen plus robuste de stocker les diffs/modifications.

143
Gates VP

Il existe un schéma de version appelé "Vermongo" qui aborde certains aspects qui n’ont pas été traités dans les autres réponses.

L'une de ces questions concerne les mises à jour simultanées et la suppression de documents.

Vermongo stocke les copies complètes des documents dans une collection d'ombres. Pour certains cas d'utilisation, cela peut entraîner des frais généraux excessifs, mais je pense que cela simplifie également beaucoup de choses.

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

30
Marian

Voici une autre solution utilisant un seul document pour la version actuelle et toutes les anciennes versions:

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

data contient toutes versions. Le tableau data est ordonné, les nouvelles versions obtiendront uniquement $Pushed à la fin du tableau. data.vid est l'identifiant de la version, qui est un numéro incrémenté.

Obtenir la version la plus récente:

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

Obtenir une version spécifique par vid:

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

retourne uniquement les champs spécifiés:

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

Insérer la nouvelle version: (et empêcher les insertions/mises à jour simultanées)

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

2 est la vid de la version la plus récente et 3 est la nouvelle version insérée. Comme vous avez besoin de la version la plus récente de vid, il est facile d'obtenir la version suivante de vid: nextVID = oldVID + 1.

La condition $and garantira que 2 est le dernier vid.

De cette façon, aucun index unique n’est nécessaire, mais la logique de l’application doit prendre en charge l’incrémentation de la variable vid à l’insertion.

Supprimer une version spécifique:

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

C'est ça!

(rappelez-vous la limite de 16 Mo par document)

19
Benjamin M

Si vous cherchez une solution prête à l'emploi -

Mongoid a construit en versioning simple

http://mongoid.org/en/mongoid/docs/extras.html#versioning

mongoid-history est un plugin Ruby qui fournit une solution nettement plus compliquée avec des fonctions d'audit, d'annulation et de restauration.

https://github.com/aq1018/mongoid-history

12
s01ipsist

J'ai travaillé avec cette solution qui prend en charge une version publiée, une version préliminaire et une version historique des données:

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

J'explique le modèle plus en détail ici: http://software.danielwatrous.com/representing-revision-data-in-mongodb/

Pour ceux qui peuvent implémenter quelque chose comme ceci dans Java , voici un exemple:

http://software.danielwatrous.com/using-Java-to-work-with-versenced-data/

Y compris tout le code que vous pouvez bifurquer, si vous aimez

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

9
Daniel Watrous

Une autre option consiste à utiliser le 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.
3
Muhammad Reda

Si vous utilisez Mangouste, j'ai trouvé le plugin suivant être une implémentation utile du format JSON Patch

mongoose-patch-history

3
bmw15

J'ai utilisé le package ci-dessous pour un projet météore/MongoDB, et cela fonctionne bien. L'avantage principal est qu'il stocke l'historique/les révisions dans un tableau du même document. Il n'est donc pas nécessaire de disposer de publications ou d'un middleware supplémentaires pour accéder à l'historique des modifications. . Il peut prendre en charge un nombre limité de versions précédentes (par exemple, les dix dernières versions), il prend également en charge la concaténation des modifications (ainsi, toutes les modifications intervenues au cours d'une période donnée seront couvertes par une révision).

nicklozon/météore-collection-révisions

Une autre option consiste à utiliser Meteor Vermongo ( here )

1
helcode