IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Shell et commandes GNU Discussion :

[bash] Fusionner plusieurs JSON


Sujet :

Shell et commandes GNU

  1. #1
    Membre averti
    Homme Profil pro
    Etudiant (Master ILC)
    Inscrit en
    Mars 2013
    Messages
    14
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Etudiant (Master ILC)
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2013
    Messages : 14
    Par défaut [bash] Fusionner plusieurs JSON
    Bonjour à toi, basheur curieux !

    Ayant plusieurs millions de fichiers JSON (correspondant pour chacun à une ville du monde), j'aimerais pouvoir fusionner le tout en un seul fichier JSON afin de balancer le tout en bulk_import dans une base de données CouchDB. Je me suis donc mis à écrire un petit script bash :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #!/bin/bash
     
    json='{ "docs" : ['
    FILES=./cities/*
    for filepath in $FILES
    do
    	json="${json}$(cat $filepath),"
    done
    json="${json%?} ] }"
     
    echo $json
     
    curl -vX POST http://root:********@127.0.0.1:5984/cities/_bulk_docs -H 'Content-Type: application/json' -d "${json}"
    Malheureusement, je me suis aperçu en lançant cette commande sur mon gros dossier que l'exécution était beaucoup, beaucoup trop longue. Je me suis donc creusé la tête pour optimiser le tout, et j'en suis rendu au code suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #!/bin/bash
     
    echo '{ "docs" : [' > tmp
    FILES=./cities/*
    for filepath in $FILES
    do
    	echo "$(cat $filepath)," >> tmp
    done
    json=`cat tmp`
     
    json="${json%?} ] }"
     
    echo "${json}" > tmp
     
    curl -vX POST http://root:********@127.0.0.1:5984/cities/_bulk_docs -H 'Content-Type: application/json' -d "${json}"

    J'ai comparé la vitesse d'exécution de ces deux codes sur un échantillon de 1000 fichiers JSON, ce qui me donne 10.431s pour le premier code, et 5.612s pour le second.

    J'aimerais savoir si il reste des points à améliorer, j'ai l'impression que certaines parties de mon code sont encore mal pensées (exemple du stockage du contenu du fichier dans une variable pour virer la dernière virgule). Pourriez vous m'aider à rendre l'exécution de ce code la plus rapide et la plus "propre" possible ?

  2. #2
    Modérateur
    Avatar de N_BaH
    Profil pro
    Inscrit en
    Février 2008
    Messages
    7 653
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 7 653
    Par défaut
    Bonjour,

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #!/bin/bash
     
    echo '{ "docs" : [' > tmp 
     
    for filepath in ./cities/*
    do
       echo "$(cat "$filepath")," >> tmp 
    done
     
    sed '$s/,//' tmp 
     
    curl -vX POST http://root:********@127.0.0.1:5984/cities/_bulk_docs -H 'Content-Type: application/json' -d "$(< tmp)"
    ...
    ?
    N'oubliez pas de consulter les cours shell, la FAQ, et les pages man.

  3. #3
    Membre Expert
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    792
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Mai 2004
    Messages : 792
    Par défaut
    Variante awk:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    echo '{ "docs" : [' > tmp
    awk 'FNR!=NR{$0=","$0}1' ./cities/* >> tmp
    echo ' ] }' >> tmp
    A essayer. Ne marche que si le json du fichier tient sur une seule ligne.

  4. #4
    Expert confirmé Avatar de disedorgue
    Homme Profil pro
    Ingénieur intégration
    Inscrit en
    Décembre 2012
    Messages
    4 347
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur intégration
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Décembre 2012
    Messages : 4 347
    Par défaut
    Bonjour,

    @N_BaH: En fait, il veut remplacer la dernière virgule (qui est ni plus ni moins que le dernier caractère de son fichier par ' ] }'
    je ne pense pas que le '$s/,//' ou avec la chaine de remplacement fonctionnerait dans tout les cas (le protocole JSON est comme xml, il n'impose pas le retour chariot).

    Par contre, sur un point de vue algorithme, il suffirait d'inverser la concaténation: echo "$(cat "$filepath")," en echo ",$(cat "$filepath")" en traitant le premier fichier avant la boucle. puis lorsque à la fin de la boucle, au lieu de faire du replace, il suffirait de faire un simple rajout à la fin du fichier.

    Donc si on reprend le code de N_BaH (pas testé):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #!/bin/bash
     
    echo '{ "docs" : [' > tmp 
    vir='' 
    for filepath in ./cities/*
    do
       echo "${vir}$(cat "$filepath")" >> tmp
       vir=',' 
    done
     
    echo ' ] }' >>tmp 
     
    curl -vX POST http://root:********@127.0.0.1:5984/cities/_bulk_docs -H 'Content-Type: application/json' -d "$(< tmp)"
    Pas testé, après a voir si plus rapide ou pas...

  5. #5
    Membre averti
    Homme Profil pro
    Etudiant (Master ILC)
    Inscrit en
    Mars 2013
    Messages
    14
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Etudiant (Master ILC)
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2013
    Messages : 14
    Par défaut
    Citation Envoyé par N_BaH Voir le message
    Bonjour,

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #!/bin/bash
     
    echo '{ "docs" : [' > tmp 
     
    for filepath in ./cities/*
    do
       echo "$(cat "$filepath")," >> tmp 
    done
     
    sed '$s/,//' tmp 
     
    curl -vX POST http://root:********@127.0.0.1:5984/cities/_bulk_docs -H 'Content-Type: application/json' -d "$(< tmp)"
    ...
    ?
    Je veux effectivement ne retirer que la dernière virgule, les objets JSON devant être séparés par une virgule, sauf évidemment pour le dernier

    Citation Envoyé par disedorgue Voir le message
    Bonjour,

    @N_BaH: En fait, il veut remplacer la dernière virgule (qui est ni plus ni moins que le dernier caractère de son fichier par ' ] }'
    je ne pense pas que le '$s/,//' ou avec la chaine de remplacement fonctionnerait dans tout les cas (le protocole JSON est comme xml, il n'impose pas le retour chariot).

    Par contre, sur un point de vue algorithme, il suffirait d'inverser la concaténation: echo "$(cat "$filepath")," en echo ",$(cat "$filepath")" en traitant le premier fichier avant la boucle. puis lorsque à la fin de la boucle, au lieu de faire du replace, il suffirait de faire un simple rajout à la fin du fichier.

    Donc si on reprend le code de N_BaH (pas testé):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #!/bin/bash
     
    echo '{ "docs" : [' > tmp 
    vir='' 
    for filepath in ./cities/*
    do
       echo "${vir}$(cat "$filepath")" >> tmp
       vir=',' 
    done
     
    echo ' ] }' >>tmp 
     
    curl -vX POST http://root:********@127.0.0.1:5984/cities/_bulk_docs -H 'Content-Type: application/json' -d "$(< tmp)"
    Pas testé, après a voir si plus rapide ou pas...
    Cette solution convient mais n'améliore pas ou très peu la vitesse d'exécution au final, on grapille quelques dizièmes de seconde quand même pour 1000 fichiers, ce qui est non négligeable pour 3.5 millions de fichiers.


    Citation Envoyé par ripat Voir le message
    Variante awk:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    echo '{ "docs" : [' > tmp
    awk 'FNR!=NR{$0=","$0}1' ./cities/* >> tmp
    echo ' ] }' >> tmp
    A essayer. Ne marche que si le json du fichier tient sur une seule ligne.
    Mes JSON sont justement sur une seule ligne, en adaptant un peu ton code j'ai réussi à obtenir un temps d'exécution de 0.687s, ce qui est une amélioration juste énorme ! Je te remercie donc de ta solution qui semble être la plus adaptée à mon problème. Pour information, le code actuel ressemble à ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    #!/bin/bash
     
    echo '{ "docs" : [' > tmp
    awk 'FNR!=NR{$0=","$0}1' ./cities2/* >> tmp
    echo ' ] }' >> tmp
     
    curl -vX POST http://root:*******@127.0.0.1:5984/cities/_bulk_docs -H 'Content-Type: application/json' -d "`cat tmp`"
    Je posterai le temps d'exécution sur la totalité des fichiers quand ça sera terminé, merci à vous tous pour l'aide

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Fusionner plusieurs Rapports BO en un seul.
    Par scalpa63 dans le forum Deski
    Réponses: 11
    Dernier message: 19/02/2010, 16h28
  2. Petite interface pour fusionner plusieurs tables
    Par mouche dans le forum VBA Access
    Réponses: 2
    Dernier message: 30/08/2007, 14h32
  3. [Bash] mettre plusieurs conditions dans un while
    Par arnaudperfect dans le forum Shell et commandes GNU
    Réponses: 13
    Dernier message: 22/07/2007, 22h44
  4. Comment fusionner plusieurs callback pour un même objet graphique
    Par biquet dans le forum Interfaces Graphiques
    Réponses: 8
    Dernier message: 19/04/2007, 10h39
  5. [VBA] Fusionner plusieurs colonnes en une seule
    Par brutos2 dans le forum Macros et VBA Excel
    Réponses: 8
    Dernier message: 18/01/2006, 14h25

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo