Un objet est-il une collection
de tables ou un document ?

Tables ou Document ?

https://slides.com/benoitchanclou

mailto:benoitchanclou@sfr.fr

Cas d'illustration

  • Référentiel de tiers pour gérer un poney club
    • une classe de base pour les tiers
    • sous-classe pour les tiers moraux : les clubs
    • sous-classe pour les tiers physique : les cavaliers
    • chaque club gère ses cavaliers (agrégation)
    • chaque club possède une adresse (composition)
Tiers
String name
Club
String license
Rider
Date birthDate
Address
String street
String zipCode
String city

*

Implémentation dans
une base relationnelle

id nom
1 Shamrock
2 Benoît
id license
1 xxx

Tiers

Clubs

id birthDate
2 xx/xx/xxxx

Riders

id street zipCode city
1 Le bas verclé 35740 Pacé

Address

ClubsRiders

idClub idRider
1 2
id nom
1 Shamrock
2 Benoît
id license
1 xxx

Tiers

Clubs

id birthDate
2 xx/xx/xxxx

Riders

Address

ClubsRiders

idClub idRider
1 2

rappel sur
l'auto-incrémentation

  • L'auto-incrémentation c'est le mal
    • espionnage via API
      GET https://xxx/tiers/1
    • ids identiques pour des ressources différentes
  • L'UUID s'est le bien

Il faut privilégier des UUIDs

id street zipCode city
1 Le bas verclé 35740 Pacé

Problèmes de complexité

  • Complexité de conception
    pour 2 types d'objets
    => 5 tables avec de multiples relations
  • Complexité de maintenance
    ajouter/supprimer un attribut
    => modifier une table

quels sont ces
deux objets ?

{
  "name": "Shamrock",
  "license": "xxx",
  "riders": [
    ...ref cavalier...
  ],
  "address": {
    "street": "le bas verclé",
    "zipcode": "35740",
    "city": "Pacé"
  }
}
{
  "name": "Benoît",
  "birthdate": ISODate("2018-09-01T12:00:00.000Z")
}

(décrits en JSON)

(par exemple transmis dans une API REST)

Mais finalement

les Bases de Données

Orientées Documents

akka 

  • MongoDB
  • CouchDB
  • ...

Orientées Graphes

akka

  • Neo4j
  • ...

Orientées Clé-Valeur

akka

  • Redis
  • ...

Orientées Indexation

akka

  • elasticsearch
  • solr
  • ...

Orientées Colonnes

akka

  • Cassandra
  • ...

Vocabulaire

Mongo vs SQL

  • Base <=> Base
  • Collection <=> Table
  • Document / Objet <=> Ligne / Enregistrement
  • Champ <=> Colonne
  • Sous document ou Lien <=> Jointure

Insertion du cavalier dans la collection des tiers

db.tiers.insert({
    "name": "Benoît",
    "birthdate": ISODate("2018-09-01T12:00:00.000Z")
  })
{
    "_id" : ObjectId("5b8c507f7cd70b933d327b1e"),
    "name" : "Benoît",
    "birthdate" : ISODate("2018-09-01T12:00:00.000Z")
}
  • Ajout automatique d'un _id

Gérer les compositions, agrégations et relations d'objets

Insertion du club dans la collection des tiers

db.tiers.insert({
  "name": "Shamrock",
  "license": "xxx",
  "riders": [
    ObjectId("5b8c507f7cd70b933d327b1e")
  ],
  "address": {
    "zipcode": "35740",
    "city": "Pacé"
  }
})
{
    "_id" : ObjectId("5b8c52ad7cd70b933d327b1f"),
    "name" : "Shamrock",
    "license" : "xxx",
    "riders" : [ 
        ObjectId("5b8c507f7cd70b933d327b1e")
    ],
    "address" : {
        "zipcode" : "35740",
        "city" : "Pacé"
    }
}
  • relation : utilisation de l'id de l'objet
  • composition : le sous-objet d'adresse est inclut dedans
  • agrégation :  
    /!\ gestion du cycle de vie à la main

Gérer le polymorphisme

  • C'est fait pour !
  • Pas besoin des complications de SQL

Règles de bon sens

  • On met un seul type d'objet dans une collection
    => On sait a priori ce qui est stocké
     
  • On identifie le type de chaque objet
    => Pour savoir précisément ce qu'on manipule

Règles de bon sens

Gérer le polymorphisme

  • on ajoute un champ "_type" ou "_class"

{
    "_id" : ObjectId("5b8c52ad7cd70b933d327b1f"),
    "_class" : "org.example.models.Club",
    "name" : "Shamrock",
    "license" : "xxx",
    "riders" : [ 
        ObjectId("5b8c507f7cd70b933d327b1e")
    ],
    "address" : {
        "zipcode" : "35740",
        "city" : "Pacé"
    }
}
{
    "_id" : ObjectId("5b8c507f7cd70b933d327b1e"),
    "_class" : "org.example.models.Rider",
    "name" : "Benoît",
    "birthdate" : ISODate("2018-09-01T12:00:00.000Z")
}

Tables SQL vs Collections MongoDB

SQL

  • complexité des tables
  • gestion automatique des agrégation
  • schéma inclus dans la base

MongoDB

  • simplicité des collections
  • gestion à la main des agrégations
  • pas de schéma obligatoire

C'est cool,

mais pas ouf !

De plus avec les DAO ça se gère tout seul !

Y'a pas mieux ?

Pas de schéma

  • Le schéma est géré par le client non par la base
    => Un seul endroit pour gérer : le client
    => Pas de maintenance du schéma interne de la base
    => Sauf changements majeurs pas besoin de modifier les objets déjà stockés

