分厚いjoinを複数のexploreに継承して保守性を高める

# TL;DR

以下の手順を踏むと、元のexploreの全ての`join`を継承し、ベーステーブルのみを差し替えられる

1. いずれかを選択する
  a. 継承元のexploreにおいて、ベーステーブルを`view_name`で指定する
  b. 継承元のexploreにおいて、ベーステーブルを`from`で指定し、独自の`view_name`名を定義する
2. 継承先のexploreにおいて、`extends`で継承元のexploreを指定し、`from`でベーステーブルのみを差し替える

# はじめに

Lookerでは、「ベーステーブル」（Exploreのベースとなる、最初に指定するテーブル）に様々なテーブルをjoinしてExploreを作ります。
似たようなベーステーブルが複数存在することはよくあります。例えば、分析のユースケースに従って似たようなサマリーテーブルを複数作成する場合などです。

大量のテーブルによる分厚いjoinがある場合、それぞれのベーステーブルに同じjoinを書いてExploreを作るのは冗長なので、`extends`を使ってコードを継承させることを検討します。

# 課題

viewでデータソースを差し替える場合、以下のように適用できます。

```lookml
view: summary_table_alike {
  extends: [summary_table] # ← 継承元のview定義を引っ張ってきて
  sql_table_name: `summary_table_alike` # ← viewのデータソースを差し替えるだけ
}
```

しかし、exploreでベーステーブルを差し替えても、`join > sql_on`のコードは差し変わりません。
joinの際に参照されるベーステーブル名をそのままにしないと、`sql_on`のkeyが見つからないというエラーが出てしまいます。

```lookml
# 継承元のexplore
explore: summary_table {
  join: dimension_table {
      relationship: many_to_one
      # ベーステーブルは${summary_table}で参照される
      sql_on: ${summary_table.dimension_key} = ${dimension_table.key} ;;
  }
}

# NGパターン1
# ベーステーブルは${summary_table_alike}で参照されるので、sql_onの${summary_table.dimension_key}が見つからない
explore: summary_table_alike {
  extends:[summary_table]
}

# NGパターン2
# ベーステーブルは${summary_table_alike2}で参照されるので、sql_onの${summary_table.dimension_key}が見つからない
explore: summary_table_alike2 {
  extends:[summary_table]
  from: summary_table_alike
}

# NGパターン3
# ベーステーブルは${summary_table_alike}で参照されるので、sql_onの${summary_table.dimension_key}が見つからない
explore: summary_table_alike3 {
  extends:[summary_table]
  view_label: summary_table_alike
}
```

# 仕様の整理

exploreにおける「explore名」「from」「view_name」の仕様を整理します。

## explore名

`from`や`view_name`が未指定な場合、Lookerはexploreのベーステーブルにexplore自体の名前（explore名）と同名のviewを使います。
以下の例では、`customer`viewをベーステーブルに使い、joinの際には`${customer.some_key}`という形式で参照します。

```lookml
explore: customer {}
```

## from

`from`が指定されている場合、ベーステーブルに`from`で指定されたviewを使います。  
joinの際にはベーステーブルを**explore名**で参照します。
以下の例では、`user`viewをベーステーブルに使い、joinの際には`${customer.some_key}`という形式で参照します。

```lookml
explore: customer {
  from: user
}
```

SQLにおけるFROM句の書き換えに相当します。AS句にはexplore名を使います。

## view_name

`view_name`が指定されている場合、ベーステーブルに`view_name`で指定されたviewを使います。  
joinの際には**view_name**で参照します。
以下の例では、`user`viewをベーステーブルに使い、joinの際には`${user.some_key}`という形式で参照します。

```lookml
explore: customer {
  view_name: user
}
```

SQLにおけるFROM句とAS句の両方の書き換えに相当します。

## from と view_name を両方使う

`from`と`view_name`が両方使われている場合、Lookerは以下の挙動をします。

1. `from`で指定したviewをベーステーブルとして用いる
2. `join`の際には、ベーステーブルを**view_name**で参照する。view_nameに独自の名前を定義できる

以下の例では、`user`viewをベーステーブルとして使い、joinの際には`${customer.some_key}`という形式で参照し、explore自体を`customer_analysis`で参照します。

```lookml
explore: customer_analysis {
  from: user
  view_name: customer
}
```

## 整理

まとめると、以下のようになります。  

|  | 何も指定しない | fromを指定 | view_nameを指定 | fromを指定、view_nameを定義 |
| -------- | -------- | -------- | -------- | -------- |
| ベーステーブル名 | explore名 | **from名** | view_name名 | from名 |
| joinの参照名 | explore名 | explore名 | **view_name名** | view_name名 |

exploreを継承するためには、ベーステーブル名を差し替えつつ、joinの参照名をそのままにする必要がありました。
joinの参照名を`view_name`とし、継承によって`from`のみを差し替えればこれを達成できます。

# 解決策１

具体的な手順は以下です。

1. 継承元のexploreにおいて、`from`を用いてソーステーブルを指定し、任意の`view_name`を定義する
2. 継承先のexploreにおいて、`extends`で元のコードを継承し、`from`を上書きしてベーステーブルを差し替える

```lookml
explore: summary_table_explore { # わかりやすいようにexplore名を変えておく
  label: "サマリ"
  from: summary_table
  view_name: summary_table_view # explore内はview_nameで参照できるようになる
  # 以下分厚いjoinが並ぶ
  join: dimension_table{
   # view_nameで参照している ↓
  　　sql_on: ${summary_table_view.dimension_key} = ${dimension_table.key} ;;
  }
  ...
}

explore: summary_table_alike_explore {
  label: "サマリ（類似）"
  extends: [summary_table_explore] # 元のコードを継承
  from: summary_table_alike # ベーステーブルを差し替え
}
```

|  | 継承元 | 継承先 |
| -------- | -------- | -------- |
| explore名 | summary_table_explore | summary_table_alike_explore |
| ベーステーブル名 | summary_table | summary_table_alike |
| joinの参照名 | summary_table_view | summary_table_view |

joinの参照名が変わらないため、継承後のexploreも元のjoinコードを使えます

# 解決策２

`from`を削っても動きます。

1. 継承元のexploreにおいて、`view_name`を用いてソーステーブルを指定する
2. 継承先のexploreにおいて、`extends`で元のコードを継承し、`from`を上書きしてベーステーブルを差し替える

```lookml
explore: summary_table_explore {
  label: "サマリ"
  view_name: summary_table
  join: dimension_table{
  　　sql_on: ${summary_table.dimension_key} = ${dimension_table.key} ;;
  }
  ...
}

explore: summary_table_alike_explore {
  label: "サマリ(類似)"
  extends: [summary_table_explore]
  from: summary_table_alike
}
```

|  | 継承元 | 継承先 |
| -------- | -------- | -------- |
| explore名 | summary_table_explore | summary_table_alike_explore |
| ベーステーブル名 | summary_table | summary_table_alike |
| joinの参照名 | summary_table | summary_table |

# 感想

新たなテーブルをjoinする際、継承元のexploreに`join`を追加するだけで全てに適用できるようになりました。
冗長なコードが減って保守が楽になりました。

# 参考

<https://cloud.google.com/looker/docs/reference/param-explore-extends>
<https://cloud.google.com/looker/docs/reusing-code-with-extends?hl=ja>