Сортировать огромный JSON-файл, используя bash или python - PullRequest
3 голосов
/ 16 апреля 2019

Требование : У меня есть файл Json в формате .gz.Таким образом, когда он сжат, он составляет около ~ 500 МБ.Когда я распаковываю его, файл json становится почти размером ~ 10 ГБ.Извлеченный файл JSON содержит отдельные объекты JSON построчно. Я хочу отсортировать файл на основе поля ps, используя любой сценарий bash или программы на python.

Поскольку файл слишком велик, его не рекомендуется загружать в память.Итак, я использовал команды 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"

Аппаратное обеспечение : 16 ГБ ОЗУ, процессор Core 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

Кроме того, есть ли какой-нибудь способ, которым я могу сделать с помощью любого сокращения Map без Hadoop?

Подход-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

1 Ответ

2 голосов
/ 16 апреля 2019

Вот одно решение, основанное на предложении в одном из комментариев:

Если вы можете, например, префикс строк с ключом сортировки, чтобы они могли быть отсортированы как текст, а не JSON, тогда сортировка GNU может легко сортировать файлы размером более 10 ГБ, не загружая их в память. - тот другой парень

Вы можете использовать jq, чтобы сделать это следующим образом:

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

В результате получатся строки со значениями, разделенными табуляцией, например:

abc {"ps":"abc","x":0}
abc {"ps":"abc","x":1}

Использование параметра -c гарантирует, что каждая пара (т. Е. Ключ сортировки и объект) записывается в одну строку.

Теперь вы можете легко сортировать строки, например, используя sort; а затем использовать, например, cut чтобы удалить поле .ps.

Наконец, если вы действительно хотите отформатировать вывод, вы можете снова использовать jq (например, jq .), суть в том, что jq по умолчанию ориентирован на поток.

Протест

Вышеприведенное предполагает, что значения .ps не имеют вкладок. Если это не так, вы можете использовать другой разделитель полей или:

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