提高Django的搜索性能
elasticsearch部署在docker中
至於elasticsearch的基礎知識,就不多說,有豐富的文檔,供君閱讀。
建立索引數據模型
這裏使用的是python中的elasticsearch_dsl,比較友好。使用ik中文分詞插件,來進行分詞,中文搜索。數據模型,與django中的model相對應,這裏只選用用於搜索的關鍵字,存入索引庫。
class AuthorComment(InnerDoc):
id = Text()
name = Text()
class Course(Document):
id = Text()
name = Text(analyzer="ik_smart", search_analyzer="ik_smart", fields={'name': Keyword()})
class Index:
name = 'course'
settings = {
"number_of_shards": 2,
}
Django數據發生變化時,同步到elasticsearch
這裏利用Django提供的信號機制實現同步,如果有其他需要,可以自己寫相應的信號來處理。
@receiver(post_save, sender=ModelCourse)
def post_save_course(sender, **kwargs):
try:
instance = kwargs.get('instance')
created = kwargs.get('created')
para = {
'name': instance.name,
'img': instance.img,
}
if not created:
course = Course.get(id=instance.id)
course.update(**para)
else:
para['id'] = instance.id
para['meta'] = {'id': instance.id}
course = Course(**para)
course.save()
except BaseException as e:
logger.error(e)
logger.error('{} 添加一對一索引失敗'.format(kwargs))
建立搜索模型
這裏僅僅使用name字段搜索
class CourseSearch(FacetedSearch):
index = 'course'
doc_types = Course
fields = ['name', ]
def search(self):
s = super(CourseSearch, self).search()
return s.filter('term', strike=False)
建立序列化器
class CourseSerializer(serializers.Serializer):
id = serializers.CharField(read_only=True)
name = serializers.CharField(read_only=True)
class Meta:
fields = '__all__'
建立視圖
這裏的分頁模型,均是從Django rest frame庫中摘抄,見諒,見諒
from rest_framework.response import Response
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.settings import api_settings
from .search import CourseSearch
from .serializers import CourseSerializer
class SearchViewSet(viewsets.ViewSet):
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
@property
def paginator(self):
"""
The paginator instance associated with the view, or `None`.
"""
if not hasattr(self, '_paginator'):
if self.pagination_class is None:
self._paginator = None
else:
self._paginator = self.pagination_class()
return self._paginator
def paginate_queryset(self, queryset):
"""
Return a single page of results, or `None` if pagination is disabled.
"""
if self.paginator is None:
return None
return self.paginator.paginate_queryset(queryset, self.request, view=self)
def get_paginated_response(self, data):
"""
Return a paginated style `Response` object for the given output data.
"""
assert self.paginator is not None
return self.paginator.get_paginated_response(data)
@staticmethod
def get_result(data):
data = data['_source']
try:
author = data['author']
data['author'] = author[0]
except KeyError:
pass
return data
@action(methods=['get'], detail=False)
def course(self, request):
name = self.request.query_params.get('name', None)
bs = CourseSearch(name)
response = bs.execute()
data = response.hits.hits[0:100]
data = list(map(self.get_result, data))
page = self.paginate_queryset(data)
serializer = CourseSerializer(page, many=True)
data = self.get_paginated_response(serializer.data)
return Response({'message': '獲取成功', 'status': status.HTTP_200_OK, 'data': data})