2016-03-09 12 views
5

Come posso usare l'impaginazione django su elasticsearch dsl. Il mio codice:Python elasticsearch-dsl django impaginazione

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) 

Qualsiasi soluzione per questo?

risposta

9

ho trovato questo paginator su questo 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) 

e poi in considerazione che uso:

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) 

questo modo funziona perfettamente ..

+0

la proprietà 'count' in questo esempio mostra solo il numero di elementi nella pagina non il totale. puoi sovrascrivere il 'count' cache_property del paginator per restituire' _count' come il conteggio totale – Nasir

0

Un altro modo di procedere è quello di creare un proxy tra Paginator e la query Elasticsearch. Paginator richiede due cose, __len__ (o count) e __getitem__ (che accetta una porzione). Una versione grezza del proxy funziona così:

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

Un'istanza delega può essere passato a Paginator e farà richieste di ES, se necessario.

0

Seguendo il consiglio di Danielle Madeley, ho anche creato un proxy per cercare risultati che funziona bene con l'ultima versione di django-elasticsearch-dsl==0.4.4.

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 

Quindi è possibile utilizzare nel vostro vista ricerca come questa:

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) 

di Django LazyObject deleghe tutti gli attributi e metodi dall'oggetto assegnato all'attributo _wrapped. Sto scavalcando un paio di metodi che sono richiesti dall'impaginatore di Django, ma non funzionano immediatamente con le istanze di Search().