2008-11-23 35 views
42

Làm cách nào để tôi triển khai các truy vấn cần thiết cho phân trang?Phân trang trong CouchDB?

Về cơ bản, khi trang 1 được yêu cầu, hãy nhận 5 mục nhập đầu tiên. Đối với trang 2, hãy tải trang tiếp theo 5 và tiếp tục.

Tôi dự định sử dụng điều này thông qua mô-đun couchdb-python, nhưng điều đó sẽ không tạo ra bất kỳ sự khác biệt nào đối với việc triển khai.

Trả lời

31

Các CouchDB Guide có một cuộc thảo luận tốt đẹp của pagination, trong đó có rất nhiều mẫu mã, ở đây: http://guide.couchdb.org/draft/recipes.html#pagination Dưới đây là thuật toán của họ:

  • Yêu cầu rows_per_page + 1 hàng từ quan điểm
  • Display rows_per_page hàng, cửa hàng hàng cuối cùng như next_startkey
  • Làm thông tin trang, giữ startkeynext_startkey
  • Sử dụng next_* giá trị để tạo liên kết tiếp theo và sử dụng các giá trị khác để tạo liên kết trước

N.B .: Cách thích hợp để tìm nạp trang trong CouchDB bằng cách chỉ định khóa khởi đầu chứ không phải chỉ mục khởi động như bạn nghĩ. Nhưng làm thế nào để bạn biết chìa khóa để bắt đầu trang thứ 2?Giải pháp thông minh: "Thay vì yêu cầu 10 hàng cho một trang, bạn yêu cầu 11 hàng, nhưng chỉ hiển thị 10 hàng và sử dụng các giá trị trong hàng thứ 11 làm khóa bắt đầu cho trang tiếp theo".

Nếu bạn mong muốn có nhiều tài liệu phát ra các khóa giống nhau, bạn sẽ cần phải sử dụng startdocid ngoài startkey để phân trang chính xác. Lý do là chỉ riêng startkey sẽ không còn đủ để xác định duy nhất một hàng. Các tham số này vô ích nếu bạn không cung cấp startkey. Trên thực tế, CouchDB sẽ xem xét tham số startkey trước tiên, sau đó nó sẽ sử dụng tham số startdocid để xác định lại phần đầu của dải ô nếu nhiều hàng có khả năng nhìn chằm chằm có cùng khóa nhưng ID tài liệu khác nhau. Điều tương tự cho số .

+2

Sự cố với aproach này là bạn không thể thực sự nhấp vào nhiều lần trước đó, chỉ một lần. Bạn phải tự lập chỉ mục TẤT CẢ trang đầu tiên có thể có trong trang khi bạn truy cập các trang tiếp theo hoặc bạn chỉ có thể quay lại 1 trang và sau đó bạn không có bất kỳ thông tin nào để đi trước một trang khác. – for3st

+0

Đối với những người vấp ngã ở đây và cũng chạy vào tình trạng khó xử của @ for3st, các đặc tính tự nhiên của một mảng giúp khắc phục vấn đề này. Bởi 'push()' ing trang trước bắt đầu '_id' thành mảng, bạn có thể dễ dàng' pop() 'mảng' _id' khi bạn bấm vào trước đó. Hầu hết tất cả những gì bạn phải làm là theo dõi là một mảng các số nguyên. – wootencl

1

Đây là những gì tôi đã đưa ra cho đến nay - để có được id của tất cả các bài viết, sau đó lấy các mặt hàng thực tế cho số x đầu tiên của ID ..

Nó không phải terribly hiệu quả, nhưng nhiều hơn so với truy xuất tất cả các bài đăng, sau đó ném hầu hết mọi thứ. Điều đó nói rằng, với sự ngạc nhiên của tôi, nó dường như chạy khá nhanh - tôi chạy phương thức posthelper.page() 100 lần và mất khoảng 0,5 giây.

Tôi không muốn đăng bài này trong câu hỏi thực tế, vì vậy nó sẽ không ảnh hưởng đến câu trả lời càng nhiều - đây là các mã:

allPostsUuid = """ 
function(doc) { 
if(doc.type == 'post'){ 
    emit(doc._id, null); 
} 
} 
""" 

class PostsHelper: 
    def __init__(self): 
     server = Server(config.dbhost) 
     db = server[config.dbname] 
     return db 


    def _getPostByUuid(self, uuid): 
     return self.db.get(uuid) 

    def page(self, number = 1): 
     number -= 1 # start at zero offset 
     start = number * config.perPage 
     end = start + config.perPage 

     allUuids = [ 
      x.key for x in self.db.query(allPostsUuid) 
     ] 
     ret = [ 
      self._getPostByUuid(x) for x in allUuids[start : end] 
     ] 

     if len(ret) == 0: 
      raise Error404("Invalid page (%s results)" % (len(allUuids))) 
     else: 
      return ret 
13

Các CouchDB HTTP View API cho nhiều phạm vi để làm phân trang một cách hiệu quả .

Phương pháp đơn giản nhất sẽ sử dụng startkeycount. Đếm là số lượng mục nhập tối đa CouchDB sẽ trả về cho yêu cầu xem đó, cái gì đó tùy thuộc vào thiết kế của bạn, và startkey là nơi bạn muốn CouchDB bắt đầu. Khi bạn yêu cầu xem nó cũng sẽ cho bạn biết có bao nhiêu mục nhập, cho phép bạn tính toán số trang sẽ có nếu bạn muốn hiển thị cho người dùng.

Vì vậy, yêu cầu đầu tiên sẽ không chỉ định khóa khởi động, chỉ cần đếm số lượng mục nhập bạn muốn hiển thị. Sau đó bạn có thể lưu ý khóa của mục nhập cuối cùng được trả về và sử dụng khóa đó làm khóa khởi đầu cho trang tiếp theo. Trong biểu mẫu đơn giản này, bạn sẽ nhận được một chồng chéo, trong đó mục nhập cuối cùng của một trang là mục đầu tiên của trang tiếp theo. Nếu điều này là không mong muốn nó là tầm thường để chỉ đơn giản là không hiển thị các mục cuối cùng của trang.

Một phương pháp đơn giản hơn để thực hiện việc này là sử dụng tham số bỏ qua để tìm ra tài liệu bắt đầu cho trang, tuy nhiên phương pháp này nên được sử dụng thận trọng. Tham số bỏ qua chỉ đơn giản là làm cho công cụ bên trong không trả về các mục mà nó đang lặp lại. Trong khi điều này mang lại cho hành vi mong muốn thì chậm hơn nhiều so với việc tìm kiếm tài liệu đầu tiên cho trang theo khóa. Càng nhiều tài liệu bị bỏ qua, yêu cầu càng chậm.

+0

Aha! Từ trang đó bạn đã liên kết: tham số đếm có thể được kết hợp với "skip = number of rows to skip". Hoàn hảo. – dbr

+0

Tôi đã thêm thông tin trên vào câu trả lời của bạn (để tôi tham khảo nếu không có gì khác), hy vọng bạn không phiền! – dbr

+2

Tôi đã chỉnh sửa lại. Sử dụng bỏ qua không phải là một cách tốt để làm điều này là hầu hết các trường hợp. – Kerr