2009-06-05 12 views
6

Digamos que tengo un modelo de libro y un modelo de autor. Quiero enumerar a todos los autores ordenados por el recuento de sus libros. ¿Cuál es la mejor manera de hacer eso?¿Cómo ordenar autores por su cuenta de libro con ActiveRecord?

Sé cómo hacer esto en SQL, ya sea haciendo where ... in con una selección anidada o con alguna combinación. Pero lo que me gustaría saber es cómo hacerlo bien con ActiveRecord.

Respuesta

9

Como se ha sugerido Kevin, counter_cache es la opción más fácil, sin duda lo que yo usaría.

class Author < ActiveRecord::Base 
    has_many :books, :counter_cache => true 
end 

class Book < ActiveRecord::Base 
    belongs_to :author 
end 

Y si está usando Rails 2.3 y que le gustaría que este es el valor predeterminado de ordenar usted podría utilizar el nuevo método default_scope:

class Author < ActiveRecord::Base 
    has_many :books, :counter_cache => true 

    default_scope :order => "books_count DESC" 
end 

books_count es el campo que realiza el comportamiento contador de almacenamiento en caché, y probablemente exista una forma mejor que usarlo directamente en el alcance predeterminado, pero le da la idea y hará el trabajo.

EDIT:

En respuesta al comentario preguntando si counter_cache funcionará si un raíles aplicación no altera los datos, así que puede, pero no de la manera predeterminada como carriles de incrementos y decrementos del mostrador de ahorrar tiempo. Lo que podría hacer es escribir su propia implementación en una devolución de llamada after_save.

class Author < ActiveRecord::Base 
    has_many :books 

    after_save :update_counter_cache 

    private 
    def update_counter_cache 
     update_attribute(:books_count, self.books.length) unless self.books.length == self.books_count 
    end 
end 

Ahora usted no tiene instalado un counter_cache, pero si el nombre de campo en la base de datos books_count según la convención counter_cache entonces cuando se mira:

@Author = Author.find(1) 
puts @author.books.size 

Se seguirá utilizando la número de contador en caché en lugar de realizar una búsqueda en la base de datos. Por supuesto, esto solo funcionará cuando la aplicación Rails actualice la tabla, por lo que si otra aplicación hace algo, entonces es posible que sus números no estén sincronizados hasta que la aplicación Rails regrese y se tenga que guardar. La única forma de evitar esto es pensar en un trabajo cron para sincronizar números si la aplicación de sus raíles no busca lo suficiente para que no importe.

+0

¿funciona el contador de caché si una aplicación sin rieles actualiza los datos en la base de datos directamente (es decir, elimina un libro)? – dplante

+0

Supongo que no, al menos hasta que guarde el registro nuevamente desde la aplicación te Rails. –

7

Sugiero contador de almacenamiento en caché del recuento de libros como otro atributo en Autor (Rails admite esto con una opción en la asociación). Este es, de lejos, el método más rápido, y Rails es bastante bueno para asegurarse de que mantenga la cuenta sincronizada.

+2

Gracias por la respuesta rápida y precisa. Utilicé su respuesta para buscar más información y encontré http://railscasts.com/episodes/23-counter-cache-column que también es muy útil. Sin embargo, he seleccionado la respuesta de Railsninja porque es más completa y ayudaría a los transeúntes a mejorar. –

9

Tomado de http://www.ruby-forum.com/topic/164155

Book.find(:all, :select => "author_id, count(id) as book_count", :group => "author_id", :order => "book_count") 
+0

Derecha. Esto es básicamente hacer una consulta SQL y hacer que el DBMS haga el trabajo, y esa es la mejor y más simple forma de hacerlo. En particular, desea hacer esto para evitar hacer muchas consultas (posiblemente hasta una por autor) cuando una consulta hará lo que necesita. –

3

rieles Suponiendo 3

@authors=Authors.include(:books).sort do |a,b| 
    a.books.size <=> b.books.size 
end 

Nota: Esto devuelve una matriz de registros, no un ActiveRecord: Relación.

Nota2: Utilice el tamaño, no cuente, ya que count ejecutará llamadas sql a la base de datos.

Cuestiones relacionadas