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

使用bash或python对大型JSON文件进行排序,python,json,bash,sorting,jq,Python,Json,Bash,Sorting,Jq,要求:我有一个.gz格式的Json文件。因此,当它被压缩时,它的大小约为500 MB。当我提取它时,json文件几乎变成约10GB。提取的JSON文件逐行包含单独的JSON对象。我想要的是使用任何bash脚本或python程序根据字段ps对文件进行排序 由于文件太大,不建议将其加载到内存中。因此,我使用gzcat和catbash命令来流式传输JSON数据,然后将它们传输到jq进行排序。但要么系统在这个过程中没有响应,要么我在output.json中得到一个空文件 >cat sth2.js

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

由于文件太大,不建议将其加载到内存中。因此,我使用gzcat和catbash命令来流式传输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内存, 核心i5处理器

示例JSON数据:-

{
    "ps":"abc"
    ....
}
{   
    "ps":"def"
    ......
}
{
    "ps":"abc"
    ....
}
预期产出

{
    "ps":"abc"
    ....
}
{   
    "ps":"abc"
    ....
}
{
    "ps":"def"
    ....
}
我不明白我做错了什么。有人能建议如何对如此巨大的JSON文件进行排序吗? 我关注的链接:

另外,有没有什么方法可以在没有Hadoop的情况下通过任何Map reduce实现

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

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

以下是一个基于其中一条评论中建议的解决方案:


例如,如果您可以使用sort键作为行的前缀,以便它们可以作为文本而不是JSON进行排序,那么GNU sort可以轻松地对10GB+文件进行排序,而无需将其加载到内存中那个家伙

您可以使用jq按照以下方式执行此操作:

jq -cr '"\(.ps)\t\(.)"' 
这将生成具有制表符分隔值的行,如下所示:

abc {"ps":"abc","x":0}
abc {"ps":"abc","x":1}
使用-c选项可确保将每对(即排序键和对象)写入一行

现在,您可以轻松地对行进行排序,例如使用
排序
;然后使用例如
cut
去除.ps字段

最后,如果您确实希望格式化输出,您可以再次使用
jq
(例如
jq.
),即jq在默认情况下是面向流的

警告 以上假设.ps值是无选项卡的。如果不是这样,则可以使用不同的字段分隔符,或者:

jq -cr '([.ps] | @tsv) + "\t" + tostring'

您可以使用base64编码和解码,而不用担心不同类型的边缘情况

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

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

gzcat sth.json.gz |\
|jq-cr'[(.|.ps |@text),(.|@base64)]@tsv'\\创建一个tsv,其中第一列包含应排序的值。
|排序-t$'\t'-k1,1\#仅对第一列进行排序。
|cut-f2 \#从seconds列获取base64编码的JSON。
|base64-d \#解码base64编码的JSON。(忽略换行)
|jq-c.#需要将每个JSON放在单独的行中。

那么如何对JSON文件进行排序?是否有可用的实用程序或其他工具?是的,正确。我测试了大约20行,我可以看到它们。例如,如果您可以使用排序键为行添加前缀,以便它们可以作为文本而不是JSON排序,那么GNU sort可以轻松地对10GB+文件进行排序,而无需将它们加载到内存中。我解释了“逐行的单个JSON对象”作为JSONL@Tomalak在上面的评论中,您提到了使用python将.gz文件内容流式传输到sqlite。你能提供更多的细节吗that@peak如果我错了,请纠正我,但是当您排序时,整个json数据将加载到内存中。
gzcat sth.json.gz | jq-cr'\(.ps)\t\(.)“| sort | cut-f2>>abc
这是我使用的命令。使用
sort
的-k选项,只需按一个键进行排序。也许使用-S选项进行微调也会有所帮助。您可能还需要检查您的
sort
是否足够最新。您可能还需要了解-T选项以及TMPDIR的角色。