Solución elegante (código en el controlador o en la vista)

Jaime I. wrote:

En la Conferencia Rails 2007, Pablo Delgado expuso una charla super
interesante sobre escalabilidad:

http://video.google.es/videoplay?docid=-3750147543196804850
conferenciarails.org - contact with domain owner | Epik.com

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!!

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

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

El 13 de agosto de 2008 20:02, Isaac Feliu Pérez
[email protected]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!

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 C.
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
*

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 :slight_smile:

El 13 de agosto de 2008 23:28, Amaia C. [email protected] escribió:

El 14 de agosto de 2008 4:04, Emili Parreño [email protected]
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 :slight_smile:

El mismo Pablete me ha explicado la técnica personalmente desde un
cibercafé
italiano :slight_smile:

###################################
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 17dias2460
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 :wink:
###################################

Voy a aplicarlo…

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 I.
[email protected]
escribi

El 14 de agosto de 2008 9:12, Fernando G.
[email protected]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 :slight_smile:

El día 14 de agosto de 2008 9:35, Jaime I.
[email protected]
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¡ :slight_smile:

Saludos
f.

El 14 de agosto de 2008 10:34, Fernando G.
[email protected]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 :stuck_out_tongue: )

¿Estoy perdido? … ya me he tomado el café eh¡ :slight_smile:

Yo si que estaba perdido y el café todavía no había llegado arriba!