Tengo una página en la que muestro información sobre cada tipo de
mensajes, y los últimos mensajes publicados de ese tipo. La primera
aproximación serÃa cargar en el controlador los tipos, y en la vista
hacer algo asÃ:
<% @tipos.each do |tipo| %>
--- Mostrar los datos del tipo ---
<% @mensajes = Mensajes.find(:all, :order => "created_at DESC", :limit
=> 3, :conditions => "tipo_id = #{tipo}" ) %>
--- Mostrar los últimos mensajes del tipo ---
<% end %>
Esto funciona bien, pero es feo porque mete en la vista una carga de
datos que deberÃa estar en el controlador. ¿Cómo podrÃa resolver esto de
una forma más elegante?
s2 y gracias
on 28.02.2008 09:42
on 28.02.2008 09:56
2008/2/28 Fernando Calatayud <ruby-forum-incoming@andreas-s.net>: > > Esto funciona bien, pero es feo porque mete en la vista una carga de > datos que debería estar en el controlador. ¿Cómo podría resolver esto de > una forma más elegante? > Sin más información, creo que la forma correcta a nivel MVC sería así: Modelos: class Tipo < AR::Base has_many :mensajes end class Mensaje < AR::Base belongs_to :tipo end Controlador: @tipos = Tipo.find(:all) # opcionalmente, Tipo.find(:all, :include => :mensajes) Vista: <% @tipos.each do |tipo| %> -- Datos de tipo -- <% tipo.mensajes.each do |mensaje| %> -- Datos de mensaje -- <% end %> <% end %> -- Sergio Gil Pérez de la Manga e-mail > sgilperez@gmail.com blog > http://www.lacoctelera.com/porras
on 28.02.2008 09:57
Hola Fernando
Al ver tipo_id, supongo que tendras una relación por el medio, con lo
que cada tipo ya tiene sus mensajes. para optimizar un poco en el
controlador podrÃa meter algo del estilo:
@tipos = Tipo.find(:all, :include => :mensajes) #carga tambien los
mensajes
Para mostrar por pantalla los mensajes puedes hacer un parcial para los
mensajes
y en la vista te quedarÃa algo asÃ:
<% for tipo in @tipos %>
<%= render :partial => "mensajes", :locals => {:mesajes =>
tipo.mensajes} %>
<% end -%>
Esta forma la suelo usar bastante y me funicona bastante bien.
Un saludo.
on 28.02.2008 10:08
Perdón, se me ha olvidado un detalle: la tabla de mensajes es enorme (varios cientos de miles), y sólo tengo que mostrar los 3 últimos. Un acceso como el que proponéis (que obviamente es la solución correcta) tendrÃa un coste muy alto y funcionarÃa mal, por lo que de entrada no es viable. Lo que quizá sà serÃa viable serÃa definir una nueva relación, un poco más elaborada: que en vez de Tipo.rb has_many :mensajes fuera algo asÃ: has_many :ultimos_mensajes, :order => 'created_at desc', :limit => 3 Y declarar una clase ultimos_mensajes que herede de mensajes... ¿qué os parece eso?
on 28.02.2008 10:10
2008/2/28 Fernando Calatayud <ruby-forum-incoming@andreas-s.net>: > > Esto funciona bien, pero es feo porque mete en la vista una carga de > datos que debería estar en el controlador. ¿Cómo podría resolver esto de > una forma más elegante? > > s2 y gracias > -- > Hay varios problemillas con tu código, obviamente el primero ya lo comentas tú: pones en la vista lógica de la aplicación, lo que no es nada recomendable. Pero supongamos que no tenemos problemas con eso (por ahora). Para obtener los mensajes de un tipo utilizas :conditions => "tipo_id = #{tipo}" que tiene dos problemas. En tú caso un problema de inyección de código es difícil, pero sería recomendable que en esas cadenas de conditions, permitieras a Rails que te protegiera utilizando :conditions => { :tipo_id => tipo.id }. Pero además no necesitas construir tú mismo las condiciones SQL cuando tienes una relación has_many/belongs_to entre los modelos Tipo y Mensaje, desde un objeto de clase Tipo puedes utilizar el método mensajes y desde un objeto de clase Mensaje el método tipo, incluso con finders y demás cosas que te proporciona Rails. De forma que tu línea para recupera mensajes sería: @mensajes = tipo.mensajes.find(:limit => 3, :order => 'created_at DESC') Mi recomendación sería seguir eliminando lógica de la vista y crear un nuevo método en mensajes llamado ultimos que precisamente hiciera esa búsqueda: class Mensaje def self.ultimos(limit = 3) # Por defecto 3, pero se puede modificar al llamar al método find(:limit => limit, :order => 'created_at DESC') end end Un método que mágicamente Rails te permite utilizar "a través" de una asociación: @mensajes = tipo.mensajes.ultimos El último problema de tu código es el "temido" problema de las n+1 consultas: tú código hace una primera consulta para obtener todos los tipos, y luego, para cada uno de ellos (n) hace otra consulta para obtener los tres últimos mensajes. Normalmente ese problema se resuelve con lo que se denomina "eager_loading", utilizando la opción:include de Rails o algo similar, pero no consigo ver con tus condiciones sobre los mensajes como se puede obtener los últimos tres de cada tipo directamente desde la base de datos. Quizá alguien con más experiencia en SQL puede ver una solución. Suerte.
on 28.02.2008 12:09
2008/2/28 Fernando Calatayud <ruby-forum-incoming@andreas-s.net>: > has_many :mensajes > > fuera algo así: > has_many :ultimos_mensajes, :order => 'created_at desc', :limit => 3 > > Y declarar una clase ultimos_mensajes que herede de mensajes... ¿qué os > parece eso? No haría falta crear otra clase, bastaría con especificar la clase en la relación, algo así: class Tipo < AR::Base has_many :mensajes has_many :ultimos_mensajes, :class_name => 'Mensaje', :order => 'created_at desc', :limit => 3 end Si no me equivoco... (estoy escribiendo el código directamente aquí) -- Sergio Gil Pérez de la Manga e-mail > sgilperez@gmail.com blog > http://www.lacoctelera.com/porras
on 28.02.2008 14:39
Sergio Gil Pérez de la Manga wrote: > No har�a falta crear otra clase, bastar�a con especificar la clase en > la relaci�n, algo > as�: class Tipo < AR::Base > has_many :mensajes > has_many :ultimos_mensajes, :class_name => 'Mensaje', :order => > 'created_at desc', :limit => 3 > end > > Si no me equivoco... (estoy escribiendo el c�digo directamente > aqu�) -- > Sergio Gil P�rez de la Manga > e-mail > sgilperez@gmail.com > blog > http://www.lacoctelera.com/porras Sergio, muchas gracias, tu solución es cojonuda... pero me temo que en términos de rendimiento es un desastre. Aunque no lo habÃa mencionado antes, por no salirnos del problema, desde mensajes se linka también con la tabla usuarios (el autor del mensaje). Y al probar esto: @tipos = Tipo.find(:all, :order => 'position', :include => {:ultimos_contenidos_3 => :usuario}) se genera una consulta tan brutal que mysql lleva un cuarto de hora y aún no me ha devuelto el resultado. No puedo pasar por abstracciones de rails porque no generan consultas óptimas, y con estas tablas tan grandes el que sea o no óptima es crÃtico. Por lo tanto, no tengo más remedio que montar más o menos "a mano" la consulta. Voy a probar a hacerlo con un método de la clase, como sugiere Daniel, a ver qué resultado da... ya os contaré. s2
on 28.02.2008 17:24
Hola fernando, la solucion que te dio pablo me parece buena, solo
cambiar lo
siguiente:
<% for tipo in @tipos %>
<%= render :partial => "mensajes", :locals => {:mesajes =>
tipo.mensajes}
%>
<% end -%>
por;
<% for tipo in @tipos %>
<%= render :partial => "mensaje", :collection => tipo.mensajes[0..2]
%>
<% end -%>
Aqui hay varias cosas importantes, primero al usar collection tendrias
la
variable local mensaje disponible automaticamnete en el partial.
Ahora por el lado del SQL, veo un problema y es que quieras o no estas
cargando todos los mensajes asociados al tipo; ahora como el metodo
'mensajes' te devuelve un array entonces puedes jugar y seleccionar la
cantidad de elementos que necesites, aunque tal vez no estan ordenados
como
tu quieras, eso ya es cosa de que juegues un poco mas con los metodos
del
array.
Otra solucion tal vez un poco mas pragmatica seria usar una sentencia
SQL
pura y dura en tu controlador para obtener los mensajes, y entonces te
evitarias de liarte tanto en las vistas.
Saludos.
El dÃa 28/02/08, Fernando Calatayud <ruby-forum-incoming@andreas-s.net>
escribió:
on 28.02.2008 17:36
Ruben Davila wrote: > Hola fernando, la solucion que te dio pablo me parece buena, solo > cambiar lo > siguiente: > > <% for tipo in @tipos %> > <%= render :partial => "mensajes", :locals => {:mesajes => > tipo.mensajes} > %> > <% end -%> > > por; > > <% for tipo in @tipos %> > <%= render :partial => "mensaje", :collection => tipo.mensajes[0..2] > %> > <% end -%> > > Aqui hay varias cosas importantes, primero al usar collection tendrias > la > variable local mensaje disponible automaticamnete en el partial. > > Ahora por el lado del SQL, veo un problema y es que quieras o no estas > cargando todos los mensajes asociados al tipo; ahora como el metodo > 'mensajes' te devuelve un array entonces puedes jugar y seleccionar la > cantidad de elementos que necesites, aunque tal vez no estan ordenados > como > tu quieras, eso ya es cosa de que juegues un poco mas con los metodos > del > array. > > Otra solucion tal vez un poco mas pragmatica seria usar una sentencia > SQL > pura y dura en tu controlador para obtener los mensajes, y entonces te > evitarias de liarte tanto en las vistas. > > Saludos. > > > > El dÃa 28/02/08, Fernando Calatayud <ruby-forum-incoming@andreas-s.net> > escribió: Yo no puedo estar cargando todos los mensajes, hablamos de más de 100.000!! Tengo que irme a SQL propia, pero el tema es que son n SQLs, y a ver cómo meto un nº indeterminado de SQLs en el controlador... por eso el primer intento ha sido meterla en la vista (que es una chapuza, pero va como una bala). s2
on 28.02.2008 17:56
Fernando comprendo exactamente tu situacion, por eso me temia que la solución que te di no iba a ser la mas óptima, ademas que no habia leido tus otros mensajes, bueno ya que necesitas bastante optimización de la consulta, tal vez este slide[1] te pueda servir. Saludos [1]: http://work.rowanhick.com/wp-content/uploads/2008/02/activerecordpresentationfeb12.pdf El dÃa 28/02/08, Fernando Calatayud <ruby-forum-incoming@andreas-s.net> escribió:
on 28.02.2008 18:14
Daniel Rodriguez Troitiño wrote: > 2008/2/28 Fernando Calatayud <ruby-forum-incoming@andreas-s.net>: >> >> Esto funciona bien, pero es feo porque mete en la vista una carga de >> datos que deber�a estar en el controlador. �C�mo podr�a resolver esto de >> una forma m�s elegante? >> >> s2 y gracias >> -- >> > > Hay varios problemillas con tu c�digo, obviamente el primero ya lo > comentas t�: pones en la vista l�gica de la aplicaci�n, lo que no es > nada recomendable. Pero supongamos que no tenemos problemas con eso > (por ahora). > > Para obtener los mensajes de un tipo utilizas :conditions => "tipo_id > = #{tipo}" que tiene dos problemas. En t� caso un problema de > inyecci�n de c�digo es dif�cil, pero ser�a recomendable que en esas > cadenas de conditions, permitieras a Rails que te protegiera > utilizando :conditions => { :tipo_id => tipo.id }. > > Pero adem�s no necesitas construir t� mismo las condiciones SQL cuando > tienes una relaci�n has_many/belongs_to entre los modelos Tipo y > Mensaje, desde un objeto de clase Tipo puedes utilizar el m�todo > mensajes y desde un objeto de clase Mensaje el m�todo tipo, incluso > con finders y dem�s cosas que te proporciona Rails. De forma que tu > l�nea para recupera mensajes ser�a: > > @mensajes = tipo.mensajes.find(:limit => 3, :order => 'created_at DESC') > > Mi recomendaci�n ser�a seguir eliminando l�gica de la vista y crear un > nuevo m�todo en mensajes llamado ultimos que precisamente hiciera esa > b�squeda: > > class Mensaje > def self.ultimos(limit = 3) # Por defecto 3, pero se puede modificar > al llamar al m�todo > find(:limit => limit, :order => 'created_at DESC') > end > end > > Un m�todo que m�gicamente Rails te permite utilizar "a trav�s" de una > asociaci�n: @mensajes = tipo.mensajes.ultimos > > El �ltimo problema de tu c�digo es el "temido" problema de las n+1 > consultas: t� c�digo hace una primera consulta para obtener todos los > tipos, y luego, para cada uno de ellos (n) hace otra consulta para > obtener los tres �ltimos mensajes. Normalmente ese problema se > resuelve con lo que se denomina "eager_loading", utilizando la > opci�n:include de Rails o algo similar, pero no consigo ver con tus > condiciones sobre los mensajes como se puede obtener los �ltimos tres > de cada tipo directamente desde la base de datos. Quiz� alguien con > m�s experiencia en SQL puede ver una > soluci�n. Suerte. Daniel, gracias por todas las correcciones. - Efectivamente, aquà no hay posibilidad de inyección de código, los tipos los maneja el administrador. - Respecto a construir las condiciones SQL yo mismo, sà necesito hacerlo: cuestión de rendimiento crÃtico. Ir del tipo a los mensajes es lentÃsimo, mientras que ir directamente a los mensajes, filtrando por tipo, es instantáneo... incluso aunque sea n+1 (en este caso, n es pequeño). - Sin embargo, tu idea de mover el código a la clase es muy buena... sólo que por tema de eficiencia, serÃa en la clase Tipo y no el la Mensaje (si lo meto en mensaje, volvemos al mismo problema). Metiendo en Tipo.rb esto: def ultimos_contenidos(limit = 3) # Por defecto 3, pero se puede modificar al llamar al método Mensaje.find(:all, :limit => limit, :order => "contenidos.created_at DESC", :conditions => "tipo_id = #{self.id} and publicado") end en la vista ya puedo hacer: tipo.ultimos_contenidos.each do |msg| Y va bien.
on 28.02.2008 22:11
2008/2/28 Fernando Calatayud <ruby-forum-incoming@andreas-s.net>: > > > > > > nuevo m�todo en mensajes llamado ultimos que precisamente hiciera esa > > asociaci�n: > > m�s experiencia en SQL puede ver una > tipo, es instantáneo... incluso aunque sea n+1 (en este caso, n es > end > Posted via http://www.ruby-forum.com/. > _______________________________________________ > Ror-es mailing list > Ror-es@lists.simplelogica.net > http://lists.simplelogica.net/mailman/listinfo/ror-es > Cuando utilizas un método de clase (los que llevan self) a través de una asociación Rails añade automáticamente la condición que tu escribes para tipo_id (tendrÃas que añadir la de publicado al método de clase, como antes no la ponÃas en mi solución no la puse). No creo que el código SQL generado de ese modo y el código que se genera con tu método sea muy diferente. De todas formas, por lo que hablas, sobre la cantidad de datos y demás, no me parecen tantos como para que MySQL tarde tanto en cargarlos. ¿Tienes bien puestos los indices de las tablas? NecesitarÃas al menos un Ãndice para tipo_id y otro para created_at en la tabla mensajes. Sin esos dos Ãndices MySQL va a tener que leer cada uno de los registros para encontrar los que quieres. Si crees que es la base de datos la que no da la talla podrÃas utilizar el plugin Query Analyzer <http://agilewebdevelopment.com/plugins/query_analyzer> para comprobar que tus queries SQL están utilizando los Ãndices correctamente. Suerte.
on 29.02.2008 09:38
Daniel Rodriguez Troitiño wrote: > De todas formas, por lo que hablas, sobre la cantidad de datos y > demás, no me parecen tantos como para que MySQL tarde tanto en > cargarlos. ¿Tienes bien puestos los indices de las tablas? > NecesitarÃas al menos un Ãndice para tipo_id y otro para created_at en > la tabla mensajes. Sin esos dos Ãndices MySQL va a tener que leer cada > uno de los registros para encontrar los que quieres. > > Si crees que es la base de datos la que no da la talla podrÃas > utilizar el plugin Query Analyzer > <http://agilewebdevelopment.com/plugins/query_analyzer> para comprobar > que tus queries SQL están utilizando los Ãndices correctamente. > > Suerte. Los Ãndices están bien puestos: en tipo_id, en created_at y en usuario_id. Pero con la consulta "estandar" de ruby, es un desastre de rendimiento, mientras que al partirlo en n+1 ya va bien. ¿La causa? Pues aunque sólo habÃa puesto lo que me parecÃa más relevante, la consulta sobre tipos lleva más cosas; no sé si será por eso el desastre de algunas combinaciones. Esta es la verdadera consulta, completa: @tipos = Tipo.find(:all, :order => 'tipos.position', :conditions => "clase_id = 1", :select => "tipos.id, tipos.nombre_completo, url_tipo, t.url_clase, tipos.descripcion, (SELECT count(*) FROM mensajes WHERE tipo_id = tipos.id and publicado and inicial_id = 0) as temas, (SELECT count(*) FROM mensajes WHERE tipo_id = tipos.id and publicado and inicial_id <> 0) as respuestas", :joins => "join clase t on tipos.clase_id = t.id") Me temo que esos SELECT count pueden tener un impacto dramático si empiezan a lanzarse de forma repetida... s2
on 01.03.2008 00:57
Perdonad por desviarme del tema, pero de donde puedo encontrar informacion acerca de este tipo de relaciones (la de "ultimos_mensajes")? class Tipo < AR::Base has_many :mensajes *has_many :ultimos_mensajes, :class_name => 'Mensaje', :order => 'created_at desc', :limit => 3* end Siempre habia visto relaciones normales con otros objectos, pero nunca en las que especificabas esos parametros. Gracias y disculpas de nuevo por desviarme del tema principal. Sergio Gil Pérez de la Manga escribió:> 2008/2/28 Fernando Calatayud <ruby-forum-incoming@andreas-s.net>:
on 01.03.2008 02:56
2008/2/29 Fernando Calatayud <ruby-forum-incoming@andreas-s.net>: > > <http://agilewebdevelopment.com/plugins/query_analyzer> para comprobar > > Me temo que esos SELECT count pueden tener un impacto dramático si > empiezan a lanzarse de forma repetida... > > > > s2 En las sentencias "select count..." creo que los índices son necesarios sobre las tres columnas a la vez, pero podría equivocarme. Te recomiendo ejecutar esas sentencias "select count..." sobre tu base de datos con "explain select count..." para ver si MySQL utiliza correctamente los índices. De cualquier manera, si MySQL utiliza bien los índices y aún te parece que la eficiencia no es la adecuanda lo más recomendable seríade-normalizar, es decir, incluir en la tabla "tipos" dos columnas "temas_count" y "respuestas_count" que se actualizaran cuando se crearan nuevos mensajes o se destruyeran los ya existentes. Suerte.
on 01.03.2008 02:58
2008/2/29 Javier Martínez <ecentinela@gmail.com>: > > Siempre habia visto relaciones normales con otros objectos, pero nunca > en las que especificabas esos parametros. > > Gracias y disculpas de nuevo por desviarme del tema principal. > > Obviamente en la ayuda de Rails: <http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#M001103>
on 01.03.2008 17:17
El dÃa 28/02/08, Fernando Calatayud <ruby-forum-incoming@andreas-s.net> escribió: > > Perdón, se me ha olvidado un detalle: la tabla de mensajes es enorme > (varios cientos de miles), y sólo tengo que mostrar los 3 últimos. Un > acceso como el que proponéis (que obviamente es la solución correcta) > tendrÃa un coste muy alto y funcionarÃa mal, por lo que de entrada no es > viable. > En la Conferencia Rails 2007, Pablo Delgado expuso una charla super interesante sobre escalabilidad: http://video.google.es/videoplay?docid=-3750147543196804850 http://2007.conferenciarails.org/archivos/pablo_delgado_escalabilidad.pdf Uno de los trucos para mostrar rápidamente los x últimos registros de una tabla enorme era crear un Ãndice inverso, en plan: reverse_created_at = 2030 - created_at Asà el MySQL los encuentra mucho más rápidamente, por lo visto.
on 03.03.2008 17:08
Daniel Rodriguez Troitiño wrote: > De cualquier manera, si MySQL utiliza bien los �ndices y a�n te parece > que la eficiencia no es la adecuanda lo m�s recomendable > ser�ade-normalizar, es decir, incluir en la tabla "tipos" dos columnas > "temas_count" y "respuestas_count" que se actualizaran cuando se > crearan nuevos mensajes o se destruyeran los ya existentes. > > Suerte. Por ahà voy a tirar... un poco de de-normalización, cacheando los datos intermedios, y ya tengo un rendimiento óptimo... y eso sÃ, un foco de problemas potenciales si meto la pata. Pero es lo que toca para este caso, creo. s2
on 03.03.2008 17:10
Jaime Iniesta wrote: > > En la Conferencia Rails 2007, Pablo Delgado expuso una charla super > interesante sobre escalabilidad: > > http://video.google.es/videoplay?docid=-3750147543196804850 > http://2007.conferenciarails.org/archivos/pablo_delgado_escalabilidad.pdf > > Uno de los trucos para mostrar rápidamente los x últimos registros de > una > tabla enorme era crear un Ãndice inverso, en plan: > > reverse_created_at = 2030 - created_at > > Asà el MySQL los encuentra mucho más rápidamente, por lo visto. Un enlace muy interesante, desde luego!! lo del Ãndice inverso, si funciona, me servirÃa para varios sitios... aunque mejor sólo lo montaré donde sea realmente necesario. s2 y gracias!!
on 03.03.2008 17:38
On 03/03/2008, Fernando Calatayud <ruby-forum-incoming@andreas-s.net> wrote: > intermedios, y ya tengo un rendimiento óptimo... y eso sÃ, un foco de > problemas potenciales si meto la pata. Pero es lo que toca para este > caso, creo. > Ya lo dijo Cal Henderson http://www.google.es/search?hl=en&client=firefox-a&rls=org.mozilla%3Aen-US%3Aofficial&hs=FfD&q=normalized+data+is+for+sissies&btnG=Search -- Manuel, que piensa que eres una excelente persona y medra en torno a http://simplelogica.net y/o http://simplelogica.net/logicola/ Recuerda comer mucha fruta y verdura.
on 13.08.2008 19:49
El 1 de marzo de 2008 18:13, Jaime Iniesta <jaimeiniesta@gmail.com>escribió: > > reverse_created_at = 2030 - created_at > > Asà el MySQL los encuentra mucho más rápidamente, por lo visto. > Estoy probando esta técnica que expuso Pablo Delgado para mostrar rápidamente los X últimos registros de las tablas... lo que él expone es que como MySQL tarda mucho en ordenar una tabla a la inversa, si por ejemplo quieres sacar los últimos 10 posts de una tabla de un millón de posts, es crear un Ãndice inverso respecto a la fecha de creación, en plan reverse_created_at = 2030 - created_at Bueno, lo primero con lo que me encuentro al llevarlo a la práctica es que no se puede restar asà directamente, 2030 es un integer y created_at una fecha asà que lo pongo asÃ: class Post < ActiveRecord::Base before_save :update_reverse def update_reverse self.reverse_created_at = (('2030/1/1'.to_datetime) - (self.created_at.to_datetime)) self.reverse_updated_at = (('2030/1/1'.to_datetime) - (self.updated_at.to_datetime)) end end Pero esto no me funciona, porque de la resta me devuelve un tipo de datos Rational que no se guarda en el MySQL... Rational(676652623, 86400) ¿Qué estoy haciendo mal?
on 13.08.2008 20:06
Jaime,
provando en el script/console, con rails 2.1 me sale esto
>> Time.zone.at(Time.zone.parse('2030-01-01') - u.created_at)
=> Thu, 28 Nov 1991 06:51:53 CET +01:00
Usease, utilizando el time.zone.at conviertes el numero de nuevo a
fecha.
Espero que te sirva!
Salutaciones,
--
Isaac Feliu
on 13.08.2008 20:09
Pensandolo mejor, otra opción elegante serÃa sobreescribir el método
"-" de la classe Integer para que si se le pasa un objeto de tipo
datetime (o el que corresponda, como ActiveSupport::TimeWithZone), te
haga la operación y devuelva un objeto de tipo datetime, del palo
module CoreHacks
module IntegerHacks
def -(other)
if other.is_a?(ActiveSupport::TimeWithZone)
Time.zone.at(Time.zone.parse("#{self}-01-01}") - other)
else
super other
end
end
end
end
end
Integer.send :include, CoreHacks::IntegerHacks
asà deberias poder hacer 2030 - created_at
¡¡Cuidado que es una hipotesis y no lo he probado!!
Salutaciones,
--
Isaac Feliu
on 13.08.2008 20:13
El 13 de agosto de 2008 20:02, Isaac Feliu Pérez <isaac.feliu@gmail.com>escribió: > Jaime, > provando en el script/console, con rails 2.1 me sale esto > > > >> Time.zone.at(Time.zone.parse('2030-01-01') - u.created_at) > => Thu, 28 Nov 1991 06:51:53 CET +01:00 > > Usease, utilizando el time.zone.at conviertes el numero de nuevo a fecha. > > Vaya, estoy en rails 2.0.1 y no tengo Time zones... pero gracias!
on 13.08.2008 23:29
Hola He hecho unas pruebecillas en la consola de rails 2.0.2 y parece que si restas dos DateTime da un Rational, pero si restas dos Time da un Float, que no tendrÃa que dar problemas con la BD. Si created_at es un datetime puedes convertirlo en Time con to_time. A lo mejor con eso te sirve. Loading development environment (Rails 2.0.2) >> a = Time.utc(year=2030) => Tue Jan 01 00:00:00 UTC 2030 >> b = User.find(:first).created_at => Wed Jan 02 17:38:14 +0100 2008 >> a.class => Time >> b.class => Time >> a - b => 694164106.0 >> c = a - b => 694164106.0 >> c.class => Float >> >> d = DateTime.new(year=2008, month=4) => Tue, 01 abr 2008 00:00:00 +0000 >> d.class => DateTime >> d.to_time.class => Time Saludos Amaia -- Amaia Castro Dabne TecnologÃas de la Información - www.dabne.net - http://blog.dabne.net ~~*~~ Buscador de Subvenciones - http://buscaboe.dabne.net Apuntes de Rails - http://apuntesderails.amaiac.net ~~*~~
on 14.08.2008 04:04
Yo creo que la solución más simple es pasar el año 2030 a segundos y el created_at también y asà tienes un integer, que seguramente mysql lo ordena más rápido que un datetime. Por ejemplo Time.utc(2030).to_i - created_at.to_i # Time.utc(2030) crea un objeto Time "01/01/2030 00:00" y andando, ya tienes un Ãndice reverso en formato integer. En el 2030 ya veremos que pasa :) El 13 de agosto de 2008 23:28, Amaia Castro <amaia@dabne.net> escribió:
on 14.08.2008 08:23
El 14 de agosto de 2008 4:04, Emili Parreño <emili@abecedata.com> escribió: > Yo creo que la solución más simple es pasar el año 2030 a segundos y el > created_at también y asà tienes un integer, que seguramente mysql lo ordena > más rápido que un datetime. > Ahà estamos compañero! Se trata de que el campo reverse_created_at ha de ser un campo numérico y no de fecha como habÃamos planificado en un principio, Emili :) El mismo Pablete me ha explicado la técnica personalmente desde un cibercafé italiano :) ################################### Si por ejemplo restas 30 de agosto a 13 de agosto, el resultado es un numero, el numero de segundos (si no me equivoco, o milisegundos) entre el 30 (futuro) y 13 (presente) . El resultado sera 17dias*24*60 en segundos. Da un rational o un integer o un numero, como quieras. El echo es que lo que tiene fecha mas vieja sera un numero grande, mientras que el mas reciente sera un numero mas pequeno. CONCLUSION el campo reverse_created_at es INTEGER o lo que sea... Debes crear un indice con una migracion o a mano de la siguiente manera ALTER TABLE posts ADD INDEX reverse_created_at ( reverse_created_at ) ALTER TABLE posts ADD INDEX reverse_updated_at ( reverse_updated_at) Y cuando debas buscar los ultimos posts le pones Post.find(:all, ...................., :order=> "reverse_updated_at" , :limit=>5) Los indices numericos van mucho mas rapidos ;) ################################### Voy a aplicarlo...
on 14.08.2008 09:12
Y por qué no, simplemente, como me decía nuestro amigo Blat:
self.created_at_inverse = Time.local(2038,01,01,00,00,00).to_i -
Time.now.to_i
O optimizando:
self.created_at_inverse = 2145913200 - Time.now.to_i
O como he resumido yo:
self.created_at_inverse = -(Time.now.to_i)
No entiendo esa fijación de que este campo sea siempre positivo (hasta el
2038).
Yo al final he dejado mi callback
así:
Este es mi callback (before_save):
def update_created_at_inverse
self.created_at_inverse = -(self.created_at.to_i)
end
Saludos
f.
El día 14 de agosto de 2008 8:23, Jaime Iniesta
<jaimeiniesta@gmail.com>
escribi
on 14.08.2008 09:36
El 14 de agosto de 2008 9:12, Fernando Guillen <fguillen.mail@gmail.com>escribió: > Y por qué no, simplemente, como me decÃa nuestro amigo Blat: > > self.created_at_inverse = Time.local(2038,01,01,00,00,00).to_i - > Time.now.to_i > > O optimizando: > > self.created_at_inverse = 2145913200 - Time.now.to_i Pues también, más rápido... > > > O como he resumido yo: > > self.created_at_inverse = -(Time.now.to_i) > > No entiendo esa fijación de que este campo sea siempre positivo (hasta el > 2038). > Según Pablete, lo de que el Ãndice sea positivo es porque MySQL tarda en ordenar a la inversa. O sea que si se lo das ya en positivo, no tiene que darle la vuelta a la tabla: 2030 - 2008 = 2022 2030 - 2009 = 2021 2030 - 2010 = 2020 2030 - 2011 = 2019 que ya vienen ordenados como nos interesa: 2019, 2020, 2021, 2022 En cambio en tu solución: -2008 -2009 -2010 -2011 que ordenados queda -2011, -2010, -2009, -2008 con lo que MySQL tiene que darle la vuelta para tenerlos como nos interesa, lo más reciente primero. Supongo que en ambos casos se nota la optimización al ser un integer en lugar de una fecha pero me parece que la gracia está en que ya el indice venga ordenado y asà evitarle el trabajo de darle la vuelta. Tiene la desventaja de que en el 2030 deja de funcionar bien el orden, pero ya me he apuntado en mi calendario del 2029 repasar este truco y actualizarlo :)
on 14.08.2008 10:34
El día 14 de agosto de 2008 9:35, Jaime Iniesta <jaimeiniesta@gmail.com> escribió:> Según Pablete, lo de que el índice sea positivo es porque MySQL tarda en > ordenar a la inversa. Estamos de acuerdo en que 'ordenar de manera inversa' quiere decir 'ordenar de manera DESC'. Ya que los índices se crean de manera natural ordenados de manera ASC. > En cambio en tu solución: > > -2008 > -2009 > -2010 > -2011 > > que ordenados queda -2011, -2010, -2009, -2008 con lo que MySQL tiene que > darle la vuelta para tenerlos como nos interesa, lo más reciente primero. No hay que olvidar que son números negativos por lo que el orden natural de estos (es decir ASC) sería: -2008 -2009 -2010 -2011 Que creo que es lo que estamos buscando: mysql> create table prueba( campo integer ); mysql> insert prueba ( campo ) values ( -2008 ); mysql> insert prueba ( campo ) values ( -2009 ); mysql> insert prueba ( campo ) values ( -2010 ); mysql> insert prueba ( campo ) values ( -2011 ); mysql> select * from prueba order by campo asc; +-------+ | campo | +-------+ | -2011 | | -2010 | | -2009 | | -2008 | +-------+ Incluso con la historia de restar 2030 - Time.now seguiría funcionando más allá del 2030. puesto que 1 > 0 > -1. ¿Estoy perdido? .. ya me he tomado el café eh¡ :) Saludos f.
on 14.08.2008 10:44
El 14 de agosto de 2008 10:34, Fernando Guillen <fguillen.mail@gmail.com>escribió: > > > > mysql> insert prueba ( campo ) values ( -2010 ); > > Incluso con la historia de restar 2030 - Time.now seguirÃa funcionando > más allá del 2030. puesto que 1 > 0 > -1. Jostris! Pues tienes razón! Me equivoqué yo al afirmar: "que ordenados queda -2011, -2010, -2009, -2008 con lo que MySQL tiene que darle la vuelta para tenerlos como nos interesa, lo más reciente primero." porque ya están ordenados como nos interesa, lo más reciente primero... y asà MySQL no tiene que darle la vuelta... ni tenemos que hacer una resta para actualizar... y todo seguirá funcionando pasado el 2.030... (lo siento @emili pero habrá que sacar dinero para la jubilación de otro lado :P ) > > ¿Estoy perdido? .. ya me he tomado el café eh¡ :) Yo si que estaba perdido y el café todavÃa no habÃa llegado arriba!