Используя jq, как я могу разделить поток объектов JSON на отдельные файлы на основе значений свойства объекта? - PullRequest
0 голосов
/ 16 февраля 2019

У меня есть очень большой файл (20 ГБ + сжатый) с именем input.json, содержащий поток объектов JSON следующим образом:

{
    "timestamp": "12345",
    "name": "Some name",
    "type": "typea"
}
{
    "timestamp": "12345",
    "name": "Some name",
    "type": "typea"
}
{
    "timestamp": "12345",
    "name": "Some name",
    "type": "typeb"
}

Я хочу разбить этот файл на файлы в зависимости от их свойства type: typea.json, typeb.json и т. Д., Каждый из которых содержит свой собственный поток json-объектов, имеющих только совпадающее свойство типа.

Мне удалось решить эту проблему для небольших файлов, однако с такими большимиНа моем экземпляре AWS не хватает памяти.Поскольку я хочу уменьшить использование памяти, я понимаю, что мне нужно использовать --stream, но я изо всех сил пытаюсь понять, как мне этого добиться.

cat input.json | jq -c --stream 'select(.[0][0]=="type") | .[1]' вернет мне значения каждого типасвойства, но как я могу использовать это, чтобы затем фильтровать объекты?

Любая помощь будет принята с благодарностью!

Ответы [ 2 ]

0 голосов
/ 16 февраля 2019

Использование jq для разделения на разделенный NUL поток пар (тип, документ) и использование собственного bash (4.1 или более поздней версии) для записи в эти документы с использованием постоянного набора файловых дескрипторов:

#!/usr/bin/env bash
case $BASH_VERSION in ''|[1-3].*|4.0*) echo "ERROR: Bash 4.1 needed" >&2; exit 1;; esac

declare -A output_fds=( )

while IFS= read -r -d '' type && IFS= read -r -d '' content; do
  if [[ ${output_fds[$type]} ]]; then  # already have a file handle for this output file?
    curr_fd=${output_fds[$type]}       # reuse it, then.
  else
    exec {curr_fd}>"$type.json"        # open a new output file...
    output_fds[$type]=$curr_fd         # and store its file descriptor for use.
  fi
  printf '%s\n' "$content" >&"$curr_fd"
done < <(jq -j '(.type) + "\u0000" + (. | tojson) + "\u0000"')

Это никогда не считывает в память более нескольких записей (предположительно, потенциально нескольких копий каждой) за раз, поэтому оно будет работать с произвольно большим файлом, если записи имеют разумный размер.

0 голосов
/ 16 февраля 2019

Если предположить, что объекты JSON в файле относительно малы (не более нескольких МБ), вам не нужно использовать (довольно сложный) параметр командной строки «--stream», который в основном необходим, когдавход является (или включает в себя) одну огромную сущность JSON.

Однако есть еще несколько вариантов.Основные из них описаны в Разделить файл JSON на отдельные файлы , это многопроходный подход (N или (N + 1) обращений к jq, где N - количество выходных файлов), иподход, который включает в себя только один вызов jq с последующим вызовом программы, такой как awk, для фактического разбиения на файлы.У каждого подхода есть свои плюсы и минусы, но если чтение входного файла N раз приемлемо, тогда первый подход может быть лучше.

Для оценки общих вычислительных ресурсов, которые потребуются, это, вероятно, будет хорошимИдея измерить ресурсы, используемые при запуске jq empty input.json

(Исходя из вашей краткой записи, это звучит как проблема с памятью, с которой вы столкнулись, прежде всего из-за разархивирования файла.)

...