👋

dbt macro tips advent calendar 2022 day 8 - マニュフェストファイルとgraph

2022/12/08に公開

便利なデータ変換ツールである dbt の中のmacroに関するtipsを書いていく dbt macro tips Advent Calendar 2022 8日目です。

マニュフェストファイル

7日目は動作モードについて書きました。
dbtの動作モードには parse phaseとexecute phasseの2つあるという話でした。
ところで、parse phaseは一体何をやっているのでしょうか?

https://docs.getdbt.com/reference/dbt-jinja-functions/execute

  1. Reads all of the files in your project and generates a "manifest" comprised of models, tests, and other graph nodes present in your project. During this phase, dbt uses the ref statements it finds to generate the DAG for your project. No SQL is run during this phase, and execute == False.

とあります。
parse phaseと便宜上読んでいる execute == False となる、この動作モードの最終成果物の一つがマニフェストと呼ばれるものになります。

マニフェストに関しては、dbtのこちらのドキュメントに記載があります。

https://docs.getdbt.com/reference/artifacts/manifest-json

実際のマニフェストはdbt compiledbt build 等のコマンドを実行すると target/manifest.json が生成されています。
このjsonファイルは非常に大きく、プロジェクトごとに実際に生成されるものは異なります。

すべて載せると記事の紙面が大変なことになるので一部抜粋しますが、以下のような情報が出力されています。

target/manifest.json
{
  "metadata": {
    "dbt_schema_version": "https://schemas.getdbt.com/dbt/manifest/v7.json",
    "dbt_version": "1.3.1",
    "generated_at": "2022-11-29T02:48:01.294699Z",
    "invocation_id": "59eb1643-e0b1-4429-9a33-395afd2ef55e",
    "env": {},
    "project_id": "d44b3debde7269af69e10dc5514b1f12",
    "user_id": "e5e3afad-a5df-424c-b6c8-20fd91b835f7",
    "send_anonymous_usage_stats": true,
    "adapter_type": "postgres"
  },
  "nodes": {
    "model.macro_tips_advcal.my_second_dbt_model": {
      "resource_type": "model",
      "depends_on": {
        "macros": [],
        "nodes": [
          "model.macro_tips_advcal.my_first_dbt_model"
        ]
      },
      "config": {
        "enabled": true,
        "alias": null,
        "schema": null,
        "database": null,
        "tags": [],
        "meta": {},
        "materialized": "view",
        "incremental_strategy": null,
        "persist_docs": {},
        "quoting": {},
        "column_types": {},
        "full_refresh": null,
        "unique_key": null,
        "on_schema_change": "ignore",
        "grants": {},
        "packages": [],
        "docs": {
          "show": true,
          "node_color": null
        },
        "post-hook": [],
        "pre-hook": []
      },
      "database": "postgres",
      "schema": "dev",
      "fqn": [
        "macro_tips_advcal",
        "example",
        "my_second_dbt_model"
      ],
      "unique_id": "model.macro_tips_advcal.my_second_dbt_model",
      "raw_code": "-- Use the `ref` function to select from other models\nselect *\nfrom {{ ref('my_first_dbt_model') }}\nwhere id = 1",
      "language": "sql",
      "package_name": "macro_tips_advcal",
      "root_path": "**********秘密だよ!!!********************",
      "path": "example/my_second_dbt_model.sql",
      "original_file_path": "models/example/my_second_dbt_model.sql",
      "name": "my_second_dbt_model",
      "alias": "my_second_dbt_model",
      "checksum": {
        "name": "sha256",
        "checksum": "0ca39b66627a285b35bf62ea1e525adc812893310fdb9ae57377e14eb89ed6fd"
      },
      "tags": [],
      "refs": [
        [
          "my_first_dbt_model"
        ]
      ],
      "sources": [],
      "metrics": [],
      "description": "A starter dbt model",
      "columns": {
        "id": {
          "name": "id",
          "description": "The primary key for this table",
          "meta": {},
          "data_type": null,
          "quote": null,
          "tags": []
        }
      },
      "meta": {},
      "docs": {
        "show": true,
        "node_color": null
      },
      "patch_path": "macro_tips_advcal://models/example/schema.yml",
      "compiled_path": null,
      "build_path": null,
      "deferred": false,
      "unrendered_config": {
        "materialized": "view"
      },
      "created_at": 1669685121.600721
    },
     ...省略...
  },
  "sources": {},
  "macros": {...省略...},
  "docs": {...省略...},
  "exposures": {},
  "metrics": {},
  "selectors": {},
  "disabled": {},
  "parent_map": {...省略...},
  "child_map": {...省略...}
 }

非常に有益な情報がたくさん載っています。
世の中には、このmanifest.jsonの parent_map にあるDAG情報を元にAirflowのWorkflowを動的に生成する例などもあります。

さて、そんな便利なマニフェストですが、dbtのmacro中で参照したくありませんか?
その夢を一部可能にするのが graph コンテキスト変数です。

graph コンテキスト変数

https://docs.getdbt.com/reference/dbt-jinja-functions/graph

graph コンテキスト変数はdbtの.sqlファイル内であればどこでも利用できる便利な変数です。
この変数には、マニフェストとほぼ同様な構造の情報が格納されています。
ところで、 この graph 使用する上で注意が必要となります。
端的に言うなら、安定的に使うなら execute phaseで使うということです。
具体的には以下のようになります。

{%- if execute %}
    -- この中で {{ graph }} を使うということ。
{%- endif %}

なぜ?それは先述のマニフェストの話と関係してきます。
parse phaseの最終成果物がマニフェストという話がありました。
つまり、 parse phaseの段階ではマニフェストはまだ完成しておらず、graphのコンテキスト変数の中身は積極的に書き換えられています。
(この部分、結構大事です。逆に言えばgraphコンテキスト変数に干渉して一部書き換えるには parse phaseでないといけないということでもあります。)

ですので、 parse phaseの段階では安定的に使用できないので、graph 変数を使うなら execute phaseでのみ使うことになります。

本日の内容としましては、以上となりますがこのままでは味気ないので、最後にgraphを使用した自作パッケージのリンクを置いておきます。

https://github.com/mashiike/dbt-independent-ref

これは、dbtで何かしらの循環系をつくりたくなる特殊なケース向けのref関数を提供するパッケージです。
こちらのパッケージの実装はgraphコンテキスト変数を利用して実装しております。興味がある方は覗いてみてください。


9日目はstatementとrun_queryの話をしようと思います。

Discussion