Gérer la version de schéma sur le client ?

{
    "addressStreet": "Le Bas Verclé",
    "addressZipCode": "35740",
    "addressCityName": "Pacé",
    ...
}
{
    "_version": 1
    "address": {
        "street": "Le Bas Verclé",
        "zipCode": "35740",
        "cityName": "Pacé"
    },
    ...
}
{
    "_version": 2
    "address": {
        "street": "Le Bas Verclé",
        "city" : {
            "zipCode": "35740",
            "name": "Pacé"
        }
    },
    ...
}
var CONVERTERS = [
  obj => { // Conversion de V0 en V1
    obj.address = {
      street: obj.addressStreet,
      zipCode: obj.addressZipCode,
      cityName: obj.addressCityName
    };
    delete obj.addressStreet;
    delete obj.addressZipCode;
    delete obj.addressCityName;
    obj._version = 1;
  },
  obj => { // Conversion de V1 en V2
    obj.address.city = {
      zipCode: obj.address.zipCode,
      name: obj.address.cityName
    };
    delete obj.address.zipCode;
    delete obj.address.cityName;
    obj._version = 2;
  }
];
while (obj._version < CONVERTERS.length) {
  CONVERTERS[obj._version](obj);
}

Tant que l'objet n'est pas dans la dernière version on le convertit à la version suivante

Gérer l'historique des objets

Un document par modification, on stocke les deltas

sous forme de JSONPatch

{ "_id": ..., "id": 174, "userId": "xxx", "date": ISODate(...), patch: [
    { "op": "add", "path": "/attr1", "value": 165 }
] }
{ "_id": ..., "id": 174, "userId": "yyy", "date": ISODate(...), patch: [
    {"op": "add", "path": "/attr2", "value": "A-1" }
] } 
{ "_id": ..., "id": 174, "userId": "zzz", "date": ISODate(...), patch: [
    { "op": "replace", "path": "/attr1", "value": 184 }
] }

chaque patch contient

  • id du document
  • date de modification
  • patch à appliquer

en option

  • id utilisateur
  • son rôle
  • id du tenant

Version mixte

Collection des objets

  • cache dispensable
    peut être reconstruit
  • récupération directe
    rapidité d'accès
  • recherche de contenu
    simplicité de la recherche sur les champs

Collection d'historiques

  • référentiel
  • gestion de l'historique
    connaître à une date donnée
  • rejoue de modifications
    rejouer les modif depuis une date
  • audit des objets
    qui, quoi, quand, comment ?

Mais encore !?

Autres points

  • Destruction
    flag d'archive plutôt que la destruction
  • Index
    Index composés, multi-clé, géospatial, texte, hash
  • Agrégats dynamiques
    chaque objet est la composition de plusieurs schémas
  • Référence & cache
    stocker les données les plus utilisées en plus de la référence à un objet tiers
    permettre les requêtes croisées
  • Changement de nature d'un objet
    scission, fusion, changement de type d'un champ
  • ...

Et au delà de la structuration des données

  • Gestion en cluster avec
    • sharding
    • replica

Conclusion

B's

  • Gestion à la main des agrégations
    (mais les frameworks résolvent cela)
  • Transactionnel limité
    (modifications atomiques)

C's

  • Simplicité de mise en œuvre
  • Orienté cloud
  • Simplicité de la gestion des schémas dans le seul code
  • Liberté de la structuration des données et maîtrise de la complexité des collections

Ressources

# Meetup Mongo

* Lancer la console
    ```
    $ mongo
    ```
    Options pour se connecter sur un serveur et avec authentification
* Quelles sont les bases disponibles
    ```
    show db
    ```
* Sélection de la base `meetup`
    ```
    use meetup
    ```
    Dès que la prochaine commande sera exécutée la base sera créée
  
* Création d'un tiers vide
    ```
    db.tiers.insert({})
    ```
* Liste des documents
    ```
    db.tiers.find()
    ```
* Suppression des documents
    ```
    db.tiers.deleteMany({})
    ```
* Création d'un tiers physique
    ```
    db.tiers.insert({
        "name": "Benoît",
        "birthdate": ISODate("2018-09-01T12:00:00.000Z")
    })
    ```
* Création d'un tiers moral
    ```
    db.tiers.insert({
      "name": "Shamrock",
      "license": "xxx",
      "riders": [
        ObjectId("5bc09283436d0906d01a43e7")
      ],
      "address": {
        "zipcode": "35740",
        "city": "Pacé"
      }
    })
    ```    
* Trouver un tiers
    * avec un *name*
        ```
        db.tiers.find({name:"Benoît"})
        ```
    * avec un *zipcode*
        ```
        db.tiers.find({"address.zipcode":"35740"})
        ```
    * avec une *regex* sur le *name*
        ``` 
        db.tiers.find({name:{$regex:/rock/}})
        ```
    * avec l'existence d'un champ
        ``` 
        db.tiers.find({license:{$exists: true}})
        db.tiers.find({license:{$exists: false}})
        ```
    * avec des valeurs possibles
        ```
        db.tiers.find({name:{$in: ["Benoît", "Shamrock"]}})
        ```
* Modifier un tiers
    * modifier un champs
        ```
        db.tiers.update({name:"Shamrock"},{$set:{"_class" : "org.example.models.Club"}})
        db.tiers.update({name:"Benoît"},{$set:{"_class" : "org.example.models.Rider"}})
        ```

Meetup Mongo

By Benoît Chanclou

Meetup Mongo

Présentation au meetup Capgemini. Seuls les aspects historiques des schémas et des données sont abordés.

  • 329