2016-03-09 30 views
5

elasticsearch dslでdjangoページネーションを使用するにはどうすればよいですか。 マイコード:Python elasticsearch-dsl djangoページ区切り

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) 

この問題に対する解決策はありますか?

答えて

9

私はこのlinkでこのページネータを見つけました:前方に別の方法で作成することです。..

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) 

それは完璧に動作し、このよう

+0

この例の 'count'プロパティは、合計ではなくページ内のアイテムの数を示しています。 paginatorの 'count' cached_propertyをオーバーライドして、合計カウントとして' _count'を返すことができます – Nasir

0

を:

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) 

、その後ビューで私が使用してPaginatorとElasticsearchクエリの間のプロキシです。 Paginatorには、__len__(またはcount)と__getitem__(スライスが必要)という2つのものが必要です。プロキシのラフバージョンは、次のように機能します。

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'] 

プロキシインスタンスがPaginatorに渡すことができ、必要に応じてESへのリクエストを行います。

0

Danielle Madeleからのアドバイスに従って、django-elasticsearch-dsl==0.4.4の最新バージョンでうまく機能する検索結果を得るためのプロキシーも作成しました。

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) 

DjangoのLazyObjectプロキシ_wrapped属性に割り当てられたオブジェクトのすべての属性とメソッド:

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 

次に、あなたはこのような検索ビューでそれを使用することができます。私はDjangoのページエディタで必要とされるいくつかのメソッドをオーバーライドしていますが、Search()インスタンスでは動作しません。