需求分析:
可以使用数据库的模糊查询(like关键字)来实现,但效率极低
在多个字段中查询,使用like关键字不方便
因此使用搜索引擎来实现全文检索 (ES)
搜索引擎原理:
搜索引擎并不是直接在数据库中进行查询
会对数据库中的数据进行一遍预处理,单独建立一份索引结构数据
类似字典的索引检索页
Elasticsearch:
开源
搜索引擎
首选底层是开源库Lucene
REST API 的操作接口
注意
搜索引擎在对数据构建索引时,需要进行分词处理。分词是指将一句话拆解成多个单字或词,这些字或词便是这句话的关键词。
Elasticsearch 不支持对中文进行分词建立索引,需要配合扩展elasticsearch-analysis-ik来实现中文分词处理。
使用docker安装elasticsearch
docker下载images
docker image pull delron/elasticsearch-ik:2.4.6-1.0
dockers load -i 也行
修改配置
在虚拟机中的elasticsearch/config/elasticsearch.yml第54行,更改ip地址为0.0.0.0,端口改为9200,默认端口为9200
# network.host: 172.18.168.123 network.host: 0.0.0.0 # # Set a custom port for HTTP: # http.port: 9200 阿里云的话去转
创建docker容器并运行
冒号前半段是本地配置 后半段是docker的
docker run -dti --network=host --name=elasticsearch -v /home/pyvip/elasticsearch/config:/usr/share/elasticsearch/config delron/elasticsearch-ik:2.4.6-1.0
如果上面的不稳定,经常EXIT。那么可以使用下面这个进行启动
docker run -dti --name=elasticsearch -p 9200:9200 delron/elasticsearch-ik:2.4.6-1.0
使用elasticsearch
进入项目虚拟环境中,安装相关包
# 进入项目虚拟环境 workon 【环境】 # 如果安装报错,先初始化 pip3 install setuptools_scm pip3 install django-haystack pip3 install elasticsearch==2.4.1
在Django里面的settings.py文件中加入如下配置:
# 添加app功能
INSTALLED_APPS = [ 'haystack', ] # Haystack引擎 HAYSTACK_CONNECTIONS = { 'default': { 'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine', # 引擎 'URL': 'http://192.168.216.137:9200/', # 此处为elasticsearch运行的服务器ip地址,端口号默认为9200!!!!!!!!!! 'INDEX_NAME': 'site', # 指定elasticsearch建立的索引库的名称 !!!!!! 这里千万不能大写,统一小写,不然会出错的 }, } # 设置每页显示的数据量 HAYSTACK_SEARCH_RESULTS_PER_PAGE = 5 # 当数据库改变时,会自动更新索引 HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
后端实现elasticsearch模块
建立索引index
from haystack import indexes
from news.models import News
# 结构必须是跟models完全一直的 , Index必须是大写
# SearchIndex和Indexable 都是index提供给我们的类
# 类的命名是有要求的,首字母都是要大写
# 如果是Comment的话那就是CommentIndex
# 继承两个类Django的haystack提供的搜索基类
# SearchIndex 是让模型和搜索引擎进行交互
class NewsIndex(indexes.SearchIndex, indexes.Indexable):
"""
News索引数据模型类
"""
# 类 与 News模型进行一致
# 搜索是需要模板的 template就是 Elasticsearch 和 haystack进行交互的字段
# document 为与模型一致化的解析
# use_template 是搜索需要模板
text = indexes.CharField(document=True, use_template=True) # 很重要
# 搜索引擎的字段与模型当中哪一个字段进行匹配。
id = indexes.IntegerField(model_attr='id')
title = indexes.CharField(model_attr='title')
digest = indexes.CharField(model_attr='digest')
content = indexes.CharField(model_attr='content')
image_url = indexes.CharField(model_attr='image_url')
def get_model(self):
"""
返回建立索引的模型类
我们的索引类是使用哪一个模型的再去模型里面找对应model_attr
:return:
"""
return News
# 返回一个查询集
def index_queryset(self, using=None):
"""
返回要建立索引的数据查询表
:param using:
:return:
self.get_model()返回News
"""
return self.get_model().objects.filter(is_delete=False)
# 在manage中执行如下命令,生成索引
python manage.py -h 可以查找关于haystack的命令 重新建立
python manage.py rebuild_index
更新命令
pyhton manage.py update_index
这里的路径千万不能错! news_text.txt 里面的news是模型的小写形式 如果这里写错,rebuild_index会寻找不了news_text.txt
# 创建templates/search/indexes/news/news_text.txt文件(文件名为:模型_text.txt) # 此模板指明当将关键词通过text参数名传递时,可以通过news 的title、digest、content 来进行关键字索引查询 {{ object.title }} {{ object.digest }} {{ object.content }}
视图逻辑功能:
1. 判断接受参数q(默认为q)是否有值,并且传入show_all里面。有则调用父类create_response(下面有源码介绍),没有则直接展示热门信息或者其他自定义。
2. 重写template
3. 使用paginator
from haystack.views import SearchView as _SearchView
class SearchView(_SearchView): # 重写template template = 'news/search.html' # 重写响应方式,如果请求参数为空, 返回模型News的热门新闻数据,否则就根据q是什么再去查 def create_response(self): default_page = 1 # q是haystack定义好的 kw = self.request.GET.get('q', '') # 是否Show_all # 以下定义的变量paginator page 都不能改 if not kw: show_all = True hot_news = models.HotNews.objects.select_related('news').only('news_id', 'news__title', 'news__image_url'). filter(is_delete=False).order_by('priority') paginator = Paginator(hot_news, HAYSTACK_SEARCH_RESULTS_PER_PAGE) try: page = paginator.page(int(self.request.GET.get('page', default_page))) # 防止传的是a,b,c except PageNotAnInteger: page = paginator.page(default_page) except EmptyPage: # 如果访问的是大于最后一页,那么返回最后一页的数据 num_pages就是总页数,也就是最后一页 page = paginator.page(paginator.num_pages) return render(self.request, self.template, locals()) else:
# 源码context = { # 'query': self.query, # 'form': self.form, # 'page': page, # 'paginator': paginator, # 'suggestion': None, # } # 父类的create_response已经帮你做好了 将上面的context 放到template当中 # 这上面的query就是个他妈的关键字 show_all = False queryset = super(SearchView, self).create_response() return queryset