Façons d'implémenter le versionnage des données dans MongoDB

298

Pouvez-vous partager vos réflexions sur la manière d'implémenter le versionnage des données dans MongoDB. (J'ai posé une question similaire à propos de Cassandra . Si vous avez des idées qui db est mieux pour cela, veuillez partager)

Supposons que j'ai besoin de versionner des enregistrements dans un simple carnet d'adresses. (Les enregistrements du carnet d'adresses sont stockés en tant qu'objets json plats). J'attends que l'histoire:

  • sera utilisé rarement
  • sera utilisé d'un seul coup pour le présenter de façon "machine à remonter le temps"
  • il n'y aura pas plus de versions que quelques centaines pour un seul enregistrement. l'histoire n'expirera 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ésentent comme suit:

    {
     '_id': 'nouvel identifiant',
     'user': user_id,
     'timestamp': horodatage,
     'address_book_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.

  • Stockez les versions en tant qu'objet sérialisé (JSON) attaché aux entrées du carnet d'adresses. Je ne sais pas comment attacher de tels objets aux documents MongoDB. Peut-être comme un tableau de chaînes. ( Modélisé d'après la version de document simple avec CouchDB )

Piotr Czapla
la source
1
Je veux savoir si cela a changé depuis la réponse à la question? Je ne sais pas grand chose sur oplog mais était-ce à l'époque, cela ferait-il une différence?
Randy L
Mon approche consiste à considérer toutes les données comme une série chronologique.

Réponses:

152

La première grande question en plongeant dans ceci est "comment voulez-vous stocker les changesets" ?

  1. Diffs?
  2. Copies record entières?

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

J'utiliserais les différentes collections pour économiser de l'espace mémoire. Vous ne voulez généralement pas d'historique complet pour une simple requête. Ainsi, en gardant l'historique hors de l'objet, vous pouvez également le garder hors de la mémoire couramment utilisée lorsque ces données sont interrogées.

Pour me faciliter la vie, je voudrais qu'un document historique contienne un dictionnaire de diff 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 ferais cette partie de mes DataObjects (EntityWrapper, peu importe) que j'utilise pour accéder à mes données. Généralement, ces objets ont une certaine forme d'historique, de sorte que vous pouvez facilement remplacer la save()méthode pour effectuer cette modification en même temps.

MISE À JOUR: 2015-10

Il semble qu'il existe maintenant une spécification pour gérer les différences JSON . Cela semble être un moyen plus robuste de stocker les différences / changements.

Gates VP
la source
2
Ne craindriez-vous pas qu'un tel document historique (l'objet des modifications) se développe avec le temps et que les mises à jour deviennent inefficaces? Ou MongoDB gère-t-il le document se développer facilement?
Piotr Czapla
5
Jetez un oeil à l'édition. L'ajout à changesest vraiment facile: db.hist.update({_id: ID}, {$set { changes.12345 : CHANGES } }, true)cela effectuera un upsert qui ne changera que les données requises. Mongo crée des documents avec "espace tampon" pour gérer ce type de changement. Il surveille également la façon dont les documents d'une collection changent et modifie la taille du tampon pour chaque collection. MongoDB est donc conçu pour exactement ce type de changement (ajouter une nouvelle propriété / pousser au tableau).
Gates VP
2
J'ai fait quelques tests et en effet la réservation d'espace fonctionne plutôt bien. Je n'ai pas pu rattraper la perte de performances lorsque les enregistrements ont été réaffectés à la fin du fichier de données.
Piotr Czapla
4
Vous pouvez utiliser github.com/mirek/node-rus-diff pour générer des différences (compatibles MongoDB) pour votre historique.
Mirek Rusin
1
Le JSON Patch RFC fournit un moyen d'exprimer des difffs. Il a des implémentations dans plusieurs langues .
Jérôme
31

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

L'un de ces problèmes concerne les mises à jour simultanées, un autre supprime les documents.

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

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

Marian
la source
5
Comment l'utilisez-vous réellement?
hadees
6
Il n'y a aucune documentation sur la façon dont ce projet est réellement utilisé. Est-ce quelque chose qui vit avec Mongo d'une manière ou d'une autre? C'est une bibliothèque Java? Est-ce simplement une façon de penser le problème? Aucune idée et aucun indice n'est donné.
ftrotter
1
Il s'agit en fait d'une application java et le code correspondant se trouve ici: github.com/thiloplanz/v7files/blob/master/src/main/java/v7db/…
ftrotter
20

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" }
    ]
}

datacontient toutes les versions. Le datatableau est ordonné , les nouvelles versions ne seront $pushéditées qu'à la fin du tableau.data.videst l'ID de version, qui est un nombre incrémentiel.

Obtenez la version la plus récente:

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

Obtenez une version spécifique en vid:

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

Renvoyer uniquement les champs spécifiés:

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

Insérer une nouvelle version: (et empêcher l'insertion / mise à jour simultanée)

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

2est la version vidla plus récente et 3la nouvelle version est insérée. Parce que vous avez besoin de la version la plus récente vid, il est facile de faire obtenir la prochaine version de vid: nextVID = oldVID + 1.

La $andcondition assurera, c'est 2la dernière vid.

De cette façon, il n'est pas nécessaire d'avoir un index unique, mais la logique d'application doit prendre soin d'incrémenter le vid insertion.

Supprimer une version spécifique:

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

C'est tout!

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

Benjamin M
la source
Avec le stockage mmapv1, chaque fois qu'une nouvelle version est ajoutée aux données, il est possible que le document soit déplacé.
raok1997
Oui c'est vrai. Mais si vous ajoutez simplement de nouvelles versions de temps en temps, cela devrait être négligeable.
Benjamin M
9

J'ai travaillé sur cette solution qui héberge une version publiée, une ébauche et une version historique des données:

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

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

Pour ceux qui peuvent implémenter quelque chose comme ça en Java , voici un exemple:

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

Y compris tout le code que vous pouvez fork, si vous le souhaitez

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

Daniel Watrous
la source
Des trucs géniaux :)
Jonathan
4

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

J'ai utilisé le package ci-dessous pour un projet météore / MongoDB, et cela fonctionne bien, le principal avantage est qu'il stocke l'historique / les révisions dans un tableau dans le même document, donc pas besoin de publications ou de 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 (de sorte que toutes les modifications intervenues au cours d'une période spécifique seront couvertes par une révision).

nicklozon / meteor-collection-revisions

Une autre option sonore consiste à utiliser Meteor Vermongo ( ici )

helcode
la source