django elasticsearch+docker实现搜索功能接口

需求分析:

可以使用数据库的模糊查询(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
原文地址:https://www.cnblogs.com/jackson669/p/12559634.html