在Rails中,如果我有以下设置:
class Post < ActiveRecord::Base
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :post
def self.approved
where(approved: true)
end
end
然后我可以这样做:
post = Post.find(100)
comments = post.comments.approved
快速获取给定Post
的所有已批准评论。
我怎样才能在Ecto中做类似的事情?
defmodule MyApp.Post do
use Ecto.Model
schema "posts" do
#columns omitted
has_many :comments, MyApp.Comment
end
end
defmodule MyApp.Comment do
use Ecto.Model
schema "comments" do
#columns omitted
belongs_to :post, MyApp.Post
end
end
我已预先加载post
comments
:
post = MyApp.Post
|> MyApp.Repo.get(100)
|> MyApp.Repo.preload(:comments)
我甚至不确定从approved
开始使用MyApp.Comment
范围。
答案 0 :(得分:10)
允许预加载接收查询。所以你可以像这样过滤相关的评论。
post =
MyApp.Post
|> Ecto.Query.preload(comments: ^MyApp.Comment.approved(MyApp.Comment))
|> MyApp.Repo.get(100)
并在您的Comment
模型中
def approved(query) do
from c in query,
where: c.approved == true
end
答案 1 :(得分:3)
我不认为使用当前版本的Ecto是可能的。预加载不允许过滤。另一种方法是通过查询获取评论:
(from comment in MyApp.Comment,
where: comment.post_id == ^post_id
and comment.approved == true,
select: comment) |> Repo.all
答案 2 :(得分:0)
我参加这个聚会真的很晚,但是有一些空闲时间,想出一个答案可以帮助刚接触Elixir的人们。
如果您来自Ruby / Rails,则要记住的一件事是Elixir / Erlang中的数据是无状态的,因为值是不可变的。因此,我们没有办法操纵帖子并将评论加载到数据结构中。我们可以通过两种方法达到相同的最终结果:
#1返回一个新的结构/映射,其中已合并注释
post_with_comments = %{post | comments: comments} # or Map.put(post, :comments, comments)
其中的注释类似于:
comments = MyApp.Repo.get_by(MyApp.Comment, where: post_id == ^post.id)
。
#2通过构建查询以一次全部抓取将数据预加载到发布数据结构中。我们可以通过将查询传递到查询中来做到这一点,见下文。
defmodule MyApp.Post.Query do
def approved_with_comments(id) do
get_post(id) |> with_approval(true) |> with_comments()
end
def get_post(id) do
from p in MyApp.Post, where: p.id == ^id
end
def with_approval(query, approval) do
from q in query, where: approved == ^approval
end
def with_comments(query) do
from q in query, preload: [:comments]
end
end
通常,您将希望始终预加载关联,因为它对数据库更有效。我个人喜欢Ecto中的这种行为,因为它迫使您不要用N + 1个查询将自己丢在脚下,或者使它们变得显而易见。
通过使用与模式匹配相同的函数名称,可以使上面的Query
模块之类的界面更符合人体工程学:
def query(query, :by_id, id), do: from q in query, where: q.id == ^id
def query(query, :by_approval, approval), do: # ....
然后,您将把您的参数所组成的约简映射到单个查询对象中,然后最终用Repo.one
进行加载,或者将任何适合您的幻想映射。