@kyanny's blog

My life. Opinions are my own.

Ecto の Getting Started を駆け足でやった

hexdocs.pm

会社で Elixir を使ってるプロジェクトがあり、そこの担当者を中心とした有志で毎週 Elixir のゆるい勉強会をやっている。都合が合うときは参加するようにしている。

Elixir のチュートリアルは全て読み終わったようで、最近はそのプロジェクト内で挙がった疑問点などについて雑談することが多い。直近の会では Ecto という Elixir 界隈では有名らしい O/R マッパのようなものの話題で、クエリするコードの書き方が二種類あるがどちらを使うべきなのか、という話題だった。

一つは SQL 風の書き方で、キーワードリストを使うので keywords syntax とか keywords query syntax と呼ばれるらしい。こちらのほうが古いバージョンの Ecto から存在するらしい。

from u in "users", where: u.age > 18, select: u.name

もう一つはパイプライン演算子(Pipe Operator)を使う書き方で、 pipe-based syntax とか expression query syntax と呼ばれるらしい。こちらのほうが新しいバージョンの Ecto から利用できるようになったらしい。

"users"
|> where([u], u.age > 18)
|> select([u], u.name)

個人的には keywords syntax のほうが SQL ほぼそのままな感じで読み書きしやすそうで好みだった(おれはそのプロジェクトでプロダクションコードを書かないので、 keywords syntax を使うべきだと強く主張はしなかった)。パイプライン演算子を使ったほうが Elixir らしくて良い、という意見もあった。

単純なクエリだとどっちでもよさそうにしか見えないので、ちょっと複雑だが仕事では当然書くようなクエリを両方で書いて比べてみたらどうか?と提案し、 JOIN するクエリを書き比べたところ、 JOIN するクエリは keywords syntax のほうがよさそう、という結論になった。

(以下のクエリは実際に書き比べてみたプロダクションコードのシンボル名などを適当に書き換えたものだが、書き換えをミスっておかしな書式になっているかもしれない)

# keywords query syntax
query = 
  from users in User,
    join: user_bookmarks in assoc(users, :user_bookmarks),
    join: bookmarks in assoc(user_bookmarks, :bookmarks),
    where:
      users.first_name == "John" and
      users.last_name == "Doe" and
      not is_nil(users.age)

# expression query syntax (Macro API)
query =
  User
  |> join(:inner, [users], user_bookmarks in assoc(users, :user_bookmarks))
  |> join(:inner, [user_bookmarks], bookmarks in assoc(user_bookmarks, :bookmarks))
  |> where(
      [users],
      users.first_name == "John" and users.last_name == "Doe" and
        not is_nil(users.age)
  )

(ここまで前置き)その場ではただ見ながら聞いてただけだったけど、実際に自分でも少しでいいから書き比べてみたくなったので Ecto を触ってみようと思い、 Getting Started をななめ読み写経してみた、という次第。

クエリの書き比べをしてみるのが目的だったので、プロジェクトや DB のセットアップが終わったら途中は全部すっ飛ばしてクエリするところだけやった。やってみた感想は、「しょせんチュートリアルの例では、書き比べて差がわかるようなものではない」という、当たり前で時間割いた割にはいまいちな結果だった。

おまけ

Docker で PostgreSQL コンテナを立ち上げたり停止したり消したり

docker run --name ecto_friends_repo -e POSTGRES_PASSWORD=pass -e POSTGRES_USER=user -e POSTGRES_DB=friends_repo -p 5432:5432 -d postgres

docker stop ecto_friends_repo

docker rm ecto_friends_repo