Scope i has_many through

Słowem wstępu: mam dwa modele - karta (card) i talia (deck) z relacją has_many through przez model deckcard.
Karta posiada artybuty określające pięć kolorów (red, green, blue, white, black) wyrażone liczbowo, karta może mieć jeden, kilka lub wszystkie z wymienionych pięciu kolorów.

class Card < ApplicationRecord
	has_many :deckcards, dependent: :destroy
	has_many :decks, through: :deckcards
end	

class Deck < ApplicationRecord
	has_many :deckcards, inverse_of: :deck, dependent: :destroy
	has_many :cards, through: :deckcards
	belongs_to :user
  	scope :by_red, -> {joins(:cards).where('cards.red > 0')}
  	scope :by_green, -> {joins(:cards).where('cards.green > 0')}
  	scope :by_blue, -> {joins(:cards).where('cards.blue > 0')}
  	scope :by_black, -> {joins(:cards).where('cards.black > 0')}
  	scope :by_white, -> {joins(:cards).where('cards.white > 0')}
end

class Deckcard < ApplicationRecord
  	belongs_to :card
  	belongs_to :deck
end

co chcę osiągnąć:
chciałbym móc filtrować talie pod względem tego czy zawierają karty posiadające wybrane kolory.
np w talii jest 10 kart z kolorem red oraz 10 kart z kolorami blue + black, chciałbym żeby ta talia wyświetliła się gdy wywołam @decks.by_red.by_blue.by_black
Niestety w obecnej postaci zwraca zamiast tego talie które zawierają kartę która posiada wszystkie trzy kolory (red + blue + black).
Tutaj moje pytanie: co mam zrobić aby wywołując wspomniane @decks.by_red.by_blue.by_black zwracało mi talie które zawierają dowolną kartę z kolorem red, dowolną kartę z kolorem blue oraz dowolną kartę z kolorem black?

Jeśli Rails 5 to .or http://blog.bigbinary.com/2016/05/30/rails-5-adds-or-support-in-active-record.html

Niestety stosując .or to gdy wybiorę np kolory red i blue wyświetli mi wszystkie talie zawierające karty które mają kolor red LUB kolor blue, a nie o to mi chodzi.
Wybierając dwa kolory chciałbym wyświetlić talie które zawierają przynajmniej jedną kartę posiadającą pierwszy kolor ORAZ przynajmniej jedną kartę posiadającą kolor drugi.

próbowałem kombinować coś w tym stylu:
joins(:cards).where(cards.any? {|card| card.red > 0})
ale niestety nie działa

Użycie Array#any? wewnątrz where nie może się udać :smiley:

Po joinie musisz zagregować dane po decku, popatrz na sqlowe GROUP BY i HAVING. W pseudokodzie wyglądałoby to jakoś tak:
joins(:cards).having("sum(cards.red) > 0 AND sum(cards.blue) > 0 AND ... ).group('deck.id')

1 Like

Dzięki, udało się po małej poprawce group('id') zamiast group('deck.id'), teraz wszystko działa jak powinno.