De MariaDB à MongoDB ep3 : extraction, boucles, conversion

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

Après avoir posé le concept, puis tenté les premières choses avec succès (pas nécessairement du premier coup), il est temps de monter en puissance avant le bouquet final. Donc aujourd’hui on met les doigts dans les tuples, boucles, changements de types, bref, ça va chauffer un peu.

Retour sur les tuples et les dictionnaires

Pour la faire court parce que je ne vais pas non plus vous faire un cours de Python aujourd’hui, les tuples sont des listes dont on ne peut pas changer le contenu. On peut les parcourir avec une boucle for. On les représente généralement entre parenthèses, les éléments séparés par une virgule (même un tuple avec un seul élément contient une virgule, me demandez pas pourquoi ils ont décidé ça). Ça ne vous rappelle rien ? Lorsqu’on a extrait les données de MariaDB, le résultat était le suivant pour extraire Skyfall :

Et donc ici, notre élément est un dictionnaire, qui en Python, est représenté entre crochets, et contient des couples « clés : valeurs ». C’est fait exprès : si j’avais laissé MySQLdb extraire les données sans préciser de convertir en dictionnaire, j’aurais eu un tuple à la place, et LA grosse différence est que j’aurais du m’asseoir sur les noms des champs, je n’aurais eu que les valeurs en guise d’éléments. C’est vital, notamment parce que sinon je vais m’asseoir sur une capacité intéressante des dictionnaires, le fait de pouvoir les modifier, afin de les réinjecter ensuite dans MongoDB.

Une nécessaire adaptation du schéma de données

Dans mes premiers essais, j’ai affecté directement l’id du film dans le document. Avec MariaDB cet id est numérique, avec la propriété AUTO-INCREMENT, et pour lequel il tient un compteur. Ce qui veut dire qu’à chaque nouvel enregistrement (nouvelle ligne), il va utiliser ce compteur, l’attribuer au nouvel enregistrement, et l’incrémenter dans l’attente du prochain.

Cette propriété n’existe pas dans le monde MongoDB, par contre, il sait générer l’_id tout seul comme un grand. Cet identifiant, dans le monde MongoDB, est un ObjectId. Un exemple plus parlant, pour l’instant seulement avec le shell mongo :

Autre propriété : les types de données. Dans l’exemple au dessus, ainsi que dans les précédents où j’ai manipulé mes documents, je n’ai travaillé qu’avec des éléments de type string, des chaînes de caractères. Hors, pour reprendre l’extraction MariaDB au dessus, on a de la chaîne de caractères, et des nombres entiers. Je pense conserver cette propriété, ce qui réduira un peu la taille des données à stocker (j’y reviendrais).

Dernier point, qui sera une évolution par rapport au fonctionnement actuel : j’utilise deux variables et donc deux champs pour gérer le prêt d’un film : ‘pret’ qui est un entier court (tinyint(1)) qui indique si le film est prêté ou pas, et ‘nom_pret’ qui est une chaîne de caractères (varchar(128)) pour le nom de l’emprunteur. Je vais simplifier en n’utilisant que le champ ‘pret’ qui sera soit absent pour dire que le film est dispo, soit contiendra directement le nom du gus (un retour de prêt se traduisant par la suppression de la clé).

À vos marques, prêt ? Partez !

Premier tour de piste

On va regarder de près comment parcourir un résultat de requête MariaDB qui renvoie plusieurs réponses. Comme on a dit, c’est un tuple de dictionnaires. On va donc voir à quoi ça ressemble, et la meilleure méthode pour parcourir chaque résultat. Je vais prendre comme exemple la recherche de titre « 007 », qui contiendra donc, entre autres, les trois films James Bond. Histoire de vous rafraîchir la mémoire, je vais remettre en entier le script, et par la suite je n’ajouterais que les petits bouts :

J’ai laissé l’ajout du module pymongo, même si on n’en a pas besoin pour cette première étape. Rien de très compliqué, Et si on l’exécute, le résultat est, je pense, limpide :

