Bash循环为mongoimport批量合并文件

时间:2017-01-18 11:49:12

标签: json bash mongodb jq

我有一个包含250万个小JSON文件的目录。它在磁盘上是104gb。他们是多行文件。

我想从文件中创建一组JSON数组,以便我可以在合理的时间内使用mongoimport导入它们。这些文件不能超过16mb,但即使我设法将它们设置为10个,我也会感到高兴。

到目前为止,我可以使用它以每分钟1000左右的速度进行一次:

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

我想我可以使用" jq"要做到这一点,但我不知道如何使bash循环一次传递10个文件到jq。 请注意,使用bash查找结果会导致错误,因为文件太多。

使用jq,你可以使用--slurp来创建数组,使用-c来创建多行json单行。但是,我无法看到如何将两者合并为一个命令。

如果可能,请帮助解决问题的两个部分。

2 个答案:

答案 0 :(得分:2)

这是一种方法。为了说明,我使用了awk,因为它可以小批量读取文件列表,因为它能够执行jq和mongoimport。您可能需要进行一些调整以使整个事情更加健壮,以测试错误,等等。

想法是生成可以查看然后执行的脚本,或者使用awk的system()命令直接执行命令。首先,让我们生成脚本:

 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();}}
'

验证完成后,您可以执行生成的脚本,也可以更改“print ...”行以使用“system(....)”

使用jq生成脚本

这是一种仅用于生成脚本的jq方法。 由于文件数量非常大,以下使用仅在jq 1.5中引入的功能,因此其内存使用情况与上面的awk脚本类似:

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

调用:

ls *.json | jq -nRr --arg group 10 -f generate.jq

答案 1 :(得分:0)

这是我想出的。它似乎工作,并以大约80秒的速度导入外部硬盘驱动器。

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

然而,有些人失败了。我已经导入了105k文件,但mongo集合中只出现了98547个文件。我认为这是因为有些文件是&gt; 16MB。