selecionar muitos através de muitos ... tipo de
-
21-08-2019 - |
Pergunta
Este é o meu primeiro post sobre Pilha, por isso, tenha comigo se eu violar qualquer protocolo.
Eu estou trabalhando em um projeto no Rails (2.1.2), e eu tenho um cenário de relações que se parece com isso:
event [has_many] days
Pessoas (em diferentes categorias) pode se inscrever para os dias do evento, dando os resultados seguinte ligação:
category [has_many] customers [has_many] orders [has_many] days
[belongs_to] event
Agora, eu gostaria de ter o número total de 'eventos' para um cliente, ou para todos os clientes em uma determinada categoria, e eu estou preso. AFAIK, não há nenhuma maneira de realizar um simples 'encontrar' através de um conjunto de objetos, correto? Então, qual seria a solução; loops aninhados, e um método de coleta para obter os 'eventos' dos 'dias' em ordens?
Por favor, deixe-me saber se eu sou claro.
Obrigado por sua ajuda!
Solução
Eu pessoalmente fazer isso usando uma instrução MySQL. Eu não sei ao certo, mas acho que é muito mais rápido, em seguida, os outros exemplos (usando os trilhos fornecidos métodos de associação).
Isso significa que, no modelo cliente que você poderia fazer algo como: (Note que eu estou supondo que você está usando as teclas de associação padrão: 'model_name_id')
class Customer
def events
Event.find_by_sql("SELECT DISTINCT e.* FROM events e, days d, orders o, customers c WHERE c.id=o.customer_id AND o.id=d.order_id AND e.id=d.event_id")
end
end
que irá retornar todos os eventos associados com o usuário, e não duplicado (a palavra-chave 'DISTINCT' faz certeza disso). Você irá, como com o exemplo acima, a informação perde sobre o que dias exatamente o usuário se inscreveu. Se você precisar essa informação, por favor, que o diga.
Além disso, eu não incluí um exemplo para o seu modelo Categoria, porque eu achei que você poderia adaptar o meu exemplo si mesmo. Se não, é só me avisar.
EDIT:
Acabei de ler você quer apenas contar os eventos. Isso pode ser feito ainda mais rápido (ou pelo menos, menos memória intensivo) usando a instrução contagem. Para usar isso, basta usar a seguinte função:
def event_count
Event.count_by_sql(SELECT DISTINCT COUNT(e.*) FROM ... ... ...
end
Outras dicas
Seus modelos provavelmente parecido com este:
class Category
has_many :customers
class Customer
has_many :orders
has_many :days, :through => :orders # I added this
belongs_to :category
class Order
has_many :days
belongs_to :customer
class Day
belongs_to :event
belongs_to :order
class Event
has_many :days
Com isso, você pode contar eventos para o cliente:
events = customer.days.count(:group => 'event_id')
Ele irá retornar OrderedHash assim:
#<OrderedHash {1=>5, 2=>13, 3=>0}>
Você pode obter eventos contam por:
events[event_id]
events[1] # => 5
events[2] # => 13
etc.
Se você quiser número total de eventos uniq:
events.size # => 3
Em caso de eventos de contagem para todos os clientes na categoria que eu faria algo assim:
events = {}
category.customers.each {|c| events.merge!(c.days.count(:group => 'event_id') ) }
events.size # => 9 - it should be total number of events
Mas neste caso você informações perder quantas vezes cada evento apareceu.
Eu não testei isso, então pode haver alguns erros.