De MariaDB à MongoDB ep2 : modélisation, premiers essais

closeCet article a été publié il y a 9 ans 5 mois 6 jours, il est donc possible qu’il ne soit plus à jour. Les informations proposées sont donc peut-être expirées.

Dans l’épisode 1, j’ai posé le contexte, les choix. Aujourd’hui, on va s’intéresser de plus près à la structure des données existantes, les contraintes par rapport au modèle actuel, et commencer à faire joujou avec le nouvel outil, le tout en Python. Un article plus long, mais qui va poser toutes les bases techniques pour procéder plus tard à la conversion proprement dite.

Table versus collection, ligne versus document

MariaDB stocke les informations sous forme de tableaux (tables), constitués de colonnes et de lignes. C’est très pratique, ça permet une grande cohérence car on peut s’assurer que chaque enregistrement (ligne) contient les mêmes champs (colonnes). Dans une même base, on peut avoir plusieurs tables, et il est possible de relier certains champs entre les tables avec des jointures (d’où le terme de base de données relationnelles).

MongoDB s’oriente lui vers un système de collections, qui contiennent des documents, bien plus souple car le contenu de chaque document au sein d’une même collection peut-être différent. En contrepartie il est impossible de faire des relations entre les différents champs des documents, car il n’y a aucun moyen de s’assurer que les champs en question sont présents.

Les deux utilisent par contre un système d’index qui permet de parcourir les enregistrements et documents plus rapidement. Et s’il est possible d’utiliser ses propres index, MongoDB utilise automatiquement, si non spécifié, le champ « _id », qui correspond au champ « id » qu’on utilise très fréquemment dans le monde SQL.

Étant un grand débutant dans le domaine, je n’argumenterais pas sur les avantages et inconvénients. Le simple fait que la modélisation soit différente m’a titillé l’esprit, et vu la simplicité de ma base de données, ça ne changera pas grand chose au final.

Ma collection de films, une base de données « simple »

Une image valant parfois plus que des mots, voilà à quoi ressemble ma base de données, telle que me l’affiche PHPMyAdmin (affichage réduit, sinon y’a environ 440 films) :

mariadb-mongodb-pma

En gros, il n’y a qu’une table qui contient les films, ce qui n’est pas scandaleux puisque je stocke exactement le même type d’informations pour chaque film. On voit par contre un « détail » qui va m’embêter un peu par la suite : le champ id_type, qui ne fait pas directement référence au type du film (DVD ou Bluray), mais à une deuxième table qui elle contient les types (ce qui permet d’en avoir plus que 2, au cas où — qui a dit Betamax ?). Lorsque je fais une requête SQL je fais une jointure pour récupérer le type en plus du reste des informations (on verra ça plus loin).

J’ai donc un souci : je ne pourrais pas reproduire exactement ce comportement avec MongoDB, puisque je ne pourrais pas relier, au niveau de la base et de mes requêtes, le contenu des deux « collections ». Même si je pourrais le faire au niveau de l’application et moyennant deux requêtes MongoDB (contre une pour MariaDB), je vais faire au plus simple, et revenir au comportement d’origine que j’avais codé pour la première version fonctionnelle de la base : le champ « type » contenait directement la mention « DVD » ou Bluray ». Une conversion qui pourra être faite à la volée lors de l’extraction.

La modélisation des données

Pour reprendre les exemples de commandes utilisées par PHP dans l’article précédent, voilà comment apparaissent les données aux applications et clients qui en font la demande :

La principale différence réside du coup dans la transmission d’informations : MySQL renvoie un tableau, MongoDB renvoie une chaîne JSON (enfin du BSON, JSON avec de l’assaisonnement), qui n’est autre que du texte. JSON oblige il est par contre très facile de le manipuler dans plusieurs langages, Javascript (JSON voulant dire à la base JavaScript Object Notation) et PHP en fait évidemment partie, ainsi que Python. Les paramètres de Domohouse sont d’ailleurs stockés en JSON, et l’API de Domoticz renvoie ses données de cette manière également.

Premiers tests avec le shell MongoDB

MongoDB, comme MariaDB, dispose d’un client en ligne de commandes permettant de manipuler ses bases (généralement utilisé pour des tests, ou des réparations/maintenance). Sous Debian Wheezy (et donc toutes les dérivées de Debian, Ubuntu en tête), l’installation est super simple, le paquet est disponible dans la base de paquets des dépots :

Il installera donc le serveur, le client, ainsi que les dépendances. Il lancera dans la foulée le serveur en écoute sur le port 27017, seulement en local ceci dit (donc seulement pour les programmes qui tournent sur la même machine). Ce qui est parfait pour nous, à part le client qui utilise la syntaxe JavaScript pour fonctionner (j’ai en horreur le JavaScript, mais dans le cadre de MongoDB, ce n’est pas mortel non plus).

