「JSONBの沼、そろそろ攻略してみない?」
こんにちは。現場でPostgreSQLと格闘し続けて早10年。今日は、若手のエンジニアがよく「これ、どうやって検索すればいいんですか……?」と頭を抱えて持ってくる、JSONBパスクエリ(jsonpath)について語らせてもらいます。
正直、昔のPostgreSQLでJSONをこねくり回そうとすると、演算子(`->` や `->>`)の嵐でクエリがスパゲッティ化しがちでしたよね。でも、PostgreSQL 12で導入された`jsonpath`のおかげで、今は驚くほどスマートに書けるようになりました。
実務で「あ、これ知っておくと寿命が延びるな」と思うポイントを、サクッと解説しますね。
—
なぜ今、jsonpathなのか?
例えば、こんな感じの「注文履歴」が格納されたJSONBカラムがあるとします。
{
“order_id”: “ORD-123”,
“items”: [
{“name”: “キーボード”, “price”: 12000, “tags”: [“gadget”, “office”]},
{“name”: “コーヒー豆”, “price”: 1500, “tags”: [“food”]}
]
}
「gadgetタグが付いていて、かつ10,000円以上の商品がある注文を探せ」なんて言われたら、従来の書き方だと……考えるだけでゾッとしますよね。でも、`jsonpath`ならこう書けます。
SELECT jsonb_path_query(data, ‘$.items[] ? (@.price > 10000 && “gadget” in @.tags)’)
FROM orders;
どうです? 読みやすさが段違いでしょう?
—
実践:jsonpathの基本文法を攻略する
jsonpathは、SQLとは別の言語ですが、覚えるべきコアな記法はそんなに多くありません。
- `$` : ルート要素(JSON全体)
- `.` : すべての子要素
- `[]` : 配列の全要素
- `?()` : フィルタ条件(ここが一番重要!)
- `@` : フィルタ内で現在評価している要素
特に `?()` の中での条件式は、SQLのWHERE句に近い感覚で書けるので、馴染みやすいはずです。
具体的な活用例:複雑な抽出
もし、「特定のタグを含む項目だけを抽出して、その名前をリストアップしたい」という要望なら、こんなクエリが実用的です。
SELECT
data->>’order_id’ as order_id,
item->>’name’ as item_name
FROM orders,
jsonb_path_query(data, ‘$.items[] ? (@.tags[] == “gadget”)’) AS item;
ポイントは、`jsonb_path_query` をFROM句で横に展開しているところです。こうすることで、JSONの中身をリレーショナルなテーブルのように扱える。これがPostgreSQLの「ハイブリッドな強み」なんです。
—
パフォーマンスの話:インデックスを貼るのを忘れずに
さて、ここまで読んでくれたあなたに、一番大事な「現場の教訓」を。
jsonpathをバリバリ使うとクエリは綺麗になりますが、フルスキャンが走るとデータ量が増えた瞬間にパフォーマンスは死にます。JSONBのインデックスと言えば `GIN` インデックスですが、jsonpathに最適化されたインデックスを作るなら、これ一択です。
CREATE INDEX idx_orders_jsonpath ON orders USING GIN (data jsonb_path_ops);
この `jsonb_path_ops` を指定したGINインデックスがあるかどうかで、数百万件規模のデータに対するレスポンスが「一瞬」か「コーヒーを淹れに行ける時間」かの違いになります。設計段階で必ず仕込んでおきましょう。
—
最後に:使いすぎには注意
ここまで褒めてきましたが、最後に先輩からのアドバイスです。
「JSONBで何でも解決しようとしないこと」
JSONBは非常に強力な武器ですが、スキーマレスであるということは「データの型や構造の保証」をDB側がしてくれないということ。本当に検索の柔軟性が必要な項目だけをJSONBに逃がし、それ以外はしっかりとしたテーブル設計を行う……このバランス感覚こそが、優れたデータベースエンジニアの証です。
「とりあえず全部JSONBに入れとけ」という運用は、半年後の自分を地獄に突き落とすことになります。
さて、今日の解説はここまで。もし「この複雑なJSON、どう抽出したらいいの?」という難問があれば、いつでも相談してください。現場からは以上です!
コメント