Carl Edwards
Carl Edwards

Reputation: 14434

Using or clause with includes

Based on the following I'd like to query albums based on an artist and their artist_credit. The artist_credit association is polymorphic and can belong to a release or track. This prevents me doing a HMT. I can grab albums from the track side by doing an includes in my query as so:

Album.includes(tracks: :artist_credits).where(artist_credits: { id: artist_id })

But what I'd like to do is get ahold of both tracks AND releases. I thought of using an or clause but get this error:

ArgumentError (Relation passed to #or must be structurally compatible. Incompatible values: [:includes])

When attempting this:

Album.includes([tracks: :artist_credits, releases: :artist_credits]).where(artist_credits: { id: artist_id }).or(where(artist_credits: { id: artist_id }))

How can I get this to work? Any better way to do this?

class Album < ApplicationRecord
  has_many :releases, dependent: :destroy
  has_many :tracks, dependent: :destroy

  has_many :album_artists, dependent: :destroy
  has_many :artists, through: :album_artists
end

class Track < ApplicationRecord
  has_many :artist_credits, as: :creditable, dependent: :destroy

  belongs_to :album
end

class Release < ApplicationRecord
  has_many :artist_credits, as: :creditable, dependent: :destroy

  belongs_to :album
end

class ArtistCredit < ApplicationRecord
  belongs_to :artist
  belongs_to :creditable, polymorphic: true # release or track
end

Upvotes: 0

Views: 44

Answers (1)

engineersmnky
engineersmnky

Reputation: 29318

I would recommend the following

class Album < ApplicationRecord
   scope :accredited_artist, ->(artist_id) {
      includes([tracks: :artist_credits, releases: :artist_credits])
       .where(id: 
         where(
           id: Track.accredited_artist(artist_id).select(:album_id)
         ).or(
           where(
             id: Release.accredited_artist(artist_id).select(:album_id)
           )
         )
       )
   }
end

class Track < ApplicationRecord
   scope :accredited_artist, ->(artist_id) {
     joins(:artist_credits).where(artist_credits: { id: artist_id })
   }
end

class Release < ApplicationRecord
   scope :accredited_artist, ->(artist_id) {
     joins(:artist_credits).where(artist_credits: { id: artist_id })
   }
end

In theory this should result in a query that looks akin to

SELECT 
  albums.* 
FROM 
  albums
WHERE 
  albums.id IN (
     SELECT 
        albums.id
     FROM 
        albums
     WHERE
        albums.id IN ( 
          SELECT 
             tracks.album_id
          FROM 
             tracks
             INNER JOIN artist_credits ON [whatever your join is here] 
          WHERE 
             artist_credits.artist_id = [ID]) OR 
         albums.id IN ( 
          SELECT 
             releases.album_id
          FROM 
             releases
             INNER JOIN artist_credits ON [whatever your join is here] 
          WHERE 
             artist_credits.artist_id = [ID])
  )

Upvotes: 1

Related Questions