Pamietac ostatnio wyszukane obiekty

Witajcie,

Chcialbym osiagnac, aby przy metodzie find AR::Base, system szukal tylko rekordow wg okreslonych kryteriow.
Wiem ze moge skorzystac z named scopes i mam wtedy na przyklad:

Class Fish < ActiveRecord::Base named_scope :young, :conditions => ["age < ?", 5] end
Wiec teraz Fish.young(:all)
pobierze tylko ryby, ktore maja mniej niz 5 lat, proste.

Teraz mam trzy problemy

Chce pobrac ryby z akwarium

Fish.find(:all, :limit => 5).map {|f| f.id}

działa, fantastycznie

Znowu wybieram ryby z akwarium

Fish.find(:all, :limit => 5).map {|f| f.id}

, wybral te same ryby, a przeciez ich juz w akwarium nie ma (wybralem przed chwila)

Problem: Jak odpytac o ryby, ktore pozostaly w akwarium ?

[code] Class Fish < ActiveRecord::Base
named_scope :aquarium, :conditions => taken

def taken
  #string z idkami wybranych ryb, polaczonymi and'ami dla budowania zapytania WHERE SQL.
  #"id != 1 and id != 2 and id !=3 and id !=4 and id != 5"
end

end[/code]
Jak rozszerzyc AR::Base#find o funkcjonalnosc zapamietania (najlepiej w zmiennej klasowej, konkretnego modelu), tablicy obiektow, ktore juz zostaly wybrane ?

Chodzi o to zebym mogl wykonac powiedzmy trzy razy pod rzad:

Fish.aquarium.find(:all, :limit => 5) Fish.aquarium.find(:all, :limit => 5) Fish.aquarium.find(:all, :limit => 5)
I zebym otrzymal 15 roznych ryb

Pozdrowienia

[quote=astropanic]Chodzi o to zebym mogl wykonac powiedzmy trzy razy pod rzad:

Fish.aquarium.find(:all, :limit => 5) Fish.aquarium.find(:all, :limit => 5) Fish.aquarium.find(:all, :limit => 5)
I zebym otrzymal 15 roznych ryb[/quote]
To jest bardziej niż bez sensu, taka składnia będzie nieczytelna i wprowadzać będzie w zakłopotanie wszystkich którzy się z nią zetkną nie znając tego rozwiązania. Może po prostu:

Fish.aquarium.find(:all, :limit => 5) Fish.aquarium.find(:all, :limit => 5, :offset => 5) Fish.aquarium.find(:all, :limit => 5, :offset => 10)
?

Bez sensu to jest negowanie czegos, jesli nie zna sie konretnego zastowania.

