使用bash或python对巨大的JSON文件进行排序

时间:2019-04-16 16:20:59

标签: python json bash sorting jq

要求:我有一个.gz格式的Json文件。因此,压缩后的大小约为500 MB。当我解压缩它时,json文件几乎变成了大约10 GB。提取的JSON文件逐行包含单个JSON对象。我要使用任何bash脚本或python程序,根据字段ps对文件进行排序。

由于文件太大,因此不建议将其加载到内存中。因此,我使用gzcat和cat bash命令流式传输JSON数据,然后将它们通过管道传输到jq以进行排序。但是要么系统在处理过程中没有响应,要么在output.json中得到空文件

>cat  sth2.json | parallel --pipe --group --block 1000M --recend '\n}\n' "jq -s -c 'sort_by(.ps) | .[]'"  > "output.json"
>gzcat  sth2.json.gz | parallel --pipe --group --block 1000M --recend '\n}\n' "jq -s -c 'sort_by(.ps) | .[]'"  > "output.json"

硬件: 16GB RAM, 核心i5处理器

样本JSON数据:-

{
    "ps":"abc"
    ....
}
{   
    "ps":"def"
    ......
}
{
    "ps":"abc"
    ....
}

预期输出

{
    "ps":"abc"
    ....
}
{   
    "ps":"abc"
    ....
}
{
    "ps":"def"
    ....
}

我不明白我在做什么错。谁能建议如何对如此巨大的JSON文件进行排序? 我关注的链接: https://github.com/joelpurra/jq-hopkok/tree/master/src/parallelism

此外,如果没有Hadoop,我可以通过任何Map Reduce进行任何操作吗?

方法1:将数据流式传输到本地Sqlite数据库。

import sqlite3
import fileinput

PATH=".../sqlite-snapshot-201904101324/testDB.db"
insert_query="INSERT INTO feeds (data) VALUES (?)"

def db_connect(db_path=PATH):
    con = sqlite3.connect(db_path)
    return con

con = db_connect() # connect to the database
cur = con.cursor() # instantiate a cursor obj

record_count = 0
for line in fileinput.input():
    cur.execute(insert_query,(line,))

命令行:

>gzcat sth.json.gz | python insert.py

3 个答案:

答案 0 :(得分:2)

这是基于其中一项评论中的建议的解决方案:

  

如果可以,例如在行的前面加上sort键,以便可以将它们作为文本而不是JSON进行排序,然后GNU sort可以轻松地对10GB以上的文件进行排序,而无需将其加载到内存中。 –那个人

您可以使用jq沿着以下行执行此操作:

mixins

这将产生带有制表符分隔值的行,如下所示:

jq -cr '"\(.ps)\t\(.)"' 

使用-c选项可确保将每一对(即排序键和对象)写入一行。

现在,您可以轻松地对行进行排序,例如使用abc {"ps":"abc","x":0} abc {"ps":"abc","x":1} ;然后使用sort剥离.ps字段。

最后,如果您确实希望格式化输出,则可以再次使用cut(例如jq),关键是jq默认是面向流的。

注意

以上假设.ps值不带制表符。如果不是这种情况,则可以使用其他字段分隔符,也可以:

jq .

答案 1 :(得分:0)

您可以使用 base64 编码和解码,而不是关注不同类型的边缘情况。

这种方法会创建两个由制表符分隔的列。第一列包含用于排序的值。第二个是base64格式的完整JSON。

排序后我们得到第二列和 base64 解码。

gzcat sth.json.gz | \
  | jq -cr '[(. | .ps | @text), (. | @base64)] | @tsv' \ # Create a TSV where the first column contains the value that should be sorted on.
  | sort -t$'\t' -k1,1 \ # Sort only on first column.
  | cut -f 2 \ # Get the base64 encoded JSON from seconds column.
  | base64 -d \ # Decode the base64 encoded JSON. (Ignores newlines)
  | jq -c . # Required to place each JSON on separate line.

答案 2 :(得分:-1)

您无需担心各种边缘情况,而可以使用base64编码完整的JSON。

gzcat sth.json.gz | \
  | jq -cr '[(. | .ps | @text), (. | @base64)] | @tsv' \ # Create a TSV where the first column contains the value that should be sorted on.
  | sort -t$'\t' -k1,1 \ # Sort only on first column.
  | cut -f 2 \ # Get the base64 encoded JSON from seconds column.
  | base64 -d \ # Decode the base64 encoded JSON. (Ignores newlines)
  | jq -c . # Required to place each JSON on separate line.