Ordering many-to-many relations

I have a many-to-many relation between two entities. The join table has a field called position, which is supposed to be used for ordering.

Example:

class Film < Relation[:sql]
  associations do
    has_many :actors, through: :film_actors
  end
end
class Actor < Relation[:sql]
...
end
class FilmActor < Relation[:sql]
  schema do
  ...
    attribute :position, Types::Integer
  end

  dataset do
    order(:position)
  end
end

Whenever I use films.combine(:actors) I expect actors to have an order specified by FilmActor relation. However, it doesn’t work this way. Instead, it orders by actors.id, even though it jons film_actors anyway.

I’ve tried modifying relation to has_many :actors, through: :film_actors, order: :position, but it doesn’t work either.

However, Sequel::Model supports it just fine:

many_to_many :actors, join_table: :film_actors, order: :position

So here’s the question: how do I achieve the same using rom-rb with rom-sql?

1 Like

hey @Morozzzko! So, I believe this is what you need ROM - Associations

We could probably just add support for :order option though that would automatically configure relation behind the scenes.

1 Like

Oh thank you! That is indeed exactly what I need right now. I didn’t expect to find it there. Actually skipped the part of the docs.

Yes, it would be nice to add support for :order in DSL. However, it’s still counter-intuitive that the association totally ignores what I’ve said about the default order of the relation. So I guess it could end up in two changes

Also I’ve noticed something: it relies on me defining an order on Actor instead of FilmActor. Which is exactly what I’d like to avoid.

I want to have a one-way binding: you can get from a Film to an Actor, but can’t do it the other way around. So Actor has no idea that FilmActor even exists.

And :view forces me to leak the abstraction.

Well, we’ve got what we’ve got. Hope it’ll get better in later versions. Thank you!

@Morozzzko oh relations are aware of all other relations and that’s by design. Treat the entire relation “layer” as one abstraction. That’s exactly why I recommend using repositories to hide details and establish strong boundaries :slightly_smiling_face:

Well, breaks my idea of growing functionality using modular design or whatever.

Here’s the context:

I have some “core” relations in my domain, the whole app revolves around them.

However, there are still smaller modules which may be easily disabled or not loaded at all. So I don’t want code from the features to leak into the “core”. It would be nice to remove all traces of feature by simply removing a directory and a boot file.

This thread actually demonstrates that it would have been possible if we’d had a better relation management.

I am also a little bummed because rom-sql is still powered by Sequel, which does enable me to do all of this

To add to my previous point:

I’m still fine with listing relations, as we can’t drop database constraints by simply dropping directories. So that would have to be explicit, which is good.

What I don’t like is that I have to define context-specific sorting logic inside one of the core relations

@Morozzzko yeah I see your PoV. It’s just a missing feature, the details how sorting is done can be hidden. Query logic is exposed more in rom-sql a bit on purpose though. I specifically wanted to avoid heavy abstractions on top of queries so that it’s easier to tweak, optimize and have more control in general over query generation. It’s not to say that having common use cases covered through DSL is a bad idea, as long as you have access to the underlying relations, things will be OK.

However, there are still smaller modules which may be easily disabled or not loaded at all . So I don’t want code from the features to leak into the “core”. It would be nice to remove all traces of feature by simply removing a directory and a boot file.

I’m not sure if I get it. Do you have associations defined between “core” relations and some other relations?

1 Like

I’m not sure if I get it. Do you have associations defined between “core” relations and some other relations?

Other relations reference “core” relations, yes.

Let me give another relevant example: if we’re building a cleaning service, we’ve got a “marketplace”, or an uber-like model. Basically, everything revolves around customers, orders for cleaning, and cleaners.

However, we’ve got mass-hiring, and each cleaner signs a contract. We have to store the contract and link it to the cleaner.

Other option is the ability to sort the scheduled orders or tag them in app. It’ll require us to add new relations, which are only usable in a single narrow context.

@Morozzzko the challenge here is that you want to decouple things that are coupled by nature, because they are associated. A base relation corresponds to a table, canonical associations correspond to tables too. If you have customers as a core base relation, and a bunch of other non-core relations associated, then you need to have customers with those non-core associations defined too. I’m pretty sure things would break if associations were defined only on one side.

It’s something to explore for sure though. I am very much aware of the fact that loading all-of-the-things can become a problem and it should be addressed eventually. This is something we could tackle after 6.0.0 is released.