ruby orm datamapper sequel

ruby - sequel model



¿Hay algún ORM de Ruby que use cursores o búsqueda inteligente? (5)

Estoy buscando un ORM de Ruby para reemplazar ActiveRecord. He estado buscando en Sequel y DataMapper. Se ven bastante bien, sin embargo, ninguno de ellos parece hacer lo básico: no cargar todo en la memoria cuando no lo necesitas.

Quiero decir que he intentado lo siguiente (o equivalente) en ActiveRecord y Sequel en la tabla con muchas filas:

posts.each { |p| puts p }

Ambos se vuelven locos de memoria. Parecen cargar todo en la memoria en lugar de buscar cosas cuando es necesario. Utilicé los find_in_batches en ActiveRecord, pero no es una solución aceptable:

  1. ActiveRecord no es una solución aceptable porque tuvimos demasiados problemas con ella.
  2. ¿Por qué mi código debe ser consciente de un mecanismo de paginación? Estoy feliz de configurar en alguna parte el tamaño de la página, pero eso es todo. Con find_in_batches necesitas hacer algo como:

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

Pero eso debería ser transparente.

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

Actualizar:

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

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

ActiveRecord en realidad tiene un modo por lotes casi transparente:

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


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

Si está utilizando el adaptador Postgres de Sequel, puede elegir usar cursores reales:

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

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

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

Si no está utilizando el adaptador Postgres de Sequel, puede usar la extensión de 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 , esto hace consultas separadas, por lo que debe tener cuidado si hay modificaciones simultáneas 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 el valor predeterminado en ActiveRecord, que es que no es un buen valor predeterminado en el caso general. Solo las consultas con conjuntos de resultados grandes realmente deben preocuparse, y la mayoría de las consultas no devuelven conjuntos de resultados grandes.

Al menos con el soporte del cursor del adaptador Postgres, es bastante fácil convertirlo en el predeterminado para su modelo:

Post.dataset = Post.dataset.use_cursor

Para la extensión de paginación, realmente no puedes hacer eso, pero puedes envolverlo en un método que lo haga casi transparente.


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


Tal vez pueda considerar Ohm , que se basa en el almacén Redis NoSQL.


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

¡Es muy muy lento en mesas grandes!

Resulta claro, 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