Tengo un directorio con 2,5 millones de archivos JSON pequeños. Son 104gb en disco. Son archivos de varias líneas.
Me gustaría crear un conjunto de matrices JSON a partir de los archivos para poder importarlos usando mongoimport en un tiempo razonable. Los archivos no pueden tener más de 16 mb, pero estaría feliz incluso si lograra obtenerlos en grupos de diez.
Hasta ahora, puedo usar esto para hacerlos uno a la vez a aproximadamente 1000/minuto:
for i in *.json; do mongoimport --writeConcern 0 --db mydb --collection all --quiet --file $i; doneCreo que puedo usar "jq" para hacer esto, pero no tengo idea de cómo hacer que el bucle bash pase 10 archivos a la vez a jq. Tenga en cuenta que el uso de bash find da como resultado un error ya que hay demasiados archivos.
Con jq puede usar --slurp para crear matrices, y -c para hacer una sola línea json multilínea. Sin embargo, no puedo ver cómo combinar los dos en un solo comando.
Por favor ayuda con ambas partes del problema si es posible.
Aquí hay un enfoque. Para ilustrar, he usado awk porque puede leer la lista de archivos en lotes pequeños y porque tiene la capacidad de ejecutar jq y mongoimport. Probablemente necesitará hacer algunos ajustes para hacer que todo sea más robusto, para probar errores, etc.
La idea es generar un script que pueda ser revisado y luego ejecutado, o usar el comando system() de awk para ejecutar los comandos directamente. Primero, generemos el script:
ls *.json | awk -v group=10 -v tmpfile=json.tmp ' function out() { print "jq -s . " files " > " tmpfile; print "mongoimport --writeConcern 0 --db mydb --collection all --quiet --file " tmpfile; print "rm " tmpfile; files=""; } BEGIN {n=1; files=""; print "test -r " tmpfile " && rm " tmpfile; } n % group == 0 { out(); } { files = files " \""$0 "\""; n++; } END { if (files) {out();}} 'Una vez que haya verificado que esto funciona, puede ejecutar el script generado o cambiar las líneas "imprimir ..." para usar "sistema (....)"
Aquí hay un enfoque solo jq para generar el script. Dado que la cantidad de archivos es muy grande, lo siguiente usa funciones que solo se introdujeron en jq 1.5, por lo que su uso de memoria es similar al script awk anterior:
def read(n): # state: [answer, hold] foreach (inputs, null) as $i ([null, null]; if $i == null then .[0] = .[1] elif .[1]|length == n then [.[1],[$i]] else [null, .[1] + [$i]] end; .[0] | select(.) ); "test -r json.tmp && rm json.tmp", (read($group|tonumber) | map("\"\(.)\"") | join(" ") | ("jq -s . \(.) > json.tmp", mongo("json.tmp"), "rm json.tmp") )Invocación:
ls *.json | jq -nRr --arg group 10 -f generate.jqEsto es lo que se me ocurrió. Parece funcionar y está importando a aproximadamente 80 por segundo en un disco duro externo.
#!/bin/bash files=(*.json) for((I=0;I<${#files[*]};I+=500)); do jq -c '.' ${files[@]:I:500} | mongoimport --writeConcern 0 --numInsertionWorkers 16 --db mydb --collection all --quiet;echo $I; doneSin embargo, algunos están fallando. He importado archivos de 105k, pero solo aparecieron 98547 en la colección de mongo. Creo que es porque algunos documentos son> 16mb.