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.