2008-09-17 15 views
6

Ho un due tavoli uniti con una tabella di aderire - questo è solo pseudo codice:Rubino/Rails collezione in collezione

Library 
Book 
LibraryBooks 

Che cosa devo fare è se ho l'id di una biblioteca, voglio per ottenere tutte le librerie di tutti i libri in cui è contenuta questa libreria.

Quindi se ho Libreria 1 e Libreria 1 contiene libri A e B e libri A e B sono in Librerie 1, 2, e 3, c'è un modo elegante (una linea) per farlo in rotaie?

Stavo pensando:

l = Library.find(1) 
allLibraries = l.books.libraries 

ma questo non sembra funzionare. Suggerimenti?

+0

Quindi vuoi tutte le librerie che hanno libri? Lo snippet di codice precedente non restituirà semplicemente la stessa libreria di l. È come chiedere a tutti i tuoi libri, chi è il loro proprietario. Un po 'di confusione .. ma Jim's ans sotto farà il trucco di raccolta. – Gishu

+0

Tutte le librerie che hanno libri che sono anche in questa libreria, sì? –

+0

@Jim - questo è esattamente ciò che voglio – aronchick

risposta

7
l = Library.find(:all, :include => :books) 
l.books.map { |b| b.library_ids }.flatten.uniq 

noti che map(&:library_ids) è più lento di map { |b| b.library_ids } in Ruby 1.8.6, e più veloce in 1.9.0.

Vorrei anche ricordare che se si utilizzava :joins invece di include lì, troverebbe la libreria e i libri correlati tutti nella stessa query accelerando il tempo del database. :joins funzionerà solo se una libreria ha libri.

+0

la lentezza di Symbol # to_proc è solitamente superata dalle chiamate al database. –

+0

Non vedo come potrebbe funzionare. La prima riga restituirà un array di oggetti Library (beh in realtà un proxy, ma con gli stessi metodi). Questo array non avrà un metodo "libri", quindi la seconda riga fallirà, non è così? – MiniQuark

3

Forse:

l.books.map {|b| b.libraries} 

o

l.books.map {|b| b.libraries}.flatten.uniq 

se si desidera che tutto in una matrice piatta.

Ovviamente, si dovrebbe davvero definire questo come metodo su Library, in modo da mantenere la nobile causa dell'incapsulamento.

2

Se si desidera restituire una serie monodimensionale di librerie, con i duplicati rimossi.

l.books.map{|b| b.libraries}.flatten.uniq 
2

Un problema con

l.books.map{|b| b.libraries}.flatten.uniq 

è che genererà una chiamata SQL per ogni libro in l. Un approccio migliore (supponendo che ho capito lo schema) potrebbe essere:

LibraryBook.find(:all, :conditions => ['book_id IN (?)', l.book_ids]).map(&:library_id).uniq 
+0

Questo non è strettamente vero, a seconda di ciò che è stato caricato inizialmente. –

+0

scusate, avrei dovuto chiarirlo: suppongo che non abbiate precaricato altro oltre a l (la libreria iniziale) –

+0

oops ... non sono sicuro che funzioni ... intendete LibraryBook o Library? – aronchick