2016-03-09 7 views
5

मैं elasticsearch डीएसएल पर 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 पर इस paginator पाया:

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) 

और फिर ध्यान में रखते हुए मैं का उपयोग करें:

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' वापस करने के लिए पेजिनेटर की' count' cached_property को ओवरराइड कर सकते हैं – Nasir

0

एक और तरीका है आगे बनाने के लिए है Paginator और लोचदार खोज क्वेरी के बीच एक प्रॉक्सी। Paginator दो चीजों की आवश्यकता है, __len__ (या count) और __getitem__ (जो एक टुकड़ा लेता है)। प्रॉक्सी का मोटे तौर संस्करण इस तरह काम करता है:

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

डेनियल मेडली की सलाह के बाद, मैंने django-elasticsearch-dsl==0.4.4 के नवीनतम संस्करण के साथ अच्छी तरह से काम करने वाले परिणामों को खोजने के लिए प्रॉक्सी भी बनाई। _wrapped विशेषता करने के लिए सौंपा वस्तु से

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 प्रॉक्सी सभी विशेषताओं और तरीके:

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 के पेजिनेटर द्वारा आवश्यक कुछ विधियों को ओवरराइड कर रहा हूं, लेकिन खोज() उदाहरणों के साथ बॉक्स से बाहर काम नहीं करता हूं।

संबंधित मुद्दे