2008-09-17 3 views
6

У меня есть две таблицы соединены с соединительной таблице - это просто псевдокод:Руби/Rails коллекции для коллекции

Library 
Book 
LibraryBooks 

Что мне нужно сделать, если у меня есть идентификатор библиотеки, я хочу чтобы получить все библиотеки, в которых находятся все книги, в которых находится эта библиотека.

Итак, если у меня есть библиотека 1, а в библиотеке 1 есть книги A и B, а книги A и B находятся в библиотеках 1, 2, и 3, есть ли элегантный (один ряд) способ сделать это в рельсах?

Я думал:

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

Но это не похоже на работу. Предложения?

+0

Итак, вы хотите, чтобы все библиотеки имели книги? Вышеприведенный фрагмент кода не приведет к возврату той же библиотеки, что и l. Это как просить все ваши книги, кто их владелец. Это вы. Немного смущения ... но ниже Джим и т. Д. – Gishu

+0

Все библиотеки, в которых есть книги, которые также находятся в этой библиотеке, да? –

+0

@Jim - это именно то, что я хочу – aronchick

ответ

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

Обратите внимание, что map(&:library_ids) медленнее, чем map { |b| b.library_ids } в Ruby 1.8.6, и быстрее в 1.9.0.

Следует также упомянуть, что если вы использовали :joins вместо include, он найдет библиотеку и связанные книги в одном и том же запросе, ускоряя время базы данных. :joins будет работать только в том случае, если в библиотеке есть книги.

+0

медлительность символа # to_proc обычно перевешивается вызовами базы данных. –

+0

Я не вижу, как это может сработать. Первая строка вернет массив объектов библиотеки (ну собственно прокси, но с теми же методами). У этого массива не будет метода «книг», так что вторая строка не сработает, не так ли? – MiniQuark

3

Возможно:

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

или

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

если вы хотите все это в плоском массиве.

Конечно, вы должны действительно определить это как метод в Библиотеке, чтобы поддержать благородную причину инкапсуляции.

2

Если вы хотите вернуть одномерный массив библиотек с удалением дубликатов.

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

Одна из проблем, с

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

является то, что он будет генерировать один SQL вызов для каждой книги в литрах. Лучший подход (предполагая, что я понимаю вашу схему) может быть:

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

Это не совсем так, в зависимости от того, что было загружено изначально. –

+0

Извините, я должен был сделать это ясно: я предполагаю, что вы не загрузили ничего, кроме l (исходная библиотека) –

+0

oops ... не уверен, что это работает ... вы имели в виду библиотеку или библиотеку? – aronchick

Смежные вопросы