ActiveRecord, asocjacje, includes i limit

Szukałem dłuższy czas rozwiązania dla swojego problemu. Kilka osób mi podpowiadało, ale albo źle zrozumiałem co mówili, albo te rozwiązania nie sprawdzały się dla mnie. Bez dłuższego więc wstępu:

Mam dwa modele. Gallery i Photo. Gallery ma wiele Photos, no i naturalnie, Photo należy do Gallery.

Chcę pobrać wszystkie Galerie wraz z maksymalnie 3 zdjęciami z każdej galerii. Póki co, robię to w ten sposób:

Gallery.includes(:photos)

Co zwraca mi wszystkie galerię i ich zdjęcia… Niestety, nie wiem jak ograniczyć ilość tych zdjęć do np. 3. Gdy przykładowo zrobię tak:

Gallery.includes(:photos).limit(3)

To ogranicza mi ilość galerii, nie zdjęć. Nigdzie nie znalazłem sposobu, aby przekazać jakoś limit do drugiego zapytania (tego z includes).

Próbowałem przez asocjację coś zdziałać:

has_many :ptohos_prevew, :limit => 3

Ale przy korzystaniu z include ten limit jest pomijany (co jest też napisane na http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html).

Czy ktoś miał podobny problem i sobie z nim poradził?
Czy też może podchodzę do tego ze złej strony?

Chyba właśnie podchodzisz ze złej strony. Spróbuj po prostu:

Gallery.all.each do |gallery| photos = gallery.photos.limit(3) end

Też ostatnio myślałem nad tym rozwiązaniem. Ma niestety jedną wadę - dla każdej galerii wykonuje jedno zapytanie. Jeśli wyświetlam 5 galerii na stronie przy paginacji, to jest to 5 dodatkowych zapytań. Niby strona nie będzie zbyt obciążona, żeby to zamuliło serwer - ale jednak to rozwiązanie nie wydaje mi się zbyt eleganckie. Miałem nadzieję, że uda się to zmieścić w dwóch zapytaniach - jednym pobierającym galerie, drugim pobierającym zdjęcia.

Tak czy siak, dzięki za pomoc, ale jeśli ktoś wie jak to rozwiązać przy mniejszej ilości zapytań, byłbym wdzięczny :slight_smile:

Ręcznie klepiesz zapytanie :wink:

W postgresql(9.0+) i oracle mozesz spróbować uzyć tzw. Window functions. Patrz: http://www.postgresql.org/docs/9.1/static/tutorial-window.html

W mysql teoretycznie pewnie mógłbyś to zrobić używajac podzapytań ten sposób:
SELECT photos.* FROM photos LEFT JOIN galleries ON galleries.id = photos.gallery_id WHERE photos.id > (SELECT p.id FROM photos AS p WHERE p.gallery_id = photos.gallery_id OFFSET 2 ORDER BY created_at DESC)

ale dlaa twojego przypadku oba rozwiązania to zdecydowany overkill. I łatwiej już zrobić n zapytań dla każdej galeri. Jak dodasz indeksy moze to być nawwet wydajniejsze niż jedno bardziej skomplikowane zapytanie.

Ok, dzięki wielkie za pomoc :slight_smile:

A może SQL-owy widok, taki który bierze z tabeli photos tylko 3 elementy dla danego gallery_id?

@Tomash, tak tylko trzeba jeszcze zrealizować ten widok ^^ Oczywiście to zapytanie które podałem można zaadaptować na widok, ale tak czy tak będzie miało ono wysoką złożoność O(n*m).
A opakowanie tego w widok może niestety spowodować że ktoś pomyśli że złożoność zapytania to O(n).