Pour utiliser le client qui se connectera à la base locale, il suffit de l’appeler simplement avec la commande mongo. Par défaut, il vous envoie sur une base « test » :

Pour sélectionner une base de données, on utilise la même commande que pour MariaDB. Sympathique, si la base n’existe pas, il la crée, ce qui est parfait pour un serveur « neuf » :

On a notre base, on va créer notre collection de films (il n’y en aura qu’une, mais vous pouvez avoir autant de collections que vous voulez dans votre base). La commande pour créer une collection est assez simple :

La deuxième ligne s’affichera si aucun problème ne survient. Voilà, maintenant on a de quoi stocker nos informations, on va donc pouvoir ajouter les premiers films pour tester, ce qui permettra de déterminer les commandes d’insertions à utiliser pour la conversion. Voilà la commande que j’avais utilisé pour créer le film utilisé en exemple de modélisation au dessus :

S’il n’y a pas d’erreur, rien n’est renvoyé. On remarquera le terme « insert » qu’utilise également MariaDB pour ajouter des entrées aux tables. Plusieurs des commandes de MongoDB ont des noms similaires aux commandes SQL, ce qui facilite leur apprentissage.

Quand on veut afficher un document dont on connaît l’id, c’est la commande find() qui va être mise à contribution. Sans paramètre, elle retourne les 20 premiers résultats de la collection (pour éviter de saturer l’affichage, surtout quand on aura les 400), pour lui donner un critère on lui passera une chaîne JSON. L’exemple est plus parlant :

J’ai ajouté pretty() à la fin de la commande juste pour avoir un affichage « sympa », il sera inutile lorsque les requêtes seront faites au sein des applications. Mais revenons au critère de find(). Pour demander à afficher le film dont l’id est 4, j’ai ajouté { « _id » : « 4 » } en paramètre. Attention, parce que j’ai eu le tour : { « _id » : « 4 » } et { « _id » : 4 } sont deux couples « clé-valeur » différents, et ça vaut aussi pour la création (j’ai eu le tour, à modifier/supprimer le mauvais document).

Premiers essais de manipulation en Python

Maintenant qu’on sait ajouter des documents avec la console MongoDB, il est temps de passer aux choses sérieuses, et de tenter le coup avec Python. Avant d’attaquer les choses sérieuses, je conseille d’utiliser python-pip (à installer si ce n’est pas déjà fait, tellement il est utile) pour installer pymongo  :

Le téléchargement est rapide, car le module est léger, des messages un peu cryptiques apparaîtront pour la compilation d’une dépendance (sans gravité si elle échoue, car il faut les fichiers de développement de Python), dont la durée dépendra de la puissance de votre machine (ce n’est pas mortel de toute façon).

NOTE : je n’utiliserais aucune forme d’authentification ou de sécurité lors de la démonstration. Il est évident que lors d’une utilisation réelle, et surtout une utilisation sur réseau « public », il faudra blinder tout ça.

Première étape, on doit définir la connexion au serveur, avec gestion de l’absence de serveur au cas où ça péterait, et on teste vite fait cette connexion :

J’ai stocké ce script dans migrate.py, l’ai rendu exécutable (chmod +x), et lancé pour voir ce que ça donnait. Tada :

Bon, évidemment, vu qu’on parle de moi, j’ai fait plusieurs erreurs pour en arriver là. Et je ne regrette pas d’avoir le livre au format papier à côté de moi. « Admirez » le petit « u » devant chaque chaîne de caractères : pymongo vous annonce par ce biais que les chaînes de caractères sont au format UTF-8, ce qui est parfait puisqu’on a utilisé UTF-8 comme encodage de fonctionnement (cf la deuxième ligne du script), et comme encodage d’écriture. Et vous avez intérêt à suivre ça de près, notamment sous Windows qui est assez prompt à vous éloigner de l’UTF-8 à la moindre pointe d’inattention (mais vous utilisez un véritable éditeur de texte, comme Sublime Text, n’est-ce pas ?).

Pour ajouter un nouveau film, en l’occurrence, la suite de Casino Royale, on va ajouter quelques lignes dans la fonction main (je ne remet pas la totalité du code pour éviter d’alourdir, car vous allez vite comprendre comment je vais procéder) :

Il s’agit donc de créer un dictionnaire Python qui va ensuite être injecté dans MongoDB avec la commande insert().

