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
- espionnage via API
- 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