DEV Community

Weerasak Chongnguluam
Weerasak Chongnguluam

Posted on • Updated on

ความ magic ของ Ecto.Query

Ecto.Query เนี่ยเป็น module ที่ช่วยให้เราเขียน SQL Query ด้วย syntax ของ Elixir ความ magic ของมันคือหน้าตามันเหมือนจะเรียก Elixir function โดยตรงเนี่ยแหละ แต่มันไม่ใช้ สิ่งที่เขียนไปมันจะโดน macro แปลงไปอยู่ในรูปของ SQL Query ให้

ตัวอย่างเช่น โค้ดที่เขียน Query แบบนี้

from p in Post, select: sum(p.wrapped_visits)
Enter fullscreen mode Exit fullscreen mode

ซึ่งมันยังคงถูก syntax Elixir นะ อธิบายทีละส่วนคือมันเรียก macro ชื่อ from

ทีนี้เวลาเรียก macro สิ่งที่เราส่งให้มันจะยังไม่ถูก evaluate ใดๆ ดังนั้น p in Post และ sum(p.wrapped_visits) ก็จะยังไม่เกิดอะไรขึ้น sum(p.wrapped_visits) ที่เราเห็นว่าเหมือนเรียกฟังก์ชัน จริงๆแล้วไม่มีโค้ด function หรือ macro ชื่อ sum ใน Ecto.Query ด้วยซ้ำ

ทั้งสองส่วนนี้จะถูกแปลงเป็นรูปแบบ AST (Abstract Syntax Tree) หลังจากนั้นตัว from macro จะค่อยแปลง AST เป็น SQL Query อีกที

ถ้าเราไปลองอ่านๆโค้ดของ Ecto ที่ https://github.com/elixir-ecto/ecto/blob/v3.5.5/lib/ecto/query/builder.ex จะเจอจุดที่คิดว่าเป็น logic ในการแปลงตรง sum(p.wrapped_visits) เป็น SQL คือ

  @static_aggregates [
    count: {0, :integer},
    count: {1, :integer},
    count: {2, :integer},
    avg: {1, :any},
    sum: {1, :any},
    row_number: {0, :integer},
    rank: {0, :integer},
    dense_rank: {0, :integer},
    percent_rank: {0, :any},
    cume_dist: {0, :any},
    ntile: {1, :integer}
  ]
Enter fullscreen mode Exit fullscreen mode

และ

  for {agg, {arity, return}} <- @static_aggregates do
    defp call_type(unquote(agg), unquote(arity)), do: {:any, unquote(return)}
  end
Enter fullscreen mode Exit fullscreen mode

นั้นคือเวลาเรียก sum(p.wrapped_visits) มันจะถูกแปลงเป็น AST แล้วเอาข้อมูลใน AST มา matching กับ call_type ที่มีจนได้ข้อมูลเพียงพอที่จะแปลงเป็น SQL ต่อไปนั่นเอง

Buy Me A Coffee

Top comments (0)