Home > OS >  How to find instances of objects that share a common relationship to another object?
How to find instances of objects that share a common relationship to another object?

Time:11-18

My question can be illustrated as follows:

I've got three models, User, Genre and Concert.

Instances of both User and Concert may be associated with any number of genres, so they have join tables with Genre, with has_and_belongs_to_many relationships.

My goal is to implement a filter that'll take an instance of User and return every instance of Concert that is associated to one or more genres in common with said User.

My code:

# concert.rb:

class Concert < ApplicationRecord
  has_and_belongs_to_many :genres
end

# user.rb:

class User < ApplicationRecord
  has_and_belongs_to_many :genres
end

# schema.rb:

create_table "genres_concerts", id: false, force: :cascade do |t|
  t.bigint "genre_id", null: false
  t.bigint "concert_id", null: false
end

create_table "genres_users", id: false, force: :cascade do |t|
  t.bigint "genre_id", null: false
  t.bigint "user_id", null: false
  t.index ["genre_id", "user_id"], name: "index_genres_users_on_genre_id_and_user_id"
  t.index ["user_id", "genre_id"], name: "index_genres_users_on_user_id_and_genre_id"
end

So I can call .genres on instances of either, eg. User.find(1).genres returns

[#<Genre:0x00000001086f1068 id: 440, name: "rock", created_at: Thu, 17 Nov 2022 01:51:38.092415000 UTC  00:00, updated_at: Thu, 17 Nov 2022 01:51:38.092415000 UTC  00:00>,

#<Genre:0x00000001086f0bb8 id: 444, name: "jazz", created_at: Thu, 17 Nov 2022 01:51:38.115169000 UTC  00:00, updated_at: Thu, 17 Nov 2022 01:51:38.115169000 UTC  00:00>,

#<Genre:0x00000001086f0ac8 id: 418, name: "electronica", created_at: Thu, 17 Nov 2022 01:50:49.304427000 UTC  00:00, updated_at: Thu, 17 Nov 2022 01:50:49.304427000 UTC  00:00>]

and Concert.find(1).genres returns

[#<Genre:0x00000001086f1068 id: 440, name: "rock", created_at: Thu, 17 Nov 2022 01:51:38.092415000 UTC  00:00, updated_at: Thu, 17 Nov 2022 01:51:38.092415000 UTC  00:00>,

#<Genre:0x00000001086f1bc8 id: 469, name: "punk", created_at: Thu, 17 Nov 2022 01:50:49.304427000 UTC  00:00, updated_at: Thu, 17 Nov 2022 01:50:49.304427000 UTC  00:00>]

In the example above, the user with the id: 1 is associated with genres named "rock", "jazz" and "electronica", so I'd be looking for every Concert that is associated to any of those.

After quite a bit of reading and searching I'm still stuck trying to figure this one out, so any input is much appreciated. Thanks in advance!

CodePudding user response:

I would try the following:

# new scope in models/concert.rb
scope :suggestions_for_user, ->(user) {
  joins(:genres).where(genres: { id: user.genres.select(:id) })
}

And use it like this:

user = User.find(...)
Concert.suggestions_for_user(user).distinct

Note the distinct will be needed because of the nature of the database join the might return duplicate concerts.

  • Related