Oczywiscie przyklad jest abstrakcyjny. Chodzi o budowanie stron w oparciu o komponenty ( http://cells.rubyforge.org/ )

Kazda komorka (cell) ma odpytac baze o kawalek danych, jednak zadna z nich nie wie, czy i o jakie dane wczesniej inna komorka pytala.

Autorzy nowych komorek maja sie nie przejmowac budowaniem dzikich zapytan SQL’owych, dla nich “dokopanie sie” do danych ma byc jak najbardziej transparentne i zblizone do natywnego find, takie sa po prostu zalozenia projektu.

Dlatego tez Fish.aquarium.find(:all, :limit => 5)
ma jak najbardziej sens.

Jesli Cie to tak razi w oczy, damy potem fragment programu do dzialu patologii kodu na tym forum, jednak poki co skupmy sie prosze na problemie.

Pozdrowienia

Też mi się wydaje, że w złą stronę idziesz. Może rozwiązanie Huberta + offset trzymany w zmiennej globalnej jakiejś ?

astropanic:

Mógłbyś napisać scenariusz, w którym 3 komórki będą wyciągały coś z jednej tabeli i mają to być różne dane? Chciałbym zobaczyć przykład, bo w tym momencie nie potrafię za bardzo znaleźć zastosowania dla takiego rozwiązania. Najlepiej odnosząc się do jakiejś realnej aplikacji (blog, cms, serwis społecznościowy czy cokolwiek innego).

W zmiennej globalnej niekoniecznie, wolalbym w zmiennej klasowej.

Czyli w jednej komorce wywoluje:

Fish.find(:all, :limit =>3)

W kolejnej:

Fish.find(5) #pobieram rybke numer 5

To jak wedlug Ciebie powinno wygladac wywolanie w trzeciej komorce ?

Pozdrowienia

[quote=drogus]astropanic:

Mógłbyś napisać scenariusz, w którym 3 komórki będą wyciągały coś z jednej tabeli i mają to być różne dane? Chciałbym zobaczyć przykład, bo w tym momencie nie potrafię za bardzo znaleźć zastosowania dla takiego rozwiązania. Najlepiej odnosząc się do jakiejś realnej aplikacji (blog, cms, serwis społecznościowy czy cokolwiek innego).[/quote]
Jasne,

Jedna komorka pobiera artykul dnia.

Druga komorka pobiera artykuly najczesciej czytane.

Trzecia komorka pobiera artykuly najwyzej oceniane.

itd itd…

Chodzi o to zeby nie pobieraly tych artykulow, ktore sa juz pobrane przez inne.

Zastosowan jest multum, to samo moze sie tyczyc sklepu internetowego, sewrisu spolecznosciowego, czy praktycznie dowolnej innej aplikacji www

Poki wiemy z gory co bedzie na stronie, zgodnie z wzorcem MVC, bez stosowania komorek ladnie sprawe zalatwimy, jesli jednak nie od nas zalezy uklad strony, i nie wiemy co w jakiej kolejnosci bedzie odpytywane, cells zalatwia (prawie) sprawe.

Pozdrowienia

[quote=astropanic]Jedna komorka pobiera artykul dnia.

Druga komorka pobiera artykuly najczesciej czytane.

Trzecia komorka pobiera artykuly najwyzej oceniane.

itd itd…

Chodzi o to zeby nie pobieraly tych artykulow, ktore sa juz pobrane przez inne.[/quote]
Ok, a co jeśli artykuł pierwszy “Kurz złamał mi rękę” nie dość, że jest artykułem dnia, to w dodatku na najwięcej nabitych odwiedzin i ma aż 5/5 gwiazdek? Gdzie pozostałe mają niskie oceny i są nie odwiedzane? To wtedy pierwsza komórka pobierze artykuł dnia i pozostawi dwa które nie będą ani najczęściej czytane (z tych 3), ani najwyżej ocenianie (z tych 3).
Chyba,że to nie ma dla ciebie znaczenia

Rozumiem, że nie wchodzi w grę kolumna taken, którą byś oznaczył dla artykułu “pobranego” bo przy każdym żądaniu chcesz je usuwać/pobierać na nowo? (co było by logiczne gdyż popularność i oceny są “dynamiczne”)

pozdrawiam

Dokladnie

Amen, na dodatek, gdy user odwiedza rozne podstrony serwisu, rozny layout powoduje “odezwanie” sie roznych komorek, wiec nawet dlatego kolumna “taken” nie wchodzi w gre.

Widze, ze w koncu dochodzimy wszyscy do porozumienia

pozdrawiam

http://gist.github.com/353421

class Fish < ActiveRecord::Base include Remember end

Fish.not_used.dupa(:all, :limit => 5) Fish.not_used.dupa(:all, :limit => 2, :order => "id DESC")
Prototyp, nie dziala dla pojedynczych pobran typu Fish.not_used.dupa(1)

Drugi problem, nie wiem jak nadpisac metode find, stad tez dodalem nowa o nazwie “dupa”, moze i glupia, ale na pewno nie ma kolizii w nazewnictwie zmiennych.

Za wszelkie uwagi i sugestie dziekuje, wiem ze kod jest koszmarny, dopiero raczkuje w Ruby.

Pozdrowienia i wesolych swiat :slight_smile:

Jeśli już chcesz iść w tę stronę to w SQL masz coś takiego jak:

id NOT IN (1,2,3,4,5)

Zgadzam się z większością głosów w tym wątku, że Twój problem, choć istotnie ma swoje uzasadnienie, to nie jest właściwie podejmowany.

Przede wszystkim brakuje mi tutaj informacji jaki ma być zakres działania tego mechanizmu - globalny dla aplikacji, ograniczony do sesji użytkownika, czy do pojedynczego requestu. Bez ustalenia tego nie można podać sensownego rozwiązania.

Inna kwestia, którą musisz przemyśleć, to jak rozwiązać problem uporządkowania wywołań - jeśli popatrzysz na przykład z artykułami, to zauważysz, że w zależności od tego, który element zostanie wywołany jako pierwszy inna będzie zawartość poszczególnych boksów. Zatem aby działało to sensownie, to chyba świadomość kontekstu i tak jest niezbędna.

A ogólnie jeśli mogę coś doradzić, to stworzenie jakiegoś obiektu, który będzie pamiętał wybranego identyfikatory i który będzie przekazywany do tej metody. Dzięki temu znacznie łatwiej będzie zrozumieć kod. Czyli zamiast:

Fish.find(:all,:limit => 3) #=> a,b,c Fish.find(:all,:limit => 3) #=> d,e,f Fish.find(:all,:limit => 3) #=> g,h,i
będziesz robił coś takiego

[code=ruby]memory = get_memory
Fish.find(:all,:limit => 3, :memory => memory) #=> a,b,c

może być w innej klasie, ważne żeby obiekt “memory” był ten sam

Fish.find(:all,:limit => 3, :memory => memory) #=> d,e,f
Fish.find(:all,:limit => 3, :memory => memory) #=> g,h,i
memory.reset
Fish.find(:all,:limit => 3, :memory => memory) #=> a,b,c[/code]
Jest to znacznie bardziej zrozumiałe i pozwala dostosować sposób zarządzania “pamięcią” do różnych przypadków (np. można ją trzymać w memcache’u).

[quote=staszek]Jeśli już chcesz iść w tę stronę to w SQL masz coś takiego jak:

id NOT IN (1,2,3,4,5)

[/quote]
Benchmarkowales kiedys NOT IN ?

Pozdrowienia

[quote=apohllo]Zgadzam się z większością głosów w tym wątku, że Twój problem, choć istotnie ma swoje uzasadnienie, to nie jest właściwie podejmowany.

Przede wszystkim brakuje mi tutaj informacji jaki ma być zakres działania tego mechanizmu - globalny dla aplikacji, ograniczony do sesji użytkownika, czy do pojedynczego requestu. Bez ustalenia tego nie można podać sensownego rozwiązania.[/quote]
Pojedynczy request http

Dla tego projektu jest to bez znaczenia, jestem tego swiadom

[quote=apohllo]A ogólnie jeśli mogę coś doradzić, to stworzenie jakiegoś obiektu, który będzie pamiętał wybranego identyfikatory i który będzie przekazywany do tej metody. Dzięki temu znacznie łatwiej będzie zrozumieć kod. Czyli zamiast:

Fish.find(:all,:limit => 3) #=> a,b,c Fish.find(:all,:limit => 3) #=> d,e,f Fish.find(:all,:limit => 3) #=> g,h,i
będziesz robił coś takiego

[code=ruby]memory = get_memory
Fish.find(:all,:limit => 3, :memory => memory) #=> a,b,c

może być w innej klasie, ważne żeby obiekt “memory” był ten sam

Fish.find(:all,:limit => 3, :memory => memory) #=> d,e,f
Fish.find(:all,:limit => 3, :memory => memory) #=> g,h,i
memory.reset
Fish.find(:all,:limit => 3, :memory => memory) #=> a,b,c[/code]
Jest to znacznie bardziej zrozumiałe i pozwala dostosować sposób zarządzania “pamięcią” do różnych przypadków (np. można ją trzymać w memcache’u).[/quote]
Zgoda z tym obiektem zliczajacym, nie rozumiem jednak jakim ulatwieniem ma byc dla czytajacego kod

Fish.find(:all,:limit => 3, :memory => memory) #=> a,b,c

nad

Fish.not_used.find(:all,:limit => 3) #=> a,b,c

Dziekuje za cenne wskazowki.

Dalej jednak nie wiem jak przepisac metode find, zeby mozna bylo gdzies zapamietac wyniki…

Pozdrowienia

Nie

Jeśli bardzo chcesz nadpisać metodę find do zobacz na alias albo alias_method_chain, przy czym monkey patching finda to chyba nie jest zbyt dobry pomysł. Czemu nie może być do tego osobnej metody ?

Nie upieram sie przy find, moze byc inna nazwa metody.

Pozdrowienia

[quote=astropanic]Zgoda z tym obiektem zliczajacym, nie rozumiem jednak jakim ulatwieniem ma byc dla czytajacego kod

Fish.find(:all,:limit => 3, :memory => memory) #=> a,b,c

nad

Fish.not_used.find(:all,:limit => 3) #=> a,b,c

Dziekuje za cenne wskazowki.[/quote]
Takim, że w pierwszym przypadku wie skąd wziął mu się obiekt memory i że ma nad nim kontrolę. Co więcej - przy testowaniu tego rozwiązania również będziesz miał pełną kontrolę.

Albo innymi słowy - to że instancja jakiejś klasy zmienia swój stan w trakcie wykonania kodu, to normalna rzecz. Zawsze możemy stworzyć sobie nową instancję i zasadniczo powinna być “dziewicza”. Jeśli ten stan chcemy gdzieś przekazać, to też nie ma problemu - możemy np. obiekt taki zserializować.
Ale jeśli zachowanie programu zależy od stanu klasy (która co prawda w Rubim też jest instancją pewnej klasy…), to jest to znacznie mniej przejrzyste. Ten poziom izolacji o którym piszesz nie jest osiągalny w sposób przejrzysty właśnie dla klas - musiałbyś mieć jakąś metodę klasową “reset”, która byłaby wywoływana na początku każdego “calla” (przynajmniej z teoretycznego p. widzenia - prawdę powiedziawszy nie jestem pewien jak to wygląda w praktyce w Rails). Nie jest też banalne zserializowanie klasy (zastanów się, co się stanie, jeśli Twoja aplikacja będzie działać na kilku serwerach).

Jeśli wyodrębniasz istotny stan “pamięci” w osobny obiekt, to dajesz do zrozumienia innym programistom, że trzeba na to zwrócić uwagę, a nie ukryć w otchłani implementacyjnej.

Jeśli nadal masz wątpliwości, że jawna kontrola stanu “pamięci” jest potrzebna, to zastanów się dlaczego w AR są metody, które pozwalają na wymuszenie przeczytania asocjacji bezpośrednio z bazy (chociaż może być już w pamięci).

Więcej na ten temat możesz poczytać sobie np. na blogu Gilada Brachy w szczególności ten post.

EDIT

Myślę, że można by w ogóle zmienić sposób używania takich obiektów - zamiast tego co wyżej, pomyśl o czymś takim:

memory = Fish.request_memory memory.find(:limit => 5) #a,b,c,d,e memory.find(:limit => 5) #f,g,h,i,j memory.reset memory.find(:limit => 5) #a,b,c,d,e
Dlaczego to jest lepsze?

  1. Bo nie musisz grzebać w AR#find
  2. Bo nikogo nie oszukujesz, że oryginalne AR#find jest tym samym, którym było wcześniej.
  3. Bo możesz zaimplementować różne wywołania “request_memory”, “application_memory”, “session_memory” i w kodzie dokładnie widać, z którego z nich korzystasz.

[quote=astropanic][quote=staszek]Jeśli już chcesz iść w tę stronę to w SQL masz coś takiego jak:

id NOT IN (1,2,3,4,5)

[/quote]
Benchmarkowales kiedys NOT IN ?

Pozdrowienia[/quote]
Ile będziesz miał tych elementów w klauzuli NOT IN (…)? Póki ich ilość nie jest w setkach (tysiącach) to nie wydaje mi się żeby tu był problem z wydajnością.

Witajcie,

Wzialem pod uwage Wasze opinie na temat rozwiazania problemu, zamiast nadpisywac metode find ustalilem dwie nowe:

find_remember - szuka poprzez zwykle find i zapamietuje co wyszukalo

reset_remember - czysci pamiec zapamietanych rekordow

Na dodatek stworzylem osobny “name space” dla kazdej z klas, dzieki czemu nie beda kolidowaly id’ki jednej klasy z id’kami drugiej, na razie poprzez zwykly hash, jednak czekam na sugestie i mozliwosci poprawy mojego kodu.

class Fish < ActiveRecord::Base acts_as_rememberable end

[code]>> Fish.find_remember(:all, :limit => 1)
=> [#<Fish id: 1, name: “seahorse”, created_at: “2010-04-03 19:13:37”, updated_at: “2010-04-03 19:13:37”>]

Fish.find_remember(:all, :limit => 1)
=> [#<Fish id: 2, name: “goldfish”, created_at: “2010-04-03 19:13:44”, updated_at: “2010-04-03 19:13:44”>]

Fish.find_remember(:all, :limit => 1)
=> [#<Fish id: 3, name: “neon”, created_at: “2010-04-03 19:14:39”, updated_at: “2010-04-03 19:14:39”>]

Fish.reset_remember
=> []

Fish.find_remember(:all, :limit => 1)
=> [#<Fish id: 1, name: “seahorse”, created_at: “2010-04-03 19:13:37”, updated_at: “2010-04-03 19:13:37”>]

Fish.find_remember(:all, :limit => 1)
=> [#<Fish id: 2, name: “goldfish”, created_at: “2010-04-03 19:13:44”, updated_at: “2010-04-03 19:13:44”>]

Fish.find_remember(:all, :limit => 1)
=> [#<Fish id: 3, name: “neon”, created_at: “2010-04-03 19:14:39”, updated_at: “2010-04-03 19:14:39”>][/code]
http://gist.github.com/354771 - co o tym myslicie ?
Pozdrowienia

Zmienne globalne są źródłem wszelkiego zła. Kiedy używasz zmiennej globalnej ginie kot, owca albo Bandzierchlast.

Zatem zmień to tak, żeby nie było tej koszmarnej zmiennej globalnej.