2009-06-05 21 views
6

Giả sử tôi có Mô hình sách và mô hình Tác giả. Tôi muốn liệt kê tất cả các tác giả được sắp xếp theo số lượng sách của họ. Cách tốt nhất để làm điều đó là gì?Cách sắp xếp tác giả theo số lượng sách của họ với ActiveRecord?

Tôi biết cách thực hiện điều này trong SQL, bằng cách thực hiện ở đâu .. với một lựa chọn lồng nhau hoặc với một số phép nối. Nhưng những gì tôi muốn biết là làm thế nào để làm điều này độc đáo với ActiveRecord.

Trả lời

9

Như Kevin đã gợi ý, counter_cache là lựa chọn dễ dàng nhất, chắc chắn những gì tôi sẽ sử dụng.

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

class Book < ActiveRecord::Base 
    belongs_to :author 
end 

Và nếu bạn đang sử dụng Rails 2.3 và bạn sẽ như thế này là mặc định đặt hàng bạn có thể sử dụng phương pháp default_scope mới:

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

    default_scope :order => "books_count DESC" 
end 

books_count là lĩnh vực mà thực hiện các hành vi truy cập bộ nhớ đệm, và có lẽ là một cách tốt hơn so với sử dụng nó trực tiếp trong phạm vi mặc định, nhưng nó mang lại cho bạn ý tưởng và sẽ hoàn thành công việc.

EDIT:

Để đối phó với những nhận xét hỏi nếu counter_cache sẽ làm việc nếu một ứng dụng đường ray không làm thay đổi dữ liệu, cũng có thể, nhưng không phải theo cách mặc định như Rails gia và decrements quầy ở tiết kiệm thời gian. Những gì bạn có thể làm là viết thực hiện của riêng bạn trong một cuộc gọi lại 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 

Bây giờ bạn không có một counter_cache cài đặt, nhưng nếu bạn đặt tên cho trường trong books_count cơ sở dữ liệu theo quy ước counter_cache sau đó khi bạn nhìn lên:

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

Nó sẽ vẫn sử dụng truy cập số được lưu trong bộ nhớ cache thay vì thực hiện tra cứu cơ sở dữ liệu. Tất nhiên điều này sẽ chỉ hoạt động khi ứng dụng đường ray cập nhật bảng, vì vậy nếu ứng dụng khác thực hiện điều gì đó thì số của bạn có thể không đồng bộ cho đến khi ứng dụng đường ray quay lại phải lưu lại. Cách duy nhất xung quanh điều này mà tôi có thể nghĩ là một công việc cron để đồng bộ số nếu ứng dụng đường ray của bạn không tra cứu mọi thứ thường xuyên đủ để làm cho nó không quan trọng.

+0

làm bộ đệm truy cập hoạt động nếu ứng dụng không có đường ray cập nhật dữ liệu trong cơ sở dữ liệu trực tiếp (tức là xóa sách)? – dplante

+0

Tôi cho rằng không, ít nhất là cho đến khi bạn lưu lại bản ghi từ ứng dụng te Rails. –

7

Tôi đề nghị truy cập bộ nhớ đệm số lượng sách dưới dạng thuộc tính khác trên Tác giả (Rails hỗ trợ điều này với tùy chọn trên liên kết). Đây là phương pháp nhanh nhất, và Rails là khá tốt về việc đảm bảo nó giữ số đếm đồng bộ.

+2

Cảm ơn câu trả lời nhanh và chính xác. Tôi đã sử dụng câu trả lời của bạn để tìm kiếm thêm thông tin và tìm thấy http://railscasts.com/episodes/23-counter-cache-column cũng rất hữu ích. Tôi đã chọn câu trả lời của railsninja mặc dù vì nó toàn diện hơn và sẽ giúp người qua đường tốt hơn. –

9

Taken từ 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

Phải. Đây là cơ bản chỉ cần làm một truy vấn SQL và nhận được DBMS để làm công việc, và đó là cách đơn giản và tốt nhất để làm điều đó. Đặc biệt, bạn muốn làm điều này để tránh thực hiện nhiều truy vấn (có thể là nhiều truy vấn cho mỗi tác giả) khi một truy vấn sẽ làm những gì bạn cần. –

3

ray Giả sử 3

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

Lưu ý: Điều này sẽ trả về một mảng các hồ sơ, không phải là một ActiveRecord: Mối quan hệ.

Lưu ý2: Sử dụng kích thước, không được tính, vì số lượng sẽ thực hiện lệnh gọi sql đến db.

Các vấn đề liên quan