LoginSignup
45
43

More than 5 years have passed since last update.

Elixir Ectoのスキーマ定義と合成可能なクエリ - モジュールからデータベース用DSLへ

Posted at

前回の続き。
今回はEcto.Queryableとクエリ合成、Migrationの詳細。

適切な道具があれば、人間の生産性と思考力は大きく飛躍する。Ectoはデータベースを操作することに特化したプログラミング言語のように操作することができる。
他のプログラミング言語では真似できないほどの柔軟性を持ったElixirだからこそできることだ。
データベースを操作するモジュールのEctoでは、データベースを操作するために作られたプログラミング言語のようなエレガントなAPIを通してアプリケーションを構築することができる。

マイグレーション

classでもstructでもなく、スキーマ定義はschemaマクロで構築できたように、マイグレーション定義においても、SQLシンタックスのように、createdropを通して操作できる。

create

mix ecto.gen.migration create_users

とすると、モジュールと空のchange/0関数のあるファイルが生成されるので以下のようにして、テーブル定義をする。

defmodule MyRepo.Migrations.CreateUsers do
  use Ecto.Migration

  def change do
    create table(:users) do
      add :username, :string
      add :email, :string
      add :age, :integer
      add :group_id, references(:groups)

      timestamps
    end
    create index(:users, [:group_id])
    create index(:users, [:username], unique: true)
  end
end

timestamps:inserted_at:updated_atをカラムに追加する。

drop

dropでテーブルやインデックスを削除する。

drop table(:users)
drop index(:users, [:username])

alter

alterはテーブルの定義を変更するときに使う。
removemodirydescriptionが使える。

alter table(:users) do
  remove :age
  modify :age, :years
  add :description, :text
end

exists?

テーブルの存在を確認したいときはこうする。
elixir:
exists? table(:users)

rename

renameでテーブル名を変更することもできる。

rename table(:users), table(:new_users)

Ectoのクエリ

Elixirのクエリはfromマクロを通して構築することができる。そして関数合成のようにクエリを 合成可能 なことが大きな特徴だ。
クエリは以下のようなお馴染みのSQLのような構文で構築していける。

from u in User,
where: u.age > 20,
select: u.name

使用可能なクエリ

  • distinct
  • exclude
  • from
  • group_by
  • having
  • join
  • limit
  • lock
  • offset
  • order_by
  • preload
  • select
  • update
  • where

使用可能なオペレーター

  • ==, !=, <=, >=, <, >
  • and, or, not
  • in
  • like, ilike
  • is_nil
  • count, avg, sum, min, max
  • datetime_add, date_add

like, ilike

from b in Books,
where: ilike(b.title, "Elixir%")

avg, sum, min, max

from b in Books,
select: avg(b.price)

datetime_add, date_add

直近1ヶ月に発売された本のリストを返すクエリはこう書けば良い。

from book in Books,
where book.published_at > datetime_add(^Ecto.DateTime.utc, -1, "month")

Elixirコードの挿入

Elixirのソースコードをクエリに含めるには変数や式の前に^をつける。

from u in User,
where: u.age > ^age and u.height > ^(height * 100)

クエリの合成

クエリ操作は合成することができる。第一引数をQueryableプロトコルを実装した変数を受け取る関数定義をしておいてパイプ演算子で合成するとかっこいい。

defmodule App.Book do
  use App.Web, :model

  schema "books" do
    field :title, :string
    field :price, :integer
    field :published_at, Ecto.DateTime

    timestamps
  end

  def published_recently(query) do
    from b in query,
    where: b.published_at > datetime_add(^Ecto.DateTime.utc, -1, "month")
  end

  def starts_with(query, title) do
    from b in query,
    where: ilike(b.title, ^(title <> "%"))
  end

  def lower_price(query, price) do
    from b in query,
    where: b.price < ^price
  end
end

と定義しておけば、直近1ヶ月で、Elixirから始まるタイトルかつ3000円以下の本を以下のように取得してくることができる。

alias App.Book
Book 
|> Book.starts_with("Elixir") 
|> Book.lower_price(3000) 
|> Book.published_recently
|> Repo.all
45
43
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
45
43