2009-09-14 24 views
5

Tôi có một thực hiện hiện tại của will_paginate mà sử dụng paginate_by_sql phương pháp để xây dựng bộ sưu tập được đánh số trang. Chúng tôi có truy vấn tùy chỉnh cho total_entries điều đó rất phức tạp và đặt một tải lớn trên DB của chúng tôi. Vì vậy, chúng tôi muốn cắt toàn bộ số lượng từ phân trang hoàn toàn.Sử dụng will_paginate không: total_entries để cải thiện một truy vấn dài

Nói cách khác, thay vì hiển thị phân trang thông thường của 'trước đó 1 [2] 3 4 5 tiếp theo', chúng tôi chỉ đơn giản là giống như nút 'tiếp theo - trước đó'. Nhưng chúng ta cần phải biết một vài điều.

  1. Chúng tôi có hiển thị liên kết trước không? Tất nhiên, điều này chỉ xảy ra nếu các bản ghi có sẵn trước khi các bản ghi được hiển thị trong lựa chọn hiện tại
  2. Chúng tôi có hiển thị liên kết tiếp theo không? Điều này sẽ không được hiển thị nếu hồ sơ cuối cùng trong bộ sưu tập đang được hiển thị

Từ docs

Một truy vấn cho các hàng đếm sẽ tự động được tạo ra nếu bạn không cung cấp: total_entries. Nếu bạn gặp sự cố với SQL được tạo này, bạn có thể muốn thực hiện việc đếm theo cách thủ công trong ứng dụng của mình.

Vì vậy, cuối cùng, tình huống lý tưởng là như sau.

  • Tháo total_entries đếm bởi vì nó gây ra quá nhiều tải trên cơ sở dữ liệu
  • Hiển thị 50 hồ sơ tại một thời điểm với semi-pagination chỉ sử dụng tiếp theo/nút trước để di chuyển và không cần để hiển thị tất cả các số trang có sẵn
  • Chỉ hiển thị nút tiếp theo và nút trước đó cho phù hợp

Có ai đã làm việc với một vấn đề tương tự hoặc có suy nghĩ về giải pháp không?

Trả lời

13

Có rất nhiều trường hợp will_paginate thực hiện công việc thực sự khủng khiếp để tính toán số lượng mục nhập, đặc biệt nếu có các tham gia liên quan đến việc nhầm lẫn bộ tạo SQL đếm.

Nếu tất cả những gì bạn cần là một phương pháp đơn giản/kế tiếp, thì tất cả những gì bạn cần làm là cố gắng truy xuất các mục nhập N + 1 từ cơ sở dữ liệu, và nếu bạn chỉ nhận được N hoặc ít hơn bạn đang ở trang cuối cùng .

Ví dụ:

per_page = 10 
page = 2 

@entries = Thing.with_some_scope.find(:all, :limit => per_page + 1, :offset => (page - 1) * per_page) 

@next_page = @entries.slice!(per_page, 1) 
@prev_page = page > 1 

Bạn có thể dễ dàng đóng gói này trong một số mô-đun có thể được bao gồm trong các mô hình khác nhau mà yêu cầu nó, hoặc thực hiện một phần mở rộng điều khiển.

Tôi thấy rằng tính năng này hoạt động tốt hơn đáng kể so với phương pháp will_paginate mặc định.

Vấn đề hiệu suất duy nhất là hạn chế của MySQL có thể là một vấn đề tùy thuộc vào kích thước bảng của bạn.

Vì lý do nào đó, lượng thời gian cần để thực hiện truy vấn có LIMIT nhỏ trong MySQL là tỷ lệ thuận với OFFSET. Trong thực tế, công cụ cơ sở dữ liệu đọc qua tất cả các hàng dẫn đến giá trị offset cụ thể, sau đó trả về hàng số LIMIT tiếp theo, không bỏ qua phía trước như bạn mong đợi.

Đối với các tập dữ liệu lớn, nơi bạn có giá trị OFFSET trong phạm vi 100.000 cộng, bạn có thể thấy hiệu suất giảm đáng kể. Làm thế nào điều này sẽ biểu hiện là tải trang 1 là rất nhanh, trang 1000 là hơi chậm, nhưng trang 2000 là rất chậm.

+0

Cảm ơn ý tưởng. Tôi sẽ hack và xem những gì tôi có thể nghĩ ra. Tôi đã có một số truy vấn SQL tùy chỉnh dài để giải quyết nhưng tôi nghĩ rằng tôi có thể làm cho chúng hoạt động thành giải pháp và có ý tưởng về hiệu suất sẽ như thế nào. Cảm ơn! – mwilliams

+0

Cảm ơn bạn đã trả lời. Đẹp và đơn giản và tôi nhìn xa hơn một giải pháp đơn giản như vậy. Hầu hết việc triển khai của tôi đều được thực hiện và có vẻ tốt hơn nhiều. Mặc dù tôi đang gặp vấn đề với các nút tiếp theo/trước đó, nhưng tôi sẽ sớm nhận được nó. Cảm ơn một lần nữa! – mwilliams

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