
ãšã³ããªãŒ
ããŒã¿ããŒã¹ãšã®éä¿¡çšã«Elixir DSLã§æžãããEctoã Ectoã¯ORMã§ã¯ãããŸããã ãªãã§ïŒ ã¯ããElixirã¯ãªããžã§ã¯ãæåèšèªã§ã¯ãªããEctoã¯ãªããžã§ã¯ããªã¬ãŒã·ã§ãã«ãããã³ã°ïŒãªããžã§ã¯ããªã¬ãŒã·ã§ãã«ãããã³ã°ïŒã«ã§ããªãããã§ãã Ectoã¯ãç§»è¡ã®äœæãã¢ãã«ïŒã¹ããŒã ïŒã®å®£èšãããŒã¿ã®è¿œå ãšæŽæ°ãããã³ããããžã®èŠæ±ã®éä¿¡ãå¯èœã«ããããã€ãã®å€§ããªã¢ãžã¥ãŒã«ã§æ§æãããããŒã¿ããŒã¹ã®æœè±¡åã§ãã
Railsã«ç²ŸéããŠããå Žåã¯ããã¡ãããæãè¿ã顿šã¯ãã®ORM ActiveRecordã§ãã ãããããããã®2ã€ã®ã·ã¹ãã ã¯çžäºã®ã³ããŒã§ã¯ãªããããåºæ¬çãªèšèªã§äœ¿çšããã®ã«é©ããŠããŸãã çŸåšãEcto 2ã®çŸåšã®ããŒãžã§ã³ã¯ãPostgreSQLããã³MySQLãšäºææ§ããããŸãã 以åã®ããŒãžã§ã³ã¯ãMSSQLãSQLite3ãMongoDBãšããã«äºææ§ããããŸãã 䜿çšãããDBMSã«é¢ä¿ãªããEcto颿°ã®åœ¢åŒã¯åžžã«åãã§ãã Ectoã¯ãPhoenixã§ããã«äœ¿çšã§ããæšæºçãªãœãªã¥ãŒã·ã§ã³ã§ãã
ãã³ãã¬ãããå±éããããšã«æ±ºããå Žåã¯ããã®ãªããžããªã®éçºã«åå ããããšãæè¿ããŸãhttps://github.com/wunsh/ecto-book-ru
ã€ãããŒã·ã§ã³Ecto 2.X
æŽæ°ãããEcto.Changesetã¢ãžã¥ãŒã«
- changeset.modelã¯changeset.dataã«ååã倿ŽãããŸããïŒä»¥éãEctoã«ã¯ãã¢ãã«ãã¯ãããŸããïŒã
cast/4
ãžã®å¿
é ãã£ãŒã«ããšãªãã·ã§ã³ã®è»¢éã¯å»æ¢ããããšèŠãªãããŸã;以éã cast/3
ããã³validate_required/3
ã䜿çšããå¿
èŠããããŸãã- Atom
:empty
cast(source, :empty, required, optional)
:empty
cast(source, :empty, required, optional)
æšå¥šãããªããªã£ãããã empty map
ã䜿çšãããã代ããã«:invalid
ã«ããããšããå§ãããŸãã
ãã®çµæã代ããã«ïŒ
def changeset(user, params \\ :empty) do user |> cast(params, [:name], [:age]) end
次ã®ããã«æ¹åããããšããå§ãããŸãã
def changeset(user, params \\ %{}) do user |> cast(params, [:name, :age]) |> validate_required([:name]) end
Ecto.Queryã¢ãžã¥ãŒã«ã®æ°ãããµãã¯ãšãª/ 1æ©èœ
Ecto.Query.subquery/1
颿°ã䜿çšãããšãã¯ãšãªããµãã¯ãšãªã«å€æã§ããŸãã ããšãã°ãåºçç©ã®å¹³åé²èЧæ°ãèšç®ããå Žåãæ¬¡ã®ããã«èšè¿°ã§ããŸãã
query = from p in Post, select: avg(p.visits) TestRepo.all(query) #=> [#Decimal<1743>]
ãã ããæã人æ°ã®ãã10ä»¶ã®æçš¿ã®ã¿ã®å¹³åãã¥ãŒæ°ãèšç®ããå Žåã¯ããµãã¯ãšãªãå¿
èŠã«ãªããŸãã
query = from p in Post, select: [:visits], order_by: [desc: :visits], limit: 10 TestRepo.all(from p in subquery(query), select: avg(p.visits)) #=> [#Decimal<4682>]
å®éã®äŸãšããŠã Repo.aggregate
颿°ã䜿çšããŠéèšããŒã¿ãèšç®ããå ŽåïŒ
# - TestRepo.aggregate(Post, :avg, :visits) #=> #Decimal<1743>
# - 10 query = from Post, order_by: [desc: :visits], limit: 10 TestRepo.aggregate(query, :avg, :visits) #=> #Decimal<4682>
subquery/1
ã§ã¯ããµãã¯ãšãªã®ãã£ãŒã«ãã«ååãä»ããããšãã§ããŸãã ç«¶åããååãæã€ããŒãã«ãåŠçã§ãããã®ïŒ
posts_with_private = from p in Post, select: %{title: p.title, public: not p.private} from p in subquery(posts_with_private), where: p.public, select: p
Ecto.Repoã¢ãžã¥ãŒã«ã®æ°ããinsert_all / 3颿°
Ecto.Repo.insert_all/3
颿°ã¯ãåäžã®èŠæ±å
ã«ã¬ã³ãŒããè€æ°æ¿å
¥ããããšãç®çãšããŠããŸãã
Ecto.Repo.insert_all Post, [%{title: "foo"}, %{title: "bar"}]
insert_all/3
ãä»ããŠè¡ãæ¿å
¥ããå Žåã inserted_at
ãupdated_at
ãªã©ã®èªåçæãã£ãŒã«ãã¯åŠçãupdated_at
ãªãããšãèæ
®ãã䟡å€updated_at
ãããŸãã ãŸãã insert_all/3
ãããšãããŒãã«åãæå®ããã ãã§Ecto.Schema
ãã€ãã¹ããŠãããŒã¿ããŒã¹ã«è¡ãæ¿å
¥ã§ããŸãã
Ecto.Repo.insert_all "some_table", [%{hello: "foo"}, %{hello: "bar"}]
å€å¯Ÿå€ã®é¢é£ä»ãã远å
many_to_many
ã¯many_to_many
ã¢ãœã·ãšãŒã·ã§ã³ããµããŒãããmany_to_many
ã«ãªããŸããïŒ
defmodule Post do use Ecto.Schema schema "posts" do many_to_many :tags, Tag, join_through: "posts_tags" end end
join_through
ãªãã·ã§ã³ã®å€ã¯ã join_through
tag_id
ãštag_id
ãå«ãããŒãã«ã®ååããŸãã¯å€éšããŒãšèªåçæåãå«ãPostTag
ãªã©ã®ã¹ããŒãã§ãã
é¢é£ä»ãã®æ¹åãããäœæ¥
belongs_to
ã many_to_many
ã¯changeset
ä»ããŠã belongs_to
ããã³many_to_many
é¢é£ä»ãã«è¡ãæ¿å
¥ããã³å€æŽã§ããããã«ããŸãã ããã«ãEctoã¯ãæ¿å
¥ã®ããã«ããŒã¿æ§é å
ã§çŽæ¥é¢é£ä»ãã®å®çŸ©ããµããŒãããŸãã äŸïŒ
Repo.insert! %Permalink{ url: "//root", post: %Post{ title: "A permalink belongs to a post which we are inserting", comments: [ %Comment{text: "child 1"}, %Comment{text: "child 2"}, ] } }
ãã®æ¹åã«ãããããªãŒæ§é ãããŒã¿ããŒã¹ã«æ¿å
¥ãããããªããŸããã
Ectoã¢ãžã¥ãŒã«ã®æ°ããassoc / 2ããªããŒãæ©èœ
Ecto.assoc/2
颿°ã䜿çšãããšãã¬ã³ãŒããéžæããããã«ããŒãããå¿
èŠããã2次ã®é¢ä¿ãå®çŸ©ã§ããŸãã äŸãšããŠãéžæããåºçç©ã®èè
ãšã³ã¡ã³ããååŸã§ããŸãã
posts = Repo.all from p in Post, where: is_nil(p.published_at) Repo.all assoc(posts, [:comments, :author])
éžæã¹ããŒã ã«ãã£ãŒã«ãã远å ããå¿
èŠããªããããé¢é£ä»ããä»ããŠãªã³ã¯ãããŒãããããšããå§ãããŸãã
ã¢ãããµãŒã
颿°Ecto.Repo.insert/2
ããã³Ecto.Repo.insert_all/3
ã¯ããªãã·ã§ã³:on_conflict
ããã³:conflict_target
䜿çšããŠã¢ãããµãŒãïŒæ¿å
¥ããã³æŽæ°ïŒããµããŒããå§ããŸããã
ãªãã·ã§ã³:on_conflict
ã¯ãäž»ããŒãäžèŽããå Žåã®ããŒã¿ããŒã¹ã®åäœã決å®ããŸãã
ãªãã·ã§ã³:conflict_target
ãæ°ããè¡ãæ¿å
¥ãããšãã«ç«¶åããã§ãã¯ããããã«äœ¿çšãããã£ãŒã«ããæ±ºå®ããŸãã
# {:ok, inserted} = MyRepo.insert(%Post{title: "inserted"}) # . {:ok, upserted} = MyRepo.insert(%Post{id: inserted.id, title: "updated"}, on_conflict: :nothing) # , title. on_conflict = [set: [title: "updated"]] {:ok, updated} = MyRepo.insert(%Post{id: inserted.id, title: "updated"}, on_conflict: on_conflict, conflict_target: :id)
æ°ããor_whereããã³or_havingãã§ããæ¡ä»¶
Ecto.Query.or_where/3
ã¯ãåŒã Ecto.Query.or_where/3
ããã³ã Ecto.Query.or_having
ãã远å ããŸããããããã¯ããORããéããŠæ¢åã®æ¡ä»¶ã«æ°ãããã£ã«ã¿ãŒã远å ããŸãã
from(c in City, where: [state: "Sweden"], or_where: [state: "Brazil"])
ã¹ãããããšã«ã¯ãšãªãäœæããæ©èœã远å ããŸãã
ãã®ææ³ã䜿çšãããšãåŒãå°ããã€äœæããŠãäžè¬çãªã¯ãšãªã«åŸã§è£éããããšãã§ããŸãã
ããšãã°ãã¯ãšãªãäœæããæ¡ä»¶ã®ã»ããããããŸãããã³ã³ããã¹ãã«å¿ããŠãã®ãã¡ã®ããã€ããéžæããã ãã§æžã¿ãŸãã
dynamic = false dynamic = if params["is_public"] do dynamic([p], p.is_public or ^dynamic) else dynamic end dynamic = if params["allow_reviewers"] do dynamic([p, a], a.reviewer == true or ^dynamic) else dynamic end from query, where: ^dynamic
äžèšã®äŸã¯ãå€éšæ¡ä»¶ãèæ
®ããŠã段éçã«ã¯ãšãªãäœæããæåŸã«1ã€ã®ã¯ãšãªå
ã®ãã¹ãŠãè£éããæ¹æ³ã瀺ããŠããŸãã
åçåŒã¯ãå¥ã®åçåŒå
where
ãŸãã¯where
ã having
ã update
ãŸãã¯join
on
having
å
where
åžžã«è£éã§ããŸãã
ã¯ãšãªã€ã³ã¿ãŒãã§ãŒã¹
HexDocsã®Ecto.Queryããã³Ecto.Repoã¢ãžã¥ãŒã«ã®ããã¥ã¡ã³ãã®åæã¯ãRusRailsGuide ã®ActiveRecord Query Interfaceã®æ¹æ³ã§è¡ãããŸãã 以äžã®ããã¹ãã¯ãEctoã䜿çšããŠããŒã¿ããŒã¹ããããŒã¿ãååŸããããŸããŸãªæ¹æ³ãé瀺ããŠããŸãã 以äžã®ããã¹ãã®åŸåã®ã³ãŒãäŸã¯ããããã®ã¢ãã«ã®äžéšã«é©çšãããŸãã
id , . defmodule Showcase.Client do use Ecto.Schema import Ecto.Query schema "clients" do field :name, :string field :age, :integer, default: 0 field :sex, :integer, default: 0 field :state, :string, default: "new" has_many :orders, Showcase.Order end # ... end defmodule Showcase.Order do use Ecto.Schema import Ecto.Query schema "orders" do field :description, :text field :total_cost, :integer field :state, :string, default: "pending" belongs_to :client, Showcase.Client has_many :products, Showcase.Product end # ... end
Ectoã¯ãããŒã¿ããŒã¹ãããªããžã§ã¯ããååŸããããã®ããã€ãã®æ€çŽ¢æ©èœãæäŸããŸãã åŒæ°ãåæ€çŽ¢é¢æ°ã«æž¡ããŠãçŽç²ãªSQLãèšè¿°ããããšãªãããŒã¿ããŒã¹ã«ç¹å®ã®ã¯ãšãªãå®è¡ã§ããŸãã Ectoã¯ãããŒã¯ãŒãã®äœ¿çšãšåŒïŒé¢æ°/ãã¯ãïŒã䜿çšãã2ã€ã®ã¹ã¿ã€ã«ã®ã¯ãšãªäœæãæäŸããŸãã
以äžã¯EctoãæäŸããåŒã®äžéšã§ã2ã€ã®ã¢ãžã¥ãŒã«ã§å®£èšãããŠããŸãïŒ
Ecto.RepoïŒ
- ååŸ/ 3
- get_by / 3
- 1/2
- ãã¹ãŠ/ 3
Ecto.QueryïŒ
- ã©ã/ 3
- or_where / 3
- order_by / 3
- éžæ/ 3
- group_by / 3
- å¶é/ 3
- ãªãã»ãã/ 3
- åå ãã/ 5
- é€å€/ 2
1.åäžã®è¡ãååŸãã
ååŸ/ 3
get/3
颿°ã䜿çšãããšãç¹å®ã®äž»ããŒã«å¯Ÿå¿ããã¬ã³ãŒããååŸã§ããŸãã äŸïŒ
# (id) 10. client = Ecto.Repo.get(Client, 10) => %Client{id: 10, name: "Cain Ramirez", age: 34, sex: 1, state: "good"}
ã¬ã³ãŒããèŠã€ãããªãå Žåã get/3
颿°ã¯nil
ãè¿ããŸãã èŠæ±ã«primary key
ããªãå ŽåããŸãã¯primary key
ãè€æ°ããå Žåã颿°ã¯argument error
ãåŒãèµ·ããargument error
ã
get!/3
颿°ã¯get/3
ãšåãããget/3
åäœããŸãããäžèŽãããšã³ããªãèŠã€ãããªãå ŽåEcto.NoResultsError
ãEcto.NoResultsError
ãŸãã
ãã®é¢æ°ã®ActiveRecordã«æãè¿ããã®ã¯ã find
ã¡ãœããã§ãã
get_by / 3
get_by/3
颿°ã䜿çšãããšãæäŸãããéžææ¡ä»¶ã«äžèŽããã¬ã³ãŒããååŸã§ããŸãã äŸïŒ
# (name) "Cain Ramirez". client = Ecto.Repo.get_by(Client, name: "Cain Ramirez") => %Client{id: 10, name: "Cain Ramirez", age: 34, sex: 1, state: "good"}
ã¬ã³ãŒããèŠã€ãããªãå Žåã get_by/3
颿°get_by/3
nil
get_by/3
è¿ããŸãã
get_by!/3
颿°ã¯get_by/3
ããã«åäœããŸãããäžèŽããã¬ã³ãŒããèŠã€ãããªãå ŽåEcto.NoResultsError
ãEcto.NoResultsError
ãŸãã
ãã®é¢æ°ã®ActiveRecordã«æãè¿ããã®ã¯ã find_by_*
ã¡ãœããã§ãã
1/2
1/2颿°ã䜿çšãããšãæäŸãããéžææ¡ä»¶ã«äžèŽãã1ã€ã®ã¬ã³ãŒããååŸã§ããŸãã äŸïŒ
# . query = Ecto.Query.from(c in Client, where: c.name == "Jean Rousey") client = Ecto.Repo.one(query) => %Client{id: 1, name: "Jean Rousey", age: 29, sex: -1, state: "good"}
nil
颿°ã¯ãã¬ã³ãŒããèŠã€ãããªãå Žåã¯nil
ãè¿ããŸãã ãŸããèŠæ±æã«è€æ°ã®ã¬ã³ãŒããèŠã€ãã£ãå Žåã颿°ã¯ãšã©ãŒãã¹ããŒããŸãã
one!/2
颿°ã¯one/2
ããã«åäœããŸãããäžèŽãããšã³ããªãèŠã€ãããªãå ŽåEcto.NoResultsError
ãEcto.NoResultsError
ãŸãã
ActiveRecord
ã®ãã®é¢æ°ã«æãè¿ããã®ã¯ãçŸåšã®å¶éã§ã¯ãªããéžææ¡ä»¶ãåãå
¥ãã<4ããŒãžã§ã³ã®first
ã¡ãœããã§ãã
2.è€æ°ã®è¡ãååŸãã
ãã¹ãŠ/ 3
all/3
颿°ã䜿çšãããšãæå®ãããã¯ãšãªæ¡ä»¶ã«äžèŽãããã¹ãŠã®ã¬ã³ãŒããååŸã§ããŸãã äŸïŒ
# . query = Ecto.Query.from(c in Client) clients = Ecto.Repo.all(query) => [%Client{id: 1, name: "Jean Rousey", age: 29, sex: -1, state: "good"}, ..., %Client{id: 10, name: "Cain Ramirez", age: 34, sex: 1, state: "good"}]
all/3
颿°ã¯ããªã¯ãšã¹ããæ€èšŒã«å€±æããå Žåã«Ecto.QueryError
ãè¿ããŸãã
ãã®é¢æ°ã®ActiveRecordã«æãè¿ããã®ã¯ãéžææ¡ä»¶ãåãå
¥ããããŒãžã§ã³<4ã®all
ã¡ãœããã§ãã
3.è¡éžææ¡ä»¶
ã©ã/ 3
where/3
åŒã䜿çšãããšãSQLåŒã®WHEREéšåã衚ãè¿ãããã¬ã³ãŒããå¶éããããã®æ¡ä»¶ãå®çŸ©ã§ããŸãã è€æ°ã®éžææ¡ä»¶ãéä¿¡ãããå Žåããããã¯AND
æŒç®åã«ãã£ãŠçµåãããŸãã
where:
ããŒã¯ãŒãã®åŒã³åºãã¯ã from/2
ãã¯ãã®äžå¯æ¬ ãªéšåã§ãã
from(c in Client, where: c.name == "Cain Ramirez") from(c in Client, where: [name: "Cain Ramirez"])
ãµã³ããªã³ã°æ¡ä»¶ã䜿çšããŠãªã¹ããè£éããããšãã§ããŸããããã«ãããå¿
èŠãªå¶éãäºåã«åéã§ããŸãã
filters = [name: "Cain Ramirez"] from(c in Client, where: ^filters)
ãã¯ãåŒã³åºã// where/3
ïŒ
Client |> where([c], c.name == "Cain Ramirez") Client |> where(name: "Cain Ramirez")
or_where / 3
or_where/3
åŒã䜿çšãããšãSQLåŒã®WHEREéšåã衚ãè¿ãããã¬ã³ãŒããå¶éããããã®ããæè»ãªæ¡ä»¶ãå®çŸ©ã§ããŸãã where/3
ãšor_where/3
ã®éãor_where/3
æå°éã§ãããåºæ¬çã§ãã 転éãããæ¡ä»¶ã¯ã OR
æŒç®åãä»ããŠæ¢åã®æ¡ä»¶ã«è¿œå ãããŸãã è€æ°ã®éžææ¡ä»¶ãor_where/3
ããå Žåããããã¯AND
æŒç®åã«ãã£ãŠäºãã«çµåãããŸãã
or_where:
ããŒã¯ãŒãã䜿çšããåŒã³åºãã¯ã from/2
ãã¯ãã®äžå¯æ¬ ãªéšåã§ãã
from(c in Client, where: [name: "Cain Ramirez"], or_where: [name: "Jean Rousey"])
ãµã³ããªã³ã°æ¡ä»¶ã䜿çšããŠãªã¹ããè£éããããšãã§ããŸããããã«ãããå¿
èŠãªå¶éãäºåã«åéã§ããŸãã ãªã¹ãå
ã®æ¡ä»¶ã¯AND
ãä»ããŠçžäºæ¥ç¶ããã OR
ä»ããŠæ¢åã®æ¡ä»¶ãçµåããŸãã
filters = [sex: 1, state: "good"] from(c in Client, where: [name: "Cain Ramirez"], or_where: ^filters)
...ãã®åŒã¯æ¬¡ãšåçã§ãïŒ
from c in Client, where: (c.name == "Cain Ramirez") or (c.sex == 1 and c.state == "good")
or_where/3
åŒã³åºãor_where/3
ïŒ
Client |> where([c], c.name == "Jean Rousey") |> or_where([c], c.name == "Cain Ramirez")
4.æååãœãŒã
åŒorder_by/3
䜿çšãããšãããŒã¿ããŒã¹ããåä¿¡ããã¬ã³ãŒãã®ãœãŒãæ¡ä»¶ãå®çŸ©ã§ããŸãã order_by/3
ã¯ãSQLã¯ãšãªã®ORDER BY
éšåãæå®ããŸãã
äžåºŠã«è€æ°ã®ãã£ãŒã«ãã§äžŠã¹æ¿ããããšãã§ããŸãã ããã©ã«ãã®ãœãŒãæ¹åã¯æé ïŒ :asc
ïŒã§ãããéé ïŒ :desc
ïŒã§åå®çŸ©ã§ããŸãã ãã£ãŒã«ãããšã«ãç¬èªã®ãœãŒãæ¹åãèšå®ã§ããŸãã
ããŒã¯ãŒãåŒã³åºãorder_by:
ã¯ã from/2
ãã¯ãã®äžå¯æ¬ ãªéšåã§ãã
from(c in Client, order_by: c.name, order_by: c.age) from(c in Client, order_by: [c.name, c.age]) from(c in Client, order_by: [asc: c.name, desc: c.age]) from(c in Client, order_by: [:name, :age]) from(c in Client, order_by: [asc: :name, desc: :age])
ãœãŒããã£ãŒã«ãã䜿çšããŠãªã¹ããè£éããããšãã§ããŸããããã«ãããå¿
èŠãªéžææ¡ä»¶ãäºåã«åéã§ããŸãã
values = [asc: :name, desc: :age] from(c in Client, order_by: ^values)
ãã¯ãåŒã³åºãorder_by/3
ïŒ
Client |> order_by([c], asc: c.name, desc: c.age) Client |> order_by(asc: :name)
5.ç¹å®ã®è¡ãã£ãŒã«ããéžæããŸã
select/3
åŒã䜿çšãããšãããŒã¿ããŒã¹ããã¬ã³ãŒããååŸããããã«è¿ãããŒãã«ã®ãã£ãŒã«ããå®çŸ©ã§ããŸãã select/3
ã¯ãSQLã¯ãšãªã®SELECT
éšåãèšå®ããŸãã ããã©ã«ãã§ã¯ã Ecto
select *
ã䜿çšselect *
çµæãã£ãŒã«ãã®ã»ããå
šäœãselect *
ãselect *
ã
select:
ããŒã¯ãŒãã®åŒã³åºãã¯ã from/2
ãã¯ãã®äžå¯æ¬ ãªéšåã§ãã
from(c in Client, select: c) from(c in Client, select: {c.name, c.age}) from(c in Client, select: [c.name, c.state]) from(c in Client, select: {c.name, ^to_string(40 + 2), 43}) from(c in Client, select: %{name: c.name, order_counts: 42})
select/3
ãselect/3
ãã¯ãåŒã³åºãïŒ
Client |> select([c], c) Client |> select([c], {c.name, c.age}) Client |> select([c], %{"name" => c.name}) Client |> select([:name]) Client |> select([c], struct(c, [:name])) Client |> select([c], map(c, [:name]))
éèŠïŒé¢é£ä»ãã®éžæãã£ãŒã«ããå¶éããå Žåãå€éšãªã³ã¯ããŒãéžæããããšãéèŠã§ããããããªããšã Ecto
ã¯é¢é£ãªããžã§ã¯ããèŠã€ããããšãã§ããŸããã
6.æååã®ã°ã«ãŒãå
SQLã¯ãšãªã§GROUP BY
ãå®çŸ©ããã«ã¯ã group_by/3
ãã¯ããgroup_by/3
ãŸãã SELECT
èšèŒãããŠãããã¹ãŠã®åã¯ã group_by/3
æž¡ãå¿
èŠããããŸãã ããã¯ãéèšé¢æ°ã®äžè¬çãªã«ãŒã«ã§ãã
ããŒã¯ãŒãgroup_by:
åŒã³åºãã¯ã from/2
ãã¯ãã®äžå¯æ¬ ãªéšåã§ãã
from(c in Client, group_by: c.age, select: {c.age, count(c.id)}) from(c in Client, group_by: :sex, select: {c.sex, count(c.id)})
group_by/3
åŒã³åºãïŒ
Client |> group_by([c], c.age) |> select([c], count(c.id))
7.éžæããè¡ã®å¶éãšã·ãã
SQLã¯ãšãªã®LIMITãæ±ºå®ããã«ã¯ãåŒlimit/3
䜿çšããŠãåä¿¡ãããå¿
èŠãªã¬ã³ãŒãã®æ°ã決å®ããŸãã
limit/3
2åæž¡ããããšãæåã®å€ã2çªç®ã®å€ã«ãã£ãŠãªãŒããŒã©ã€ããããŸãã
from(c in Client, where: c.age == 29, limit: 1) Client |> where([c], c.age == 29) |> limit(1)
SQLã¯ãšãªã§OFFSETãæ±ºå®ããã«ã¯ã offset/3
åŒã䜿çšããŸããããã¯ãè¿ãããã¬ã³ãŒããéå§ãããåã«ã¹ããããããã¬ã³ãŒãã®æ°ã決å®ããŸãã
offset/3
2åæž¡ããããšãæåã®å€ã¯2çªç®ã®å€ã«ãã£ãŠãªãŒããŒã©ã€ããããŸãã
from(c in Client, limit: 10, offset: 30) Client |> limit(10) |> offset(30)
8.ããŒãã«ãçµåãã
ã¯ãšãªã¯å€ãã®å Žåãããã€ãã®ããŒãã«ãåç
§ããŸã;ãã®ãããªã¯ãšãªã¯JOIN
æ§é ã䜿çšããŠæ§ç¯ãããŸãã Ectoã§ã¯ã join/5
ãã®ãããªæ§é ãå®çŸ©ããããšãç®çãšããŠããŸãã ããã©ã«ãã§ã¯ãããŒãã«çµåæŠç¥ã¯INNER JOINã§ããã :inner
ãïŒleft ã: right ã: :cross
ãŸãã¯:full
åå®çŸ©ã§ããŸãã ããŒã«ãã£ãŠã¯ãšãªãæ§ç¯ããå Žå:inner_join
:join
ã¯ã:: :inner_join
:left_join
:right_join
:cross_join
ãŸãã¯:full_join
眮ãæããããšãã§ããŸãã
join:
ããŒã¯ãŒãåŒã³åºãã¯ã from/2
ãã¯ãã®äžå¯æ¬ ãªéšåã§ãã
from c in Comment, join: p in Post, on: p.id == c.post_id, select: {p.title, c.text} from p in Post, left_join: c in assoc(p, :comments), select: {p, c} from c in Comment, join: p in Post, on: [id: c.post_id], select: {p.title, c.text}
on
æž¡ããããã¹ãŠã®ããŒã¯æ¥ç¶æ¡ä»¶ãšèŠãªãããŸãã
in
ã«é¢ããŠå³åŽãè£éããããšãå¯èœã§ãã äŸïŒ
posts = Post from c in Comment, join: p in ^posts, on: [id: c.post_id], select: {p.title, c.text}
ãã¯ãåŒã³åºãjoin/5
ïŒ
Comment |> join(:inner, [c], p in Post, c.post_id == p.id) |> select([c, p], {p.title, c.text}) Post |> join(:left, [p], c in assoc(p, :comments)) |> select([p, c], {p, c}) Post |> join(:left, [p], c in Comment, c.post_id == p.id and c.is_visible == true) |> select([p, c], {p, c})
9.ãªãŒããŒã©ã€ãæ¡ä»¶
Ectoã䜿çšãããšããªã¯ãšã¹ãã§æ¢ã«å®çŸ©ãããŠããæ¡ä»¶ãåé€ããããããã©ã«ãå€ãè¿ãããšãã§ããŸããããã«ã¯exclude/2
åŒã䜿çšããŸãã
query |> Ecto.Query.exclude(:select) Ecto.Query.exclude(query, :select)
äžæ¬è¡æäœã®ã³ãã³ã
äžæ¬æ¿å
¥
Ecto.Repo.insert_all/3
颿°ã¯ã転éããããã¹ãŠã®ã¬ã³ãŒããæ¿å
¥ããŸãã
Repo.insert_all(Client, [[name: "Cain Ramirez", age: 34], [name: "Jean Rousey", age: 29]]) Repo.insert_all(Client, [%{name: "Cain Ramirez", age: 34}, %{name: "Jean Rousey", age: 29}])
insert_all/3
颿°ã¯ã inserted_at
ãupdated_at
ãªã©ã®èªåinsert_all/3
ãã£ãŒã«ããåŠçããŸããã
äžæ¬æŽæ°
Ecto.Repo.update_all/3
颿°Ecto.Repo.update_all/3
ãæž¡ããããã£ãŒã«ãå€ã®ã¯ãšãªæ¡ä»¶ã«è©²åœãããã¹ãŠã®è¡ãæŽæ°ããŸãã
Repo.update_all(Client, set: [state: "new"]) Repo.update_all(Client, inc: [age: 1]) from(c in Client, where: p.sex < 0) |> Repo.update_all(set: [state: "new"]) from(c in Client, where: p.sex > 0, update: [set: [state: "new"]]) |> Repo.update_all([]) from(c in Client, where: c.id < 10, update: [set: [state: fragment("?", new)]]) |> Repo.update_all([])
äžæ¬åé€
Ecto.Repo.delete_all/2
颿°ã¯ãã¯ãšãªæ¡ä»¶ã«è©²åœãããã¹ãŠã®è¡ãåé€ããŸãã
Repo.delete_all(Client) from(p in Client, where: p.age == 0) |> Repo.delete_all
å®çšäŸ
ãªã¯ãšã¹ãã®æ§æ
query = from p in App.Product, select: p query2 = from p in query, where: p.state == "published" App.Repo.all(query2)
ããŒãžããŒã·ã§ã³æ©èœ
defmodule Finders.Common.Paging do import Ecto.Query def page(query), do: page(query, 1) def page(query, page), do: page(query, page, 10) def page(query, page, per_page) do offset = per_page * (page-1) query |> offset([_], ^offset) |> limit([_], ^per_page) end end
# With Posts: second page, five per page posts = Post |> Finders.Common.Paging.page(2, 5) |> Repo.all # With Tags: third page, 10 per page tags = Tag |> Finders.Common.Paging.page(3) |> Repo.all
Query.API
æ¯èŒæŒç®åïŒ ==
!=
ã <=
ã >=
ã <
ã >
ããŒã«æŒç®åïŒ and
ã or
ã not
ã¹ã€ãããªãã¬ãŒã¿ãŒïŒ in/2
æ€çŽ¢æ©èœïŒ like/2
ããã³ilike/2
ãã«ãã§ãã¯ïŒ is_nil/1
ã¢ã°ãªã²ãŒã¿ãŒïŒ count/1
ã avg/1
ã sum/1
ã min/1
ã max/1
ä»»æã®SQLãµãã¯ãšãªã®é¢æ°ïŒ fragment/1
from p in Post, where: p.published_at > ago(3, "month") from p in Post, where: p.id in [1, 2, 3] from p in Payment, select: avg(p.value) from p in Post, where: p.published_at > datetime_add(^Ecto.DateTime.utc, -1, "month") from p in Post, where: is_nil(p.published_at) from p in Post, where: ilike(p.body, "Chapter%") from p in Post, where: is_nil(p.published_at) and fragment("lower(?)", p.title) == "title"
転éããããªããžããªå
ã§ä»»æã®SQLã¯ãšãªãå®è¡ããŸãã
Ecto.Adapters.SQL.query(Showcase, "SELECT $1::integer + $2", [40, 2]) => {:ok, %{rows: [{42}], num_rows: 1}}
ãã®é¢æ°ã®ActiveRecordã«æãè¿ããã®ã¯ã find_by_sql
ã¡ãœããã§ãã
åŒããæ§ç¯ãããã¯ãšãªãSQLã«å€æããŸãã
Ecto.Adapters.SQL.to_sql(:all, repo, Showcase.Client) => {"SELECT c.id, c.name, c.age, c.sex, c.state, c.inserted_at, c.created_at FROM clients as c", []} Ecto.Adapters.SQL.to_sql(:update_all, repo, from(c in Showcase.Client, update: [set: [state: ^"new"]])) => {"UPDATE clients AS c SET state = $1", ["new"]}
ãã®é¢æ°ã¯ãActiveRecord :: Relationã®åãã¡ãœããã§ãã
æåŠ
http://guides.rubyonrails.org/active_record_querying.html
https://hexdocs.pm/ecto/Ecto.html
https://github.com/elixir-ecto/ecto
https://blog.drewolson.org/composable-queries-ecto/
jbã䜿çšããåŠç¿
http://blog.plataformatec.com.br/2016/05/ectos-insert_all-and-schemaless-queries/
ããšãã
颿°åããã°ã©ãã³ã°èšèªã®Elixirã«èå³ãããå ŽåããŸãã¯åã«å
±æããŠããå Žåã¯ã Wunsh && Elixirããã³ProElixir Telegramãã£ããã«åå ããããšããå§ãããŸãã
åœå
ã®Elixirã³ãã¥ããã£ã¯ããããžã§ã¯ãWunsh.ruã«çŽé¢ããŠåäžã®ãã©ãããã©ãŒã ãšããŠç»å Žãå§ããŠããŸãã çŸåšããããžã§ã¯ãã«ã¯ããŒãã«é¢ãããã¥ãŒã¹ã¬ã¿ãŒããããéæ³ãªãã®ã¯ãããŸãããé±ã«äžåºŠããã·ã¢èªã§ãšãªãã·ã«ã«é¢ããèšäºã®éžæãå«ãæçŽãå±ããŸãã
UPDïŒ
pure_evilããã®æŽæ°-MongoDBã§ã¯ã Ectoã®2çªç®ã®ããŒãžã§ã³ãæ©èœããŸããããããŸã§ã®ãšãããã©ãŒã¯ã®åœ¢åŒã«ãªã£ãŠããŸãïŒ https : //github.com/michalmuskala/mongodb_ecto/pull/91