27

在 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)

我什至不确定approvedMyApp.Comment.

4

3 回答 3

13

允许预加载接收查询。所以你可以像这样过滤相关的评论。

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
于 2015-12-17T12:06:45.197 回答
3

我认为当前版本的 Ecto 不可能。预加载不允许过滤。另一种方法是通过查询获取评论:

(from comment in MyApp.Comment, 
  where: comment.post_id == ^post_id 
    and comment.approved == true,
select: comment) |> Repo.all
于 2015-06-17T11:07:36.120 回答
3

我参加这个聚会真的迟到了,但有一些空闲时间,我想一个答案可以帮助刚接触 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 通过构建一个查询来一次将数据预加载到 post 数据结构中。我们可以通过将查询传递给查询来做到这一点,见下文。

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: # ....

然后,您将 reduce 映射到您的任何参数到单个查询对象中,然后您最终加载该对象Repo.one或任何适合您喜欢的对象。

于 2018-08-03T01:09:48.287 回答