J’ai montré par la même occasion comment on accédait aux éléments du dictionnaire, ici en appelant la clé ‘annee’, on obtient la valeur (qui n’est pas renseignée pour les Enfoirés, je n’avais pas encore mis les garde-fous sur le formulaire).

Deuxième tour de piste

Pour chaque passage dans la boucle, on va adapter ce dont on a besoin. Ce qui veut dire ajouter ou modifier certains éléments, et en retirer d’autres :

  • Ajouter, car je vais « renommer » le champ « types » en « type », ce qui revient à ajouter le nouveau avec la valeur de l’ancien, puis supprimer cet ancien
  • Modifier, car on va remplacer le contenu de  « pret » par celui de « nom_pret », si celui-ci n’est pas vide, sinon supprimer les deux
  • Supprimer, car on ne conservera pas les champs « id_type », « t.id_type », « nom_pret », « types » et « id » (qui sera généré par MongoDB) devenus surperflus.

Plusieurs lignes seront nécessaires, mais rien de très compliqué, là encore (je remet juste la boucle) :

Probablement parce que je n’ai pas suffisamment d’expérience en Python, je n’ai pas trouvé de méthode élégante pour faire autrement qu’une seule ligne par champ à supprimer. Ceci dit je n’ai pas non plus de gros traitements à faire pour l’instant, car on ne travaille que sur quatre dictionnaires. Sur les 440, ça sera une autre paire de manches. Bref, vous remarquez que j’affiche deux fois film, une fois avant, une fois après. Et ça donne ça :

Pour rappel, il n’existe pas d’ordonnancement, le fait que les éléments ne soient pas dans le même ordre n’a aucune importance, à part pour la lisibilité.

Troisième tour de piste

Eh bien maintenant, on va tenter l’ajout du dictionnaire résultat dans MongoDB. On avait déjà détaillé la commande dans l’épisode précédent, il suffira donc de l’adapter ici au nom de variable :

J’ai supprimé le premier affichage de film et conservé le second, histoire de bien voir ce qui sera injecté dans MongoDB. On connaît déjà ce qui s’affiche quand on l’exécute, alors voyons voir le résultat en passant par le shell MongoDB :

J’ai un « léger » problème : mon année est bien stockée, mais du fait que MySQLdb a décrété qu’un int(4) dans MariaDB devait se traduire par un entier long, elle est donc stockée sous la forme d’un entier long dans MongoDB, soit sous la forme d’une valeur sur 64bit. Ce qui est bien trop long, et consommera une blinde de ressources quand il s’agira d’attaquer les 400+ films, sans parler du futur. Tout ça pour une année sur quatre chiffres. Comme c’est la dernière valeur purement numérique des informations sur les films, j’ai deux choix : soit tenter de revenir à un entier plus court, soit le convertir en chaîne de caractères. Si l’on s’en réfère à  cet article, avec quatre chiffres pour l’année je suis plus avisé de la stocker sous forme d’entier court, ce qui prendra moins de place et donc de ressources. On va donc ajouter une ligne dans la boucle pour tenter de corriger le tir, dans les ajouts/modifications, avant l’insertion du film :

Au fait, entre chaque test, j’ai vidé la collection avec la commande db.films.remove() sans arguments, ce qui supprime tous les documents de la collection. Une fois fait, le résultat est édifiant (après avoir supprimé les anciens essais, encore une fois) :

Et le sprint final ?

Et bien, notre script fonctionne, mais j’ai un problème de taille, au sens propre du terme : ici, les quatre résultats de la requête sont stockées dans la variable data, intégralement « interprétée » (fetchall() ) avant la boucle, ce qui peut paraître raisonnable. Mais ma base contient plus de quatre cents films, et tous les stocker dans une seule variable pourrait bien exploser les compteurs, et considérablement ralentir l’exécution. Il va donc falloir revoir quelque peu la méthode d’extraction. Mais on a déjà bien avancé, reposons les neurones jusqu’au prochain (et probablement dernier) épisode.