lorenzomar

lorenzomar

Hi folks,

I have a sample application with User and Comments schemas with a relation 1:m between them.

I would like to add a 1:1 relation on User schema to retrieve last approved comment.

Do you know if there is some sort of preload_order option in has_one as we have in has_many relation?

Thanks in advance!!

Showing Posts 1 to 7

victorbjorklund

victorbjorklund

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]
lorenzomar

lorenzomar OP

Sorry what I mean was to have both where and order_by together when the association is defined.

Something like:

schema "users" do
    ....

    has_many :comments, Comment
    has_one :latest_comment, Comment, where: [status: :approved], preload_order: [desc: :approved_at]
  end

In has_many exists preload_order option (Ecto.Schema — Ecto v3.14.0) that can be used to sort the related records

jerdew

jerdew

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.

victorbjorklund

victorbjorklund

Aha, I see. No that doesn’t seem possible. You could use my solution (even if it less clean) or a virtual field like @jerdew mentions.

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

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.

saveman71

saveman71

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?

I opened a proposal PR for an example implementation, which worked for our use case : feat: add preload_order for has_one by saveman71 · Pull Request #4505 · elixir-ecto/ecto · GitHub

While I agree that if you need this feature, your schema is probably flawed, but as it was basically free to implement I hope to see it land in main.

fteschke

fteschke

From what I see in the Ecto debug log output, the standard preload query does not include a limit 1.
My guess: Because it is used both to preload for a single item and to preload for a list of items:

Repo.preload(comment, :latest_comment)
Repo.preload([comment_1, comment_2], :latest_comment)

For this reason, I think this suggestion is broken, as it selects only 1 comment overall, instead of one comment per user:

(Maybe it used to work, but the Ecto internals have since changed?)

Instead of a limit 1 it should be distinct on user_id:

latest_comments_query = from c in Comments, where: c.approved == true, order_by: [desc: c.inserted_at], distinct: c.user_id
— All posts loaded —

Where Next? Top

Trending in Questions Top

katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
achenet
Hello, I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind. However, when I launch mix phx.server, I get an error...
New
peck
Hi everyone. My team and I have been working on a fairly modest app based around video streaming and chat, but we’ve landed a customer t...
New
bradley
I really like the adapter patterns that ecto, nebulex, waffle, etc. use and would love find something similar for a key management servic...
New
unaware8150
Hello folks! So at work, we are seeing some situations where we have to define some “fixed” strings that are used across the codebase in...
New
Cxx-mlr
I’m working on a small exercise involving update_in/3, and I came up with this solution: data = %{ name: "Periodic Table", category:...
New
Alvinkariuki
How Can I Optimise Compile Time Dependencies I have been building an elixir application for about 2 years now. Many modules and files ha...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
juhalehtonen
There has been a thread to discuss the Stack Overflow Developer Survey on this forum every year since 2018, so here’s yet another one for...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews