2016-03-09 9 views
5

Wie kann ich django Paginierung auf elasticsearch dsl verwenden. Mein Code:Python elasticsearch-dsl django Paginierung

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) 

Jede Lösung für dieses?

Antwort

9

fand ich diesen paginator auf diesem 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) 

und dann im Hinblick auf die ich benutze:

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) 

diese Weise ist es perfekt funktioniert ..

+0

Die 'count'-Eigenschaft in diesem Beispiel zeigt nur die Anzahl der Elemente auf der Seite an, nicht die Summe. Sie können die cached_property "count" des Paginators überschreiben, um '_count' als Gesamtanzahl zurückzugeben – Nasir

0

Ein anderer Weg ist, zu schaffen ein Proxy zwischen der Paginator und der Elasticsearch-Abfrage. Paginator erfordert zwei Dinge, __len__ (oder count) und __getitem__ (das dauert eine Scheibe). Eine grobe Version des Proxy funktioniert wie folgt:

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

Ein Proxy-Instanz Paginator weitergegeben werden können und Anfragen ES machen je nach Bedarf.

0

Dem Rat von Danielle Madeley folgend, habe ich auch einen Proxy für die Suchergebnisse erstellt, der gut mit der neuesten Version von django-elasticsearch-dsl==0.4.4 funktioniert.

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) 

Djangos LazyObject Proxies alle Attribute und Methoden aus dem auf dem _wrapped Attribut zugewiesen Objekt:

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 

Dann können Sie es in Ihre Suche wie folgt verwenden. Ich überschreibe ein paar Methoden, die von Djangos Paginator benötigt werden, aber nicht mit den Search() - Instanzen.