Vous remarquerez probablement que les éléments ne s’afficheront pas toujours dans le même ordre. Ce n’est pas grave, et non, ils ne sont pas dans le désordre : il n’y a tout simplement pas d’ordre dans un dictionnaire python. Ce n’est pas gênant, à partir du moment où une même clé se retrouve dans les documents à trier, on pourra toujours procéder, avec la commande sort() de MongoDB (très utile notamment dans le tri alphabétique sur le titre que j’utilise déjà au niveau de MariaDB).

Maintenant, extraire les infos de MySQL

Bien, on sait manipuler MongoDB avec Python, voyons de plus près comment se manger du MySQL. C’est le module MySQLdb, à ajouter aux lignes « import… » qui va s’en charger. Il est fourni en standard avec Python, contrairement à pymongo. Aucun besoin de l’installer donc, juste le charger au début du script.

J’ai cherché à extraire ce coup-ci les infos de Quantum of Solace, qui est donc le dernier de la « trilogie » Daniel Craig (faux, puisqu’on sait qu’il va y en avoir au moins un autre, ce qui me ravit). Voyons d’abord le code (toujours l’affichage réduit), toujours dans la fonction main, puis analysons son contenu :

Ah oui : pour ajouter le module, j’ai utilisé « import MySQLdb as mdb« , qui permet de définir un alias, ce qui est pratique car en plus de la longueur du nom, le mélange majuscule/minuscule n’est vraiment pas agréable à saisir.

Voyons les autres lignes :

  • base = … : définit la connexion à la base de donnée (hote, utilisateur, mot de passe, base)
  • cursor = … : par défaut, MySQLdb renvoie chaque ligne de résultat sous forme de tuple python; on peut lui dire de renvoyer un dictionnaire à la place
  • requete = … : la requête SQL, sous forme de chaine de caractères, en y incluant la jointure pour récupérer le type
  • ligne = … : récupère l’objet MySQL contenant les résultats
  • data = … : est un tuple contenant les dictionnaires (un seul ici) résultant des lignes trouvées par MySQL (grâce à la commande fetchall() )

Si on l’exécute, le résultat est assez surprenant, mais finalement très logique :

C’est donc un tuple (représenté entre parenthèses), qui contient un seul résultat, un dictionnaire (représenté entre crochets { }, comme pour le JSON, c’est pratique non ?), avec les couples clés : valeurs. Quelques bizarreries toutefois : à l’instar de pymongo qui renseigne le fait que les chaînes de caractères sont unicode, MySQLdb indique par un L que les nombres sont des nombres entiers longs (L pour Long Integer). Pas choquant puisque ce sont des valeurs numériques entières dans la base de données. On remarque aussi les deux champs ‘id_type’ et ‘t.id_type’, qui ont en fait la même valeur : le premier provient de la table collect_liste_films qui contient… la liste des films (facile), et le deuxième qui est l’id de la table collect_types qui contient les types de films, la jointure nous permettant alors d’extraire « types » qui lui contient réellement le type (« Bluray » dans mon exemple). Ça fait un paquet de types ça, désolé.

La suite au prochain numéro

Voilà, on a donc trouvé comment extraire les infos de la base de données MariaDB, et comment ajouter un film dans la future base MongoDB. Il va malgré tout falloir une petite dose de traitement sur les données extraites, et voir comment automatiser l’extraction et l’ajout des quelques 440 films qui sont renseignés dans cette base. Au programme donc, parcours du tuple de dictionnaires, extraction/conversion des données de chaque dictionnaire, pour en faire un nouveau qu’on injectera dans MongoDB. Que du bonheur quoi 🙂

4 Commentaires
Le plus ancien
Le plus récent
Commentaires en ligne
Afficher tous les commentaires
Pierre-Gilles
01/08/2015 13:57

MongoDB est pratique pour des petits projets, mais attention à ne pas s’enfermer dedans, il faut garder en tête que c’est une base de donnée orienté document et qu’on ne peut pas faire la même chose qu’avec une base de donnée relationnelle ! Un bon article qui résume le problème, par les créateurs de Diaspora ( le réseau social décentralisé ), ils s’étaient basés sur MongoDB au début mais ils ont vite dû migrer vers un MySQL finalement beaucoup plus souple ! Le titre est certes provoquant, mais le contenu vraiment très intéressant : http://www.sarahmei.com/blog/2013/11/11/why-you-should-never-use-mongodb/

Pierre-Gilles
02/08/2015 21:16
Répondre à  Seboss666

Quel idiot je crois même que j’avais trouvé l’article sur ton blog quand tu l’avais posté !

J’en profite pour dire que tes articles sont très intéressants, j’avais trouvé ton blog en début d’année (j’étais tombé sur tes tutos de sécurisation de serveur Linux ), et depuis je suis! 🙂