Pregunta

He estado tratando de cadena consultas Arel usando alcances, en lugar de usar una lógica de largo aliento que escribí en el controlador. Sin embargo, los alcances son más lentos que acaba de obtener todos los registros y luego tamizar a través de ellos con cierta lógica. Me pregunto, entonces, ¿por qué ámbitos son mejores.

Esto es lo que estoy haciendo:

  • una pregunta tiene muchas respuestas
  • una respuesta pertenece a una pregunta
  • una pregunta tiene una columna "question_type" que utilizo para solucionar el problema

En primer lugar, los ámbitos manera ...

en question.rb:

scope :answered, joins(:answers).order('answers.created_at desc')
scope :dogs, where(:question_type => "dogs")
scope :cats, where(:question_type => "cats")
scope :mermaids, where(:question_type => "mermaids")

en questions_controller.rb:

@dogs_recently_answered = Question.answered.dogs.uniq[0..9]
@cats_recently_answered = Question.answered.cats.uniq[0..9]
@mermaids_recently_answered = Question.answered.mermaids.uniq[0..9]

A continuación, en la vista, I ciclo a través de esas variables de instancia (que ahora son las matrices que contienen como máximo 10 elementos) y mostrar los resultados.

Éstos son los tiempos que se tarda en cargar la página (cinco veces diferentes):

Completado 200 OK en 535ms (Visitas: 189.6ms | ActiveRecord: 46.2ms)

Completado 200 OK en 573ms (Visitas: 186.0ms | ActiveRecord: 46.3ms)

Completado 200 OK en 577ms (Visitas: 189.0ms | ActiveRecord: 45.6ms)

Completado 200 OK en 532ms (Visitas: 182.9ms | ActiveRecord: 46.1ms)

Completado 200 OK en 577ms (Visitas: 186.7ms | ActiveRecord: 46.9ms)

Ahora, el camino controlador desordenado ...

@answers = Answer.order("created_at desc")
@all_answered = []
@answers.each {|answer| @all_answered << answer.question}
@recently_answered = @all_answered.uniq
@dogs_all_answered = []
@cats_all_answered = []
@mermaids_all_answered = []
@recently_answered.each do |q|
  if q.question_type == "dogs"
    @dogs_all_answered << q
    @dogs_recently_answered = @dogs_all_answered[0..9]
  elsif q.question_type == "cats"
    @cats_all_answered << q
    @cats_recently_answered = @cats_all_answered[0..9]
  elsif q.question_type == "mermaids"
    @mermaids_all_answered << q
    @mermaids_recently_answered = @mermaids_all_answered[0..9]
  end
end

Y aquí son los tiempos que se tarda en cargar la página ahora (cinco veces diferentes):

Completado 200 OK en 475ms (Visitas: 196.5ms | ActiveRecord: 34.5ms)

Completado 200 OK en 480ms (Visitas: 200.4ms | ActiveRecord: 36.4ms)

Completado 200 OK en 434ms (Visitas: 198.2ms | ActiveRecord: 35.8ms)

Completado 200 OK en 475ms (Visitas: 194.2ms | ActiveRecord: 36.4ms)

Completado 200 OK en 475ms (Visitas: 195.0ms | ActiveRecord: 35.4ms)

Así que ...

Además de facilitar la lectura, lo que está por ser ganado por perfeccionar la consulta con un alcance? ¿Se eventualmente se convierten en más rápido cuando hay más registros?

¿Fue útil?

Solución

En primer lugar, no estoy seguro de entender cómo una pregunta puede ser que no sea único, por lo que me vería en que se trata de eliminar. No sé la lógica de sus datos, por lo que podría no ser aplicable, pero es un paso adicional que usted podría ser capaz de evitar.

Así es como yo lo enfoque:

scope :answered, joins(:answers).order('answers.created_at desc')
scope :recent, take(10)
scope :dogs, where(:question_type => "dogs")
scope :cats, where(:question_type => "cats")
scope :mermaids, where(:question_type => "mermaids")

@dogs_recently_answered = Question.answered.dogs.recent
@cats_recently_answered = Question.answered.dogs.recent
@mermaids_recently_answered = Question.answered.dogs.recent

Esto desplaza la parte TOP de la consulta a la base de datos que le corresponde en lugar de ir a buscar todos de las filas y luego desechar todos menos 10. En función de los criterios uniquing, es posible también utilizar un alcance como

scope :unique, select('DISTINCT column_name')

y luego se puede utilizar Question.cats.unique.recent y obtener todo de una consulta rápida que aprovecha el álgebra relacional que los sistemas de bases de datos están diseñados para.

Otros consejos

Creo que la razón los alcances son más lentas en este caso es porque dan lugar a 3 consultas de bases de datos separadas, mientras que el otro enfoque utiliza el conocimiento de que los tres resultados pueden ser satisfechas por la sola consulta que utilice.

Si se asume que es el caso, no es sorprendente que los alcances están haciendo 3 consultas separadas ya que el sistema no sabe cuando se llama a la primera que se va a llamar a los demás justo después. Tal vez hay una estrategia de optimización que sería razonable para este escenario, pero no sé que ActiveRecord implementa.

De todos modos, esta es una desventaja del alcance en este caso particular. Me gusta porque son ámbitos limpia / clara, flexible y se encapsula una abstracción nombre para una consulta. AFAICT, en muchos casos no son apreciablemente más lenta que la consulta directa equivalente.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top