JQ: как настроить функцию распыления для большого случая использования потоковой передачи? - PullRequest
1 голос
/ 30 апреля 2019

У меня очень большой файл со следующей структурой:

{ 
  "users": { ... },
  ...
  "stats": {
    "daily": {
      "k1": { ... },
      "k2": { ... },
      ...
      "kN": { ... }
    },
    "monthly": {
      "p1": { ... },
      "p2": { ... },
      ...
      "pN": { ... }
    }
  }
}

В stats есть только два ключа: daily и monthly, которые оба содержат очень большое количество ключейпары-значения.

Я бы хотел передать все пары ключ-значение внутри .stats.daily и .stats.monthly отдельно.Если бы файл был маленьким, я просто сделал бы jq '.stats.daily' myfile.json и jq '.stats.monthly' myfile.json

Я не могу понять, как редактировать функцию atomize из поваренной книги, чтобы делать то, что я хочу.Вот что я пытаюсь, но это не работает:

jq -nc --stream '
  def atomize(s):
    fromstream(foreach s as $in ( {previous:null, emit: null};
      if ($in | length == 2) and ($in|.[0][0]) != .previous and .previous != null
      then {emit: [[.previous]], previous: $in|.[0][0]}
      else { previous: ($in|.[0][0]), emit: null}
      end;
      (.emit // empty), $in) ) ;
  atomize(2|truncate_stream(inputs | select(.[0][0] == "daily"))

Может кто-нибудь объяснить, как это работает и как это исправить для моего варианта использования?Спасибо

1 Ответ

1 голос
/ 30 апреля 2019

Поскольку вы указали, что хотите обрабатывать «ежедневные» значения отдельно от «ежемесячных» значений, давайте сосредоточимся на первом.

Для этого давайте начнем с использования fromstream и truncate_stream:

С вводом, подобным приведенному в примере, но настроенным так, чтобы он действовал в формате JSON:

fromstream( 1|truncate_stream(1|truncate_stream(
  inputs | select( .[0][0] == "stats" and .[0][1] == "daily" ) )) )

даст:

{"k1":{"a":[1]},"k2":{"a":[1]},"kN":{"a":[1]}}

Если у вас есть jq 1.6, то приведенный выше фильтр jq можно упростить до:

fromstream(2|truncate_stream(
  inputs | select( .[0][0:2] == ["stats","daily"] ) ))

Теперь мы должны использовать atomize вместо fromstream для получения желаемого результата. Например, используя jq 1.6, мы видим, что:

atomize(2|truncate_stream(
  inputs | select( .[0][0:2] == ["stats","daily"] ) ))

будет производить:

{"k1":{"a":[1]}}
{"k2":{"a":[1]}}
{"kN":{"a":[1]}}

1024 * Воззвание * jq -n -c --stream -f program.jq input.json Повышение эффективности

При условии, что объекты на входе не имеют повторяющихся ключей, приведенное выше решение может быть упрощено, так что после обработки интересующего ключа дальнейшая обработка не производится. Это может быть достигнуто с помощью run/3, как определено ниже. Тогда потоковое решение становится:

atomize( 1 | truncate_stream( 1 | truncate_stream(
  run( inputs; .[0][0:2]; ["stats", "daily"] ))))

или с JQ 1.6:

atomize( 2 | truncate_stream(
  run( inputs; .[0][0:2]; ["stats", "daily"] )))

run/3

# emit the first run of items in the stream for which f == $value
def run(stream; f; $value):
  label $done
  | foreach stream as $x ( {};
      ($x | f) as $k
      | if .start then (if $k == $value then . else .stop = true end)
        elif $k == $value then .start = true
        else .
        end;
      if .stop then break $done 
      elif .start then $x
      else empty
      end );
...