Yes, you can just do it the same way as for has_many.
So for a schema like this:
schema "users" do
field :email, :string
field :password, :string, virtual: true, redact: true
field :hashed_password, :string, redact: true
field :confirmed_at, :naive_datetime
has_many :comments, Comment
has_one :latest_comment, Comment
timestamps(type: :utc_datetime)
end
You can achieve it like this:
latest_comments_query = from c in Comments, where: c.approved == true, order_by: [desc: c.inserted_at], limit: 1
Repo.all from u in User, preload: [latest_comment: ^latest_comments_query]
That sort of dynamic relationship isn’t how has_one is meant to work. You have to manually control a has_one such as: When you add a new comment, you would unset the current latest_comment and then set the new entry as the latest_comment.
What you’re thinking would take the shape of a virtual: true field, and you’d define a query like above. When you load the struct, you’d then run a separate query for the latest_comment, and populate the virtual field.
Hey @lorenzomar the general challenge with this is when you are dealing with a collection of users and you want to get the most recent approved comment for each of them. This is doable with a lateral join, but at least last I looked Ecto won’t do this for you.
What will happen in the case of a has_one with a preload_order is that it will load ALL comments and then get the first one in memory. This is going to be a foot gun when you get more records.
Hi @benwilson512, I’m not sure why would a (theoretical at the moment) preload_order implementation load all the records in memory, since it already has a limit 1 in the select, did I miss something?