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

Blokh
Hey guys, I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly Do you guys have any suggestions what is the best prac...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
Onor.io
I have what I’ve heard referred to as a “lookup table” in my database. This is a way of assigning codes to common values. One common lo...
New
Trolleger
What approach to take when sending live updates to “random” users Hi! I have a question, I have a little chat app, and when I create a DM...
New
matt-savvy
Anyone here using Honeybadger? My Honeybadger account is being overwhelmed with noise from some bots. Seeing a lot of Bandit.HTTPError...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
wintermeyer
There are three potential reasons for members of this forum to have a look at https://vutuv.de You are tired or annoyed of LinkedIn. Yo...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews