2016-03-09 16 views
5

Làm thế nào tôi có thể sử dụng django pagination trên elasticsearch dsl. Mã của tôi:Python elasticsearch-dsl django pagination

query = MultiMatch(query=q, fields=['title', 'body'], fuzziness='AUTO') 

s = Search(using=elastic_client, index='post').query(query).sort('-created_at') 
response = s.execute() 

// this always returns page count 1 
paginator = Paginator(response, 100) 
page = request.GET.get('page') 
try: 
    posts = paginator.page(page) 
except PageNotAnInteger: 
    posts = paginator.page(1) 
except EmptyPage: 
    posts = paginator.page(paginator.num_pages) 

Bất kỳ giải pháp nào cho điều này?

Trả lời

9

tôi thấy paginator này về vấn đề này link:

from django.core.paginator import Paginator, Page 

class DSEPaginator(Paginator): 
    """ 
    Override Django's built-in Paginator class to take in a count/total number of items; 
    Elasticsearch provides the total as a part of the query results, so we can minimize hits. 
    """ 
    def __init__(self, *args, **kwargs): 
     super(DSEPaginator, self).__init__(*args, **kwargs) 
     self._count = self.object_list.hits.total 

    def page(self, number): 
     # this is overridden to prevent any slicing of the object_list - Elasticsearch has 
     # returned the sliced data already. 
     number = self.validate_number(number) 
     return Page(self.object_list, number, self) 

và sau đó theo quan điểm của tôi sử dụng:

q = request.GET.get('q', None) 
    page = int(request.GET.get('page', '1')) 
    start = (page-1) * 10 
    end = start + 10 

    query = MultiMatch(query=q, fields=['title', 'body'], fuzziness='AUTO') 
    s = Search(using=elastic_client, index='post').query(query)[start:end] 
    response = s.execute() 

    paginator = DSEPaginator(response, settings.POSTS_PER_PAGE) 
    try: 
     posts = paginator.page(page) 
    except PageNotAnInteger: 
     posts = paginator.page(1) 
    except EmptyPage: 
     posts = paginator.page(paginator.num_pages) 

cách này nó hoạt động hoàn hảo ..

+0

thuộc tính 'count' trong ví dụ này chỉ hiển thị số lượng mục trong trang không phải là tổng số. bạn có thể ghi đè 'count' cached_property của trình thu thập trang để trả lại' _count' làm tổng số – Nasir

0

Một con đường phía trước là tạo ra một proxy giữa số Paginator và truy vấn Elasticsearch. Paginator yêu cầu hai thứ, __len__ (hoặc count) và __getitem__ (phải mất một lát). Phiên bản proxy hoạt động như sau:

class ResultsProxy(object): 
    """ 
    A proxy object for returning Elasticsearch results that is able to be 
    passed to a Paginator. 
    """ 

    def __init__(self, es, index=None, body=None): 
     self.es = es 
     self.index = index 
     self.body = body 

    def __len__(self): 
     result = self.es.count(index=self.index, 
           body=self.body) 
     return result['count'] 

    def __getitem__(self, item): 
     assert isinstance(item, slice) 

     results = self.es.search(
      index=self.index, 
      body=self.body, 
      from_=item.start, 
      size=item.stop - item.start, 
     ) 

     return results['hits']['hits'] 

Một phiên bản proxy có thể được chuyển đến Paginator và sẽ gửi yêu cầu đến ES khi cần.

0

Theo lời khuyên của Danielle Madeley, tôi cũng đã tạo proxy để tìm kiếm kết quả hoạt động tốt với phiên bản django-elasticsearch-dsl==0.4.4 mới nhất.

from django.utils.functional import LazyObject 

class SearchResults(LazyObject): 
    def __init__(self, search_object): 
     self._wrapped = search_object 

    def __len__(self): 
     return self._wrapped.count() 

    def __getitem__(self, index): 
     search_results = self._wrapped[index] 
     if isinstance(index, slice): 
      search_results = list(search_results) 
     return search_results 

Sau đó, bạn có thể sử dụng nó theo quan điểm tìm kiếm của bạn như thế này:

paginate_by = 20 
search = MyModelDocument.search() 
# ... do some filtering ... 
search_results = SearchResults(search) 

paginator = Paginator(search_results, paginate_by) 
page_number = request.GET.get("page") 
try: 
    page = paginator.page(page_number) 
except PageNotAnInteger: 
    # If page parameter is not an integer, show first page. 
    page = paginator.page(1) 
except EmptyPage: 
    # If page parameter is out of range, show last existing page. 
    page = paginator.page(paginator.num_pages) 

của Django LazyObject proxy tất cả các thuộc tính và phương pháp từ đối tượng được gán cho thuộc tính _wrapped. Tôi đang ghi đè một vài phương pháp được yêu cầu bởi Django's paginator, nhưng không làm việc ra khỏi hộp với các tìm kiếm() trường hợp.