2012-01-13 12 views
19

Estoy buscando un ORM Ruby para reemplazar ActiveRecord. He estado mirando Sequel y DataMapper. Se ven bastante bien, pero ninguno de ellos parece hacer lo básico: no cargar todo en la memoria cuando no lo necesitas.¿Hay ORM de Ruby que utilizan cursores o búsqueda inteligente?

quiero decir que he intentado lo siguiente (o equivalente) en ActiveRecord y Sequel en la mesa con un montón de filas:

posts.each { |p| puts p } 

Ambos se vuelven locos en la memoria. Parece que cargan todo en la memoria en lugar de ir a buscar cosas cuando es necesario. He utilizado el find_in_batches en ActiveRecord, pero no es una solución aceptable:

  1. ActiveRecord no es una solución aceptable porque hemos tenido demasiados problemas con él.
  2. ¿Por qué mi código debe tener en cuenta un mecanismo de búsqueda? Me complace configurar en algún lugar el tamaño de la página, pero eso es todo. Con find_in_batches, debe hacer algo como:

    post.find_in_batches {| batch | batch.each {| p | puts p}}

Pero eso debería ser transparente.

Entonces, ¿hay algún lugar confiable de Ruby ORM que realiza la búsqueda correctamente?


Actualización:

Como se mencionó Sergio, en Rails 3 se puede utilizar find_each cuales exactamente lo que quiero. Sin embargo, como ActiveRecord no es una opción, excepto si alguien realmente puede convencerme de usarlo, las preguntas son:

  1. ¿Qué ORM admiten el equivalente de find_each?
  2. ¿Cómo hacerlo?
  3. ¿Por qué necesitamos un find_each, mientras que find debería hacerlo, no?

Respuesta

43

Sequel's Dataset#each produce filas individuales a la vez, pero la mayoría de los controladores de bases de datos cargarán primero el resultado completo en la memoria.

Si está utilizando el adaptador de Postgres de Sequel, puede optar por utilizar los cursores reales:

posts.use_cursor.each{|p| puts p} 

Esto obtiene 1000 filas a la vez de forma predeterminada, pero se puede usar una opción para especificar la cantidad de filas que se apropiación por búsqueda de cursor:

posts.use_cursor(:rows_per_fetch=>100).each{|p| puts p} 

Si no está utilizando el adaptador de Postgres Sequel, puede utilizar la extensión para la paginación de Sequel:

Sequel.extension :pagination 
posts.order(:id).each_page(1000){|ds| ds.each{|p| puts p}} 

Sin embargo, al igual que find_in_batches/find_each de ActiveRecord, esto hace consultas separadas, por lo que debe tener cuidado si hay modificaciones concurrentes en el conjunto de datos que está recuperando.

La razón por la que este no es el valor predeterminado en Sequel es probablemente la misma razón por la que no es la predeterminada en ActiveRecord, que es que no es un buen valor predeterminado en el caso general. Solo las consultas con grandes conjuntos de resultados realmente deben preocuparse por ello, y la mayoría de las consultas no devuelven grandes conjuntos de resultados.

Al menos con el apoyo del cursor adaptador de Postgres, que es bastante fácil para que sea el predeterminado para su modelo:

Post.dataset = Post.dataset.use_cursor 

Para la extensión de paginación, realmente no se puede hacer eso, pero se puede envolver en un método que lo hace principalmente transparente.

+0

Funciona también para MySql o es solo con Postgres? – mb14

+0

El material de use_cursor es postgres-only. No estoy seguro de que MySQL admita cursores para devolver resultados. La documentación del cursor MySQL indica que "MySQL admite cursores dentro de programas almacenados", consulte http://dev.mysql.com/doc/refman/5.6/en/cursors.html. –

+0

Php usa db_fetch masivamente, así que supongo que Mysql admite cursores. Sin embargo, tal vez no en el controlador de ruby ​​predeterminado – mb14

3

ActiveRecord en realidad tiene un casi transparente batch mode:

User.find_each do |user| 
    NewsLetter.weekly_deliver(user) 
end 
+0

Gracias (1). Está en Rail 3, (estamos usando rails 2.3.x). De todos modos, ActiveRecord no es una opción. Voy a reformular mi pregunta – mb14

+0

Lástima que no puedes usarlo :-(Pero voy a estar pendiente de esta pregunta, tal vez aparezca algo interesante :-) –

+1

find_each estaba alrededor desde 2.3.2 de acuerdo con apidock: http://apidock.com/rails/v2.3.2/ActiveRecord/Batches/ClassMethods/find_each – tokland

-1

Tal vez se puede considerar Ohm, que se basa en Redis tienda NoSQL.

+0

Necesito algo para MySql – mb14

4
Sequel.extension :pagination 
posts.order(:id).each_page(1000) do |ds| 
    ds.each { |p| puts p } 
end 

¡Es muy lento en mesas grandes!

Se hace evidente, miró el cuerpo del método: http://sequel.rubyforge.org/rdoc-plugins/classes/Sequel/Dataset.html#method-i-paginate

# File lib/sequel/extensions/pagination.rb, line 11 

def paginate(page_no, page_size, record_count=nil) 
    raise(Error, "You cannot paginate a dataset that already has a limit") if @opts[:limit] 
    paginated = limit(page_size, (page_no - 1) * page_size) 
    paginated.extend(Pagination) 
    paginated.set_pagination_info(page_no, page_size, record_count || count) 
end 
2

Este código funciona más rápido que find_in_batches en ActiveRecord

id_max = table.get(:max[:id]) 
id_min = table.get(:min[:id]) 
n=1000 
(0..(id_max-id_min)/n).map.each do |i| 
    table.filter(:id >= id_min+n*i, :id < id_min+n*(i+1)).each {|row|} 
end 
+0

Usé Sequel.mysql2 –

Cuestiones relacionadas