[Elixir ORM #0] 开始

工作中重度使用 Elixir,之前一直是用 erlport 包装了 sqlalchemy ,但在 Elixir 里用起来却非常不舒服,于是转头去研究 ecto ,然后发现几个地方用起来不太舒服,具体哪里就不在这里吐槽了,总之就是决定自己搞一套类 ORM 的东西,因为个人看来 ORM 和 OO 是绝配,不适合在 FP 中搞,写 maru 的时候从语法级别抄 grape 抄出来的不卫生宏的坑还没填上,所以这次只参考 AR 的 query 语法,其它完全针对 Elixir 语言设计。

基本的语法已经设计完成,因为以下三个原因项目不能开源,所以决定写一系列 blog 记录开发流程。

  1. 业务针对性很强,几乎没有通用性。
  2. 使用了 `~>` 和 `<~` 两个运算符,容易和别的库冲突。
  3. 功能极简,作为微服务设计,单项目使用没有优势。

从开始到基本的 demo 跑通大约用了一周的时间,取名 Ench。

Repo & Model 定义设计如下:

config :ench, Repo,
  adapter:  Ench.Adapters.MySQL,
  poolsize: 2,
  hostname: "127.0.0.1",
  port:     3306,
  database: "test",
  username: "u",
  password: "p"

defmodule Repo do
  use Ench.Model.Repo
end

defmodule Resume do
  use Repo, table: "resumes"
end

defmodule Collection do
  use Repo, table: "collections"
end

defmodule User do 
  use Repo, table: "users" 
  use Ench 

  expose :resumes, [lazy: true, with: user] do 
    Resume ~> where(%{user_id: user[:id]})
  end 

  def collect_job(user, job_id) do
    Collection <~ ins(%{user_id: user[:id], job_id: job_id})
  end
end

DSL 设计如下:

调用:

u = User ~> find(1)  # find_by(%{id: 1})
u[:resumes] ~> all
u ~> collect_job(1)

查:

User ~> where("age > ?", [20]) ~> limit(10) ~> offset(10) ~> all
User ~> where("age > :age", %{age: 20}) ~> order(:id, :desc) ~> all
User ~> where(%{name: "falood"}) ~> first

增:

User <~ ins(%{name: "falood", phone: "1333333333"})

改:

user = User ~> find(1)
user <~ set(%{name: "neko"})
user <~ inc(%{age: 1})

删:

user = User ~> find(1)
user <~ del

开发过程中,池用的 pooler,postgres 驱动用的 postgrex ,这些当然不是重点了,真正想记录的是以下两点:

  1. [Elixir ORM #1] Repo & Model 的 DSL 设计
  2. [Elixir ORM #2] `~>` 和 `<~` 的实现

待续