Node CSV parser à la rescousse
Ne ratez pas nos articles sur l'open source, le big data et les systèmes distribués, fréquence faible d’un email tous les deux mois.
Vendredi dernier, une heure avant la fermeture des portes de mon client pour le week-end, un collègue est venu me voir. Il vient juste de finir d’exporter 9 fichiers CSV à partir d’une base de données Oracle qu’il souhaitait importer dans Greenplum, une base de donnée colonne que le client final souhaitait commencer à tester lundi matin.
Le problème exposé était assez simple. Il avait besoin d’une solution rapide (moins d’une heure, codage inclus) pour transformer toute les dates du fichier CSV source en un format adapté à Greenplum. Alors qu’Oracle exportait les dates sous la forme «JJ/MM/AAAA», Greenplum était suffisamment pointilleux pour s’attendre à des dates sous la forme «AAAA-MM-JJ».
Voici les fichiers que nous devions importer :
-rw-r - r-- 1 ingres ingres 41G 2011-12-09 18:23 a.csv
-rw-r - r-- 1 ingres ingres 418M 2011-12-09 17:22 b.csv
-rw-r - r-- 1 ingres ingres 45G 2011-12-09 18:26 c.csv
-rw-r - r-- 1 ingres ingres 1,8G 2011-12-09 17:37 d.csv
-rw-r - r-- 1 ingres ingres 896M 2011-12-09 17:33 e.csv
-rw-r - r-- 1 ingres ingres 382M 2011-12-09 17:13 f.csv
-rw-r - r-- 1 ingres ingres 7,6G 2011-12-09 18:09 g.csv
-rw-r - r-- 1 ingres ingres 621M 2011-12-09 17:33 h.csv
-rw-r - r-- 1 ingres ingres 3,1G 2011-12-09 17:46 i.csv
Parmi ces fichiers, deux ont une taille supérieure à 45 Go.
Étant donné que la génération de nouveaux fichiers aurait probablement pris trop de temps, la première question posée était de savoir si la commande d’importation Greenplum accepterait ou non les données stdin
via un pipe Unix, question qui fut heureusement répondu par l’affirmative. Donc, ma première suggestion a été de lire un fichier avec cat
, de le piper vers Awk, qui reformaterait la date ligne par ligne et le dirigerait ensuite vers l’outil d’importation. La commande ressemblerait à ceci :
chat a.csv | awk '...' | psql -p $ port $ base-de-données -c "COPY $ table DE STDIN AVEC CSV;"
La solution semble réalisable. Problème, nous ne sommes pas un expert Awk et je ne pouvais pas lui garantir qu’une heure suffirait pour écrire et tester la commande.
Compte tenu de ma familiarité avec Node à cette époque, je lui ai dit qu’écrire un petit script JavaScript qui interagissait avec stdin
et stdout
serait beaucoup sur s’il souhaitait partir en weekend avec un peu d’espoir. C’est à ce moment-là que nous avons pensé à utiliser le paser CSV, la première bibliothèque que j’ai écrite pour Node il y a un peu plus d’un an.
Nous devions d’abord configurer le proxy et installer Node sur ce serveur, ce qui prenait environ 10 minutes. Pour cela, nous avons utilisé NVM. Ensuite, nous avons écrit le code JavaScript suivant :
#!/usr/bin/env node
var csv = require ('./ csv');
csv ()
.fromStream (process.stdin)
.toStream (process.stdout)
.transform (fonction (données, index) {
pour (var i = 0; i <data.length; i ++) {
if (/ (\ d {2}) \ / (\ d {2}) \ / (\ d {2}) /. test (données [i])) {
data [i] = data [i] .replace (/ (\ d {2}) \ / (\ d {2}) \ / (\ d {2}) /, "20 $ 3- $ 2- $ 1");
}
}
renvoyer des données;
});
process.stdin.resume ()
Notez comment l’utilisation de l’expression régulière aurait pu être optimisée. Mais notre objectif n’était pas d’accélérer le processus d’importation, mais seulement de le faire fonctionner le plus rapidement possible.
Nous avions déjà à notre disposition un petit script bash permettant de faire une boucle dans les fichiers CSV et de lancer la commande d’importation Greenplum. Nous le modifions un peu pour que la version finale ressemble à ceci :
#!/bin/bash
pour csv en $ (ls ~ / export / *. csv)
faire
table = `echo $ csv | cut -d '/' -f 5`;
table = `echo $ table | cut -d '.' -f 1`;
table = `echo $ table | sed 's / \ (. * \) ../ \ 1 /' `;
chat $ csv | transform.js | psql -p 5433 my_database -c "COPY $ table DE STDIN AVEC CSV;"
terminé
Lundi matin, le parser CSV avait fait son travail et tous les fichiers CSV étaient ingérés dans Greenplum.