Ecto 3.0のリリースに先立ち、plataformatec社開発チームのblog記事「A sneak peek at Ecto 3.0: query improvements (part 1)」が公開されました。本稿はplataformatec社の許諾によりこの記事にもとづいて、Ecto 3.0のもっとも大きな改善項目であるEcto.Query
APIについてご紹介します。blog記事は次回part 2が予定されていますので、本稿は前編としました。
名前つきバインディングにより結合の構成を改善
Ectoはjoin
により、複数のスキーマとテーブルを結合することがつねにできます。
query =
from p in Post,
join: c in Comment,
where: p.id == c.post_id,
select: c
ここで、クエリに手を加えて、公開されたコメントだけが返されるようにしましょう。前掲のクエリは、つぎのように構成できます。
from [_, c] in query, where: c.public
前掲コード例からわかるように、クエリ(p
およびc
)の中にあるバインディングを抽出してから、フィルタが適用できます。この例のバインディングは位置をともない、in
の左辺のリストにおける順序に依存します。p
とc
という名前は暫定で、クエリ全体には関係しません。つまり、つぎのように書き替えても、クエリの意味は同じです。
from [_, comment] in query, where: comment.public
位置をともなうバインディングで問題なのは、クエリの構成がきわめてむずかしくなる場合があることです。込み入った検索の機能を構築しようとして、複数のテーブルを結合し、順序を変えて、位置バインディングを追跡することは、複雑で脆弱性が生じやすくなります。
Ecto 3.0は、from
とjoin
それぞれに名前がつけられるように改めます。前掲のクエリがつぎのように書き替えられるのです。
query =
from p in Post,
join: c in Comment,
as: :comments,
where: p.id == c.post_id,
select: c
join
のあとにad
オプションが加わえられたことにご注目ください。既存の:comments
をフィルタリングするときも、クエリ内における順序にかかわらず、つぎのように書けます。
from [comments: c] in query, where: c.public
位置バインディングをキーワードリストに置き替えます。キーがバインディング名で、値は結合を割り当てる変数です。ここでも、変数c
は暫定ですので、どのような名前でも構いません。重要なのは、既存の:comments
にバインディングするということです。
Ecto 3.0は明示的な名前の仕組みとして、変数名によるのでなく、:as
オプションを採り入れました。変数名はうっかり重複してしまうことがあるからです。とくに、開発者がクエリの変数名をひと文字に省いているとそのおそれは高まるでしょう。さらに、同じ名前に何度もバインディングしようとすれば、エラーが生じます。
もうひとつ挙げておきたいのは、:as
オプションはfrom
にも与えられるということです。
query =
from p in Post,
as: :posts,
join: c in Comment,
as: :comments,
where: p.id == c.post_id,
select: c
名前つきバインディングにより、複雑な検索フォームや検索APIなど動的なクエリが、Ectoでさらに柔軟に構築できます。
データベース接頭辞とインデックスヒント
名前つきバインディングを加えるためにつくったEctoの基盤に、さらにふたつの機能が加えられます。from
/join
接頭辞とインデックスヒントです。
Ecto v2.0に接頭辞という考え方が採り入れられました。接頭辞(プレフィックス)の意味は、データベースエンジンによって異なります。Postgresでは、接頭辞はPostgresスキーマのことです。Postgresのデータベースには複数のスキーマがあり、デフォルトのスキーマは「public」と呼ばれます。MySQLはスキーマをサポートしていないので、接頭辞の機能は単に異なるデータベースの意味になります。
Ecto v2.0に接頭辞が採り入れられた目的は、異なる接頭辞からデータを簡単に選択、挿入、更新、あるいは削除することでした。目指したのは、マルチテナントアプリケーションをサポートすることです。けれど、Ecto v2.0では、一度に扱える接頭辞がひとつにかぎられていました。たとえば、クエリをひとつ書いて、異なるふたつの接頭辞の間でデータを結合することができません。
Ecto v3.0ではこの制限が外れます。from
/join
に、:as
オプションと同じように、prefix
オプションが与えられるのです。たとえば、すべての投稿が公開されているシステムを考えましょう。けれど、コメントはシステムを使うクライアントに固有だとします。それは、システムに複数の接頭辞がクライアントごとにあり、各接頭辞がそれぞれのコメントテーブルをもつということです。すると、それらの接頭辞にわたるつぎのようなクエリが書けます。
from p in Post,
prefix: "public",
join: c in Comment,
prefix: "client1",
where: p.id == c.post_id,
select: c
さらに、Ecto 3.0では同様のAPIにより、MySQLやMSSQLデータベースにもあるインデックスヒントが使えるようになります。
from p in Post,
join: c in Comment,
hints: ["USE INDEX FOO", "USE INDEX BAR"],
where: p.id == c.post_id,
select: c
ヒントはあまり使わないかもしれません。こうした機能については、データベース免責事項を必ずお読みください。
接頭辞とヒントのオプションにより、開発者はより柔軟にクエリを組み立てて最適化し、Ecto.Query
の力を余すことなく活用することができます。SQLへのフォールバックも避けられます。
その他の変更
Ecto.Query
はwhere
とhaving
にタプルが使えるようになります。たとえば、where: {p.foo, p.bar} > {^foo, ^bar}
といったクエリが書けるのです。カーソルベースのページング処理などに使えるでしょう。
+
や-
、*
、/
などの算術演算子もサポートされます。ただし、これらの演算子はもとにえるデータベースエンジンに委任するだけです。したがって、データベースを必ず調べ、どの型が被演算子にできるかお確かめください。
最後に、テーブルやソース全体を引数とするデータベース関数が呼び出せるようになります。fragment("some_function(?)", p)
という使い方です。
次回の[後編]では、Ecto 3.0のEcto.Query
に加えられた改善や機能についてさらにご説明します。
Top comments (0)