jqで複数のjsonファイルから特定のデータを抽出してみた

2023/09/27に公開

はじめに

こんにちはスペースマーケットでエンジニアをしているderaです。

先日、集計の中間データとして出力されるjsonの解析する事がありました。ファイルサイズが数百MBありエディターで開くことが難しかったが、jqを使うことで内容の確認をする事ができました。今回はその内容を備忘録として残しておきます。

環境

  • macOS Ventura 13.5.2
  • jq 1.7

どのような事をしたのか

まずどのような事をしたのかですが、以下のようなデータがあったとします。

// data1.json
{
  "type": "タイプ1",
  "users": [
    {
      "id": 1,
      "values": [
        {
          "id": 1,
          "value": 100
        }
      ]
    },
    {
      "id": 2,
      "values": [
        {
          "id": 2,
          "value": 200
        }
      ]
    }
  ]
}
// data2.json
{
  "type": "タイプ2",
  "users": [
    {
      "id": 2,
      "values": [
        {
          "id": 3,
          "value": 300
        },
        {
          "id": 4,
          "value": 200
        }
      ]
    },
    {
      "id": 3,
      "values": [
        {
          "id": 5,
          "value": 300
        }
      ]
    }
  ]
}
// data3.json ~ data7.json も同じフォーマットのデータが続く

ファイルの内容を簡単に説明すると、オブジェクトの第1階層のtypeはデータの種類を表していてファイル毎に違っています。ファイルは1から7まであるので、7種類のデータが存在するという事になります。次に、オブジェクトの第1階層のusersですが、これはユーザごとの値を配列形式で持っています。

今回はこのような形式のjsonから、特定の条件のデータのみ抽出して、内容の確認を行うという事をしました。

結論

結論を先に書くと、次のコマンドを実行して、ユーザID:2のデータを抽出しました。
複雑になるので、解説時はdata1とdata2のみを対象として書かせていただきます。

$ cat data1.json data2.json | jq -s '.[0].users + .[1].users|.[]|select(.id==2)' | jq -s
[
  {
    "id": 2,
    "values": [
      {
        "id": 2,
        "value": 200
      }
    ]
  },
  {
    "id": 2,
    "values": [
      {
        "id": 3,
        "value": 300
      },
      {
        "id": 4,
        "value": 200
      }
    ]
  }
]

解説

cat data1.json data2.json | jq -s '.[0].users + .[1].users|.[]|select(.id==2)' | jq -s
このコマンドをいくつかに分解して解説します。

cat data1.json data2.json | jq -s

まず、jqの基本的な使い方ですが、jqは標準入力から受け取ったJSONを加工して標準出力に出力するコマンドです。

複数のファイルからデータを取得したいので、catコマンドでファイルを結合してjqに渡しています。

また、-sオプションをつける事で、複数のファイルを結合した際に、それらを配列にしてくれます。

ここまでの部分を実行すると、次のような結果が得られます。

$ cat data1.json data2.json | jq -s
[
  {
    "type": "タイプ1",
    "users": [
      {
        "id": 1,
        "values": [
          {
            "id": 1,
            "value": 100
          }
        ]
      },
      {
        "id": 2,
        "values": [
          {
            "id": 2,
            "value": 200
          }
        ]
      }
    ]
  },
  {
    "type": "タイプ2",
    "users": [
      {
        "id": 2,
        "values": [
          {
            "id": 3,
            "value": 300
          },
          {
            "id": 4,
            "value": 200
          }
        ]
      },
      {
        "id": 3,
        "values": [
          {
            "id": 5,
            "value": 300
          }
        ]
      }
    ]
  }
]

.[0].users + .[1].users

次に、jqで配列を扱う方法ですが、.[index]という形式で配列の要素にアクセスできます。なので、.[0].users は1つ目のファイルのusersを、.[1].users は2つ目のファイルのusersを取り出しています。

また、配列の結合は + で行えます。なので、.[0].users + .[1].users は1つ目のファイルと2つ目のファイルのusers配列を結合しているという事になります。

ここまでの部分を実行すると、次のような結果が得られます。

$ cat data1.json data2.json | jq -s '.[0].users + .[1].users'
[
  {
    "id": 1,
    "values": [
      {
        "id": 1,
        "value": 100
      }
    ]
  },
  {
    "id": 2,
    "values": [
      {
        "id": 2,
        "value": 200
      }
    ]
  },
  {
    "id": 2,
    "values": [
      {
        "id": 3,
        "value": 300
      },
      {
        "id": 4,
        "value": 200
      }
    ]
  },
  {
    "id": 3,
    "values": [
      {
        "id": 5,
        "value": 300
      }
    ]
  }
]

|.[]|select(.id==2)

次に、users配列から特定のユーザIDのデータを抽出します。jqでは |(パイプ)を使うことで、それまでの結果に対して処理をおこなう事ができます。selectを行う際に配列のままだと、配列の要素がすべて抽出されてしまうので、配列の要素を展開するために、|.[]を挟んでいます。取り出したオブジェクトに対して、selectを行う事で、条件に合致するオブジェクトのみを抽出しています。

ここまでの実行結果は以下のようになります。
ユーザIDが2のデータのみに絞り込みをすることができました。

$ cat data1.json data2.json | jq -s '.[0].users + .[1].users|.[]|select(.id==2)'
{
  "id": 2,
  "values": [
    {
      "id": 2,
      "value": 200
    }
  ]
}
{
  "id": 2,
  "values": [
    {
      "id": 3,
      "value": 300
    },
    {
      "id": 4,
      "value": 200
    }
  ]
}

jq -s

ここまでの結果でもやりたい事は達成できていますが、json形式から逸脱してしまっているので、最後にもう一度jq -sを実行して、オブジェクトを配列に詰め直しています。

まとめ

今回はjqを使って、複数のjsonファイルから特定のデータを抽出する方法を紹介しました。
jqはある程度ファイルサイズが大きくなっても使うことができ、インストールも手軽に行える点が良いと思いました。

参考

スペースマーケット Engineer Blog

Discussion