Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

238
Views
Bash loop to merge files in batches for mongoimport

I have a directory with 2.5 million small JSON files in it. It's 104gb on disk. They're multi-line files.

I would like to create a set of JSON arrays from the files so that I can import them using mongoimport in a reasonable amount of time. The files can be no bigger than 16mb, but I'd be happy even if I managed to get them in sets of ten.

So far, I can use this to do them one at a time at about 1000/minute:

for i in *.json; do mongoimport --writeConcern 0 --db mydb --collection all --quiet --file $i; done

I think I can use "jq" to do this, but I have no idea how to make the bash loop pass 10 files at a time to jq. Note that using bash find results in an error as there are too many files.

With jq you can use --slurp to create arrays, and -c to make multiline json single line. However, I can't see how to combine the two into a single command.

Please help with both parts of the problem if possible.

over 4 years ago · Santiago Trujillo
2 answers
Answer question

0

Here's one approach. To illustrate, I've used awk as it can read the list of files in small batches and because it has the ability to execute jq and mongoimport. You will probably need to make some adjustments to make the whole thing more robust, to test for errors, and so on.

The idea is either to generate a script that can be reviewed and then executed, or to use awk's system() command to execute the commands directly. First, let's generate the 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();}}
'

Once you've verified this works, you can either execute the generated script, or change the "print ..." lines to use "system(....)"

Using jq to generate the script

Here's a jq-only approach for generating the script. Since the number of files is very large, the following uses features that were only introduced in jq 1.5, so its memory usage is similar to the awk script above:

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") )

Invocation:

ls *.json | jq -nRr --arg group 10 -f generate.jq
over 4 years ago · Santiago Trujillo Report

0

Here is what I came up with. It seems to work and is importing at roughly 80 a second into an external hard drive.

#!/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; done

However, some are failing. I've imported 105k files but only 98547 appeared in the mongo collection. I think it's because some documents are > 16mb.

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!