基于django+elasticsearch的全文检索

​ 关于django和elasticsearch,就不做过多的介绍了(内容太多啦,哈哈哈哈)。直接进入正题。

一、django+haystack+elasticsearch

​ 了解过django+elasticsearch全文检索的,可能还听说过python另外一个搜索框架:haystack,这是python里面自带的一个搜索引擎,通常情况下可以满足一些基本的需求,相对来说功能也是比较强大的。当然haystack还有一个作用就是接入第三方的搜索引擎elasticsearch,solr,whoosh等。具体可能就是需要对settings里面进行安装和配置:

一、安装
$ pip install django-haystack # 官方支持弹性1.x、2.x和5.x,所以es版本不能太高
$ pip install elasticsearch==x.x.x #这里安装的版本需要注意了,一定是比服务器版本的es低的,根据自己部署的es版本来下载客户端
二、应用注册和配置
#应用注册
INSTALLED_APPS = [# 全文检索'haystack',
]# Haystack接入Elasticsearch
HAYSTACK_CONNECTIONS = {'default': {'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine','URL': 'http://xxx.xxx.xxx.xxx:9200/',  # Elasticsearch服务器ip地址,端口号固定为9200'INDEX_NAME': 'XXX',  # Elasticsearch建立的索引库的名称},
}#这俩个配置就是属于后期配置了
# 当添加、修改、删除数据时,自动生成索引
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'# 用于决定每页显示数据条数:
HAYSTACK_SEARCH_RESULTS_PER_PAGE = 5
三、路由配置
# Haystack 注册
url(r'^search/', include('haystack.urls')),
四、数据库配置
DATABASES = {'default': {'ENGINE': 'django.db.backends.mysql',  # 数据库引擎'HOST': '127.0.0.1',  # 数据库主机'PORT': 3306,  # 数据库端口'USER': 'xxx',  # 数据库用户名'PASSWORD': 'xxx',  # 数据库用户密码'NAME': 'xxx'  # 数据库名字},
}
五、 Haystack 建立数据索引

1.创建索引类

  • 通过创建索引类,来指明让搜索引擎对哪些字段建立索引,也就是可以通过哪些字段的关键字来检索数据。
  • 在项目中对 需要进行信息全文检索的表进行建立索引类,所以在 对应的应用中新建 search_indexes.py 文件,用于存放索引类。这个文件名是固定的哦!
from haystack import indexesfrom es.models import EveryoneDemoclass File_SearchIndex(indexes.SearchIndex, indexes.Indexable):"""索引数据模型类"""text = indexes.CharField(document=True, use_template=True)def get_model(self):"""返回建立索引的模型类"""return EveryoneDemo# 针对哪些数据进行查询def index_queryset(self, using=None):"""返回要建立索引的数据查询集"""#return self.get_model().objects.filter()return self.get_model().objects.all()

索引类 SKUIndex 说明:

  • 在 EveryoneDemo建立的字段,都可以借助 HaystackElasticsearch 搜索引擎查询。
  • 其中 text 字段我们声明为 document=True,表名该字段是主要进行关键字查询的字段。
  • text字段的索引值可以由多个数据库模型类字段组成,具体由哪些模型类字段组成,我们用 use_template=True 表示后续通过模板来指明。

创建 text 字段索引值模板文件

  • templates 目录中创建 text 字段使用的模板文件
  • 具体在 templates/search/indexes/es/everyonedemo_text.txt 文件中定义
{{ object.字段名 }}
{{ object.字段名 }}
{{ object.字段名 }}
....

模板文件说明:当将关键词通过text参数名传递时

  • 此模板指明 EveryoneDemo 的某三个字段作为text字段的索引值来进行关键字索引查询。
  • es是我的应用名,everyonedemo_text.txt,是模型类名小写_text.txt这是固定的,不要乱改
#生成模型文件和进行数据迁移
python manage.py makemigrations
python manage.py migrate#手动生成索引
python manage.py rebuild_index

前端部分就是直接根据路"/search/"去提交搜索咯,这里就不说了。

六、前端部分的补充

​ 上面有介绍分页,在这就补充一下代码(有点乱啊哈哈哈哈)

准备搜索页分页器

<div class="search_list"><ul class="search_result_list">{% if query %}{% for result in page %}<div class="search_res">{#                    <a href="file:///{{ result.object.full_path }}" id="a1">#}<li class="li_style">文件名:{% highlight result.object.file_name with query %}<br>{#                            标签名:{% highlight result.object.F_Key with query %}<br>#}提交时间:{{ result.object.ctime }}<br>文件详情:{% highlight result.object.text_info with query %}<br><a href="{{ result.object.full_path }}">连接地址</a><br></li></div>{% empty %}<p>没有找到你要信息</p>{% endfor %}{% else %}请输入搜索关键词,例如 10月5日文件,可用空格进行分词搜索!{% endif %}</ul><div class="pagenation"><div id="pagination" class="page"></div></div>
</div>
<script type="text/javascript" src="{% static 'js/jquery.pagination.min.js' %}"></script>
<script>$(function () {$('#pagination').pagination({currentPage: {{ page.number }},totalPage: {{ paginator.num_pages }},callback: function (current) {{#window.location.href = '/search/?q=iphone&amp;page=1';#}window.location.href = '/search/?q={{ query }}&page=' + current;}})});
</script>

然后就没了!!!!

二、django+elasticsearch

​ 呵呵。。好像跟上一段没啥区别哦,所以上述不是重点,就是废话,哈哈哈哈。简单使用ES的话,上述就够了,有点ES基础得可以继续学习咯!因为不用haystack这个框架,很多繁杂得事情需要我们自己去处理了。个人经验告诉我,不用这个框架写起来更舒服,哈哈哈哈。

还是一个django项目,并进行配置,数据库配置同上,然后就是应用的注册。这一次es就不用配置了。

​ 然后就是直接进行创建索引映射,上代码:

一、索引的创建与映射
def es2(request):es = Elasticsearch(['http://xxx.xxx.xxx.xxx:9200/'])# "analyzer": "ik_smart"body = {"mappings": {"doc": {"properties": {"file_name": {"type": "text","analyzer": "ik_smart"},"full_path": {"type": "text","index":"false"},"ctime": {"type": "date","format": "strict_date_optional_time||epoch_millis"},"text_info": {"type": "text","analyzer": "ik_smart"},}}},"settings": {"number_of_shards": 2, #分片数"number_of_replicas": 0 #副本数},}es.indices.create(index='索引名', body=body)return HttpResponse('ok')
  • 分片数和副本数设计的作用就是提高我们的查询速度,如果查询的数据很大的话分片数就增加,默认是5个分片,当然我用不了那么多,大概了解了一下如果你有100G的数据,大概就给5个分片左右,以此类推。副本数,就等于是一个备份数据一样,当然我用不到,其次就是,单个服务器就是0哦,es集群可以考虑副本。

  • 如果你的es中配置了ik中文分词器呢,在建立映射的时候可以指定指定进行ik分词:“analyzer”: “ik_smart”,不指定的话默认是官方的简单分词器。也可以指定keyword类型,是不分词的,具体场景下,某些词,确实不分词的好,“index”,的属性具体有三,analyzednot_analyzedfalse/no, analyzed:表示该字段被分析,编入索引,产生的token能被搜索到;not_analyzed:表示该字段不会被分析,使用原始值编入索引,在索引中作为单个词;no:不编入索引,无法搜索该字段;

  • 时间类型的字段,这里用的是:“format”: "strict_date_optional_time||epoch_millis"如:“2020-01-01T1:10:30Z” 还可以是yyyy-MM-dd,yyyy-MM-dd mm:ss等等格式 。当然还有很多复杂的数据类型,大家可以自行了解。

  • 这个创建索引映射的函数我是写在view视图中的,这里按照大家的喜欢来写吧,es的东西太多了,可以单独去学习一下。

  • 如果想看到自己创建的索引,可以去下载安装elasticsearch-head-master,需要的环境很复杂,可参考: https://www.cnblogs.com/hualess/p/11540477.html 博主大哥写的特别详细。

  • 关于es的用法可见:https://elasticsearch-py.readthedocs.io/en/master/api.html 。dsl的可见:https://elasticsearch-dsl.readthedocs.io/en/latest/,这里针对的是python,es关于python的博客太少了。

二、helpers.bulk写入数据
def  es2(request):.....query_obj = models.模型类名.objects.all()#action = [{"_index": "上面创建的索引名","_type": "doc","_source": {"file_name": i.file_name,"full_path": i.full_path,"ctime": i.ctime,"text_info": i.text_info}} for i in query_obj]# 批量写入数据helpers.bulk(es, action, request_timeout=1000)return HttpResponse('ok')
  • 批量模型类里面指定好的数据库表,我这里用的是MySQL,SQL server也是可以的哦。这里也可以直接通过上传es,不通过python。具体做法,网上很丰富的呢!
三、搜索接口设计

懒得讲解了就简单粗暴一些吧(哈哈哈哈),有不懂可以私聊咯:

1、前端js部分:
function show(page) {var searchMsg = $('#searchMsg').val();$.ajax({url: "/everyone/",type: "POST",data: {"search_msg": searchMsg},success: function (data) {if (data) {//显示结果条数$('#totalNum').html("<b style='color:red'>约" + data.hits.total + "条</b>");//结果展示var html = '';$.each(data.data_msg, function (index, item) {html +='<div class = "res_show">\n' +'<h3>' + item._source.file_name + '</h3>\n' +'<p>' + item._source.ctime + '</p>' +'<p>' + item._source.text_info + '</p>\n' +'</div>';});$('#showData').html(html);}},})
}
  • ajax发送请求给后端,并传递搜索的关键字参数searchMsg,后端进行es的搜索。
  • 返回的结果进行HTML页面字符串拼接。在放入我们指定的showData的div中。
2、html部分
<div class="search_test"><input type="text" class="input_text" name="q" placeholder="搜索文件" id="searchMsg"><input type="submit" class="input_btn1" name="" value="搜索" οnclick="show(page)"><span id="totalNum"></span>
</div>
3、路由部分
urlpatterns = [path('everyone/', views.everyone, name="everyone"),
]
4、视图函数部分
def everyone(request):if request.method == "POST":search_msg = request.POST.get("search_msg")res = filter_msg(search_msg, "everyone_all")return JsonResponse(res)else:return render(request, "everyone.html")
  • 通过前端的ajax请求拿到search_msg,并通过filter_msg函数进行搜索。
5、es的搜索部分
def filter_msg(search_msg, search_data):es = Elasticsearch(['http://xxx.xxx.xxx.xxx:9200/'])body = {"size": 200,"query": {"bool": {"should": [{"match": {"file_name": search_msg,}},{"match": {"text_info": search_msg,}},]}},}res = es.search(index=search_data, body=body)return res
  • es搜索方式很多哦,这里也是最常用的bool综合类型的查询,其关键方法有should,must,not must具体用法很多。也比较普遍。其他查询方式贼多。
四、关键字高亮显示

​ 关键字高亮是一个搜索引擎很常见也很必要的功能,我的简单理解就是将查询后的结果进行渲染,拿我们的HTML标签将关键字包裹起来,在进行颜色的渲染,所以就是将搜索部分的函数filter_msg进行添加一个高亮的参数设置。

"highlight": {"pre_tags": ["<font style='color:red;font-size:20px'>"],"post_tags": ["</font>"],"fields": {"file_name": {"fragment_size": 20, "no_match_size": 10},"text_info": {"fragment_size": 20, "no_match_size": 10},}}
  • 前端部分就是将原先的_source,替换为highlight
  • ES的内置高亮分为三种:plain、postings、fast-vector-highlighter,考虑到各位的任性,ES也支持自定义标签高亮。良心啊!但是这三种高亮也是有特别之处的,例如fast-vector-highlighter,就是专门针对长文本出的高亮,总体来说,ES的搜索速度有一部分原因耽误在高亮渲染的过程上,也就是我们处理高亮的速度,fast-vector-highlighter:为解决 highlighter 高亮器质大文本字段上高亮速度跟不上的问题,lucene高亮模块提供了基于向量的高亮方式 fast-vector-highlighter(也称为fvh)。fvh高亮器利用建索引时候保存好的词向量来直接计算高亮段落,在高亮过程中比plain高亮方式少了实时分析过程,取而代之的是直接从磁盘中将分词结果直接读取到内存中进行计算。故要使用fvh的前置条件就是在建索引时候,需要配置存储词向量,词向量需要包含词位置信息、词偏移量信息。上述不是我说的!哈哈哈哈!这么专业的话。我不会!
五、排序搜索

​ 同上啊,一样的思路。es语法中,排序就是指定一个sort的字段,并指明排序方式。这里我是按照时间进行降序。排序的接口肯定是单独写的,类比于点击搜索一样的思路。

"sort": [{"ctime": {"order": "desc",}}
]

根据业务场景来记录,多级排序得实现:_score就是es得评分机制,先按照文档分数排序,再根据时间排序,这个方式还是比较好的。

 "sort": [{"_score": {"order": "desc"}},{"ctime": {"order": "desc", }},]
六、搜索分页

​ 这里因为没用haystack,所以自己得写一个分页器。底下是分页器得函数,当然这不是固定的,网上很多种分页器函数,可以自己参考一下。

class Pagination():def __init__(self, current_page, all_count, per_num=8, max_show=10):try:# self.current_page = int(request.GET.get('page', 1))self.current_page = int(current_page)if self.current_page <= 0:self.current_page = 1except Exception as e:self.current_page = 1# 最多显示的页码数self.max_show = max_showhalf_show = max_show // 2# 每页显示的数据条数self.per_num = per_num# 总数据量self.all_count = all_count# 总页码数self.total_num, more = divmod(all_count, per_num)if more:self.total_num += 1# 总页码数小于最大显示数:显示总页码数if self.total_num <= max_show:self.page_start = 1self.page_end = self.total_numelse:# 总页码数大于最大显示数,最多显示5个if self.current_page <= half_show:self.page_start = 1self.page_end = max_showelif self.current_page + half_show >= self.total_num:self.page_end = self.total_numself.page_start = self.total_num - max_show + 1else:self.page_start = self.current_page - half_showself.page_end = self.current_page + half_show@propertydef start(self):return (self.current_page - 1) * self.per_num@propertydef end(self):return self.current_page * self.per_num@propertydef show_li(self):# 存放li标签的列表html_list = []# first_li = '<li><a href="{}?page=1">首页</a></li>'.format(self.base_url)# html_list.append(first_li)if self.current_page == 1:prev_li = '<li class="disabled"><a>上一页</a></li>'else:prev_li = '<li ><a href="#" page={}>上一页</a></li>'.format(self.current_page - 1)html_list.append(prev_li)for num in range(self.page_start, self.page_end + 1):if self.current_page == num:li_html = '<li class = "active"><a href="javascript:;" page={0}> {0}</a></li>'.format(num)else:li_html = '<li><a href="javascript:;" page={0}>{0}</a></li>'.format(num)html_list.append(li_html)if self.current_page == self.total_num:next_li = '<li class="disabled"><a>下一页</a></li>'else:next_li = '<li><a href="javascript:;" page={}>下一页</a></li>'.format(self.current_page + 1)html_list.append(next_li)return ''.join(html_list)
  • 这里的分页是与前文的搜索部分一样的接口,我这里是这么理解的。当我们传入,search_msg与current_page时,可以考虑当当前页默认为1时,肯定代表的就是搜索,当当前大于1时,就是所谓的翻页,在show_li中已经将这样的一个结果进行前端页面的字符串拼接,所以前端拿到之后可以直接渲染展示,作为一个后端程序员,即便是拼接,咱也得在后端进行拼接。哈哈哈哈!
七、搜索建议器
  • 关于建议器,可分为三类,词条建议器:term suggest、词组建议器:phrase suggester、完成建议器:completion suggester,上下文建议器:context suggester,具体实现都差不多,前俩个类似于是一种纠错功能,第三种就是提供自动完成得功能。第四个就是对于第三种得完善,作为一个合格搜索引擎,了解用户想搜什么与纠正用户的错误还是很有必要的。(版本较低的es可能不适用,建议升级下版本5.X以上吧)

这里举例一个完成建议器:

def suggest(search_msg, search_data):body = {"suggest": {"my_sugget": {"text": search_msg,"completion": {"field": "subject.suggest","size": 5}}}}try:res = es.search(index=search_data, body=body)res = [i['text'] for i in res['suggest']['my_sugget'][0]['options']]except Exception as e:passreturn res

如果想要做completion建议器,建立映射的时候,也要给这个字段加上completion 类型,才能使用哦!term ,phrase 很简单就可以实现,想要了解的可以自行百度。哈哈哈哈!

 body = {"mappings": {"doc": {"properties": {"subject": {"type": "text","fields": {"suggest": {"type": "completion",# "analyzer": "standard",}}}}}}}

至此,就结束了,有点突然的结束哈!就是写累了。

基于django+elasticsearch的全文检索相关推荐

  1. 浅入Elasticsearch(全文检索服务器)

    目录 系列文章目录 一.首先是我们为什么要用Elasticsearch 二.Elasticsearch的介绍 三.Elasticsearch的原理 四.ES客户端 五.Es客户端的安装 5.1 安装 ...

  2. es全文检索论文_浅谈使用ElasticSearch实现全文检索

    浅谈使用ElasticSearch实现全文检索 IDEADATA-iDSP-Yeun   2016年5月15日 现如今,无论是互联网信息还是企业内部每天产生的信息,都在以指数级增长,对于企业内部,每天 ...

  3. 【谷粒商城高级篇】Elasticsearch:全文检索

    谷粒商城笔记合集 分布式基础篇 分布式高级篇 高可用集群篇 ===简介&环境搭建=== ===Elasticsearch=== 项目简介与分布式概念(第一.二章) Elasticsearch: ...

  4. 【毕业设计之python系列】基于django的奶茶店管理系统

     基于django的奶茶店管理系统  摘 要 近年来,奶茶的受欢迎程度在全球范围内迅速增长.随着奶茶店数量的增加,管理这些商店变得越来越复杂.店主需要同时处理库存.订单.员工和客户等各种任务,有效地管 ...

  5. 在IIS上部署基于django WEB框架的python网站应用

    django是一款基于python语言的WEB开源框架,本文给出了如何将基于django写的python网站部署到window的IIS上. 笔者的运行环境: Window xp sp3 IIS 5.1 ...

  6. 【Django】基于Django架构网站代码的目录结构---转载

    经典的Django项目源码目录结构 Django在一个项目的目录结构划分方面缺乏必要的规范.在Django的官方文档中并没有给出大型项目的代码建议目录结构,网上的文章也是根据项目的不同结构也有适当的变 ...

  7. python做一个考试系统_1218Python基于Django在线考试系统设计

    如果你满意这个设计可以分享到: 以下是本计算机毕业设计介绍,若对此项目感兴趣,请联系客服QQ:344245001 [运行截图请往下看]编程语言:Android.iOS.C#.Asp/Asp.net.J ...

  8. 基于django和vue的xdh官网设计

    前言 本项目是使用三段分离的设计 前台 使用materialize框架搭建的前台页面,后端使用的django写的接口 后台 使用Amazon UI 模板搭建的界面,管理各个部分的内容 项目环境 pyt ...

  9. python开发的著名软件公司_软件开发公司_软件外包_项目外包平台基于Python开发一个全文检索系统...

    基于Python开发一个全文检索系统.功能要求为: 使用全文检索引擎对文本进行检索.文本的格式为Word.PDF.TXT. 同时按数据域进行复合条件检索.数据域指文本对应的信息,例如创建人.文件编号. ...

最新文章

  1. 欢迎大家批评:CSDN Blog用户体验调查
  2. RAC 实例不能启动 ORA-1589 signalled during ALTER DATABASE OPEN
  3. JVM 垃圾回收算法 -可达性分析算法!!!高频面试!!!
  4. mysql 值为0 但却被认为null_MySQL介于普通读和锁定读的加锁方式
  5. 《推荐系统笔记(四)》svd的python计算实例
  6. 测试python安装成功_Python在Windows上安装配置测试
  7. 同一个项目,项目名称不一致,这两个项目同时在Eclipse中出现
  8. 文章学习_基于HowNet 的词汇语义倾向计算
  9. Linux/Ubuntu20 安装 TP-link(RTL8812AU) 无线网卡驱动
  10. linux中echo是什么意思中文,在Linux操作系统中Echo的用法
  11. 嫌我们理科生不够浪漫?你有一份音乐数学入门指南待查收
  12. 菜菜的刷题日记 | 215. 数组中的第K个最大元素
  13. 在浏览器输入URL,按下回车之后的流程?
  14. C语言的飞机订票系统
  15. mantis 重启mysql_机器突然重启导致Mantis错误
  16. 电脑USB接口实现鼠标和键盘功能
  17. Android 求时间差
  18. Win10链接XP共享文件夹
  19. 基于Springboot的民宿管理平台
  20. 手机root后安装xposed框架后,如何启动云闪付?

热门文章

  1. python colors后面_Python colors.BASE_COLORS属性代码示例
  2. 统计遗传学:第一章,基因组基础概念
  3. 新闻管理系统页面代码 php,PHP实战 新闻管理系统 使用到了bootst_php
  4. .Net(C#)汉字和Unicode编码互相转换
  5. 独家 | 一文读懂社交网络分析-上(附学习资源)
  6. Log.v,Log.d,Log.i,Log.w,Log.e的用法
  7. 树莓派系列二(语音识别)
  8. win7下启动linux系统下载软件,WIN7+LINUX双系统启动
  9. 评论2月1日凯尔特人与湖人之战
  10. 和程序员约会的10个理由