Django全文搜索引擎
背景
解决问题
网站首页提供了搜索框,这样的搜索框是为了在数据库进行数据检索的,但是如果直接在数据库中使用like进行查询数据会非常的慢,因此考虑到引入了全文检索的概念,借助搜索引擎能够对文字进行分词,并构建起文字对应的数据库中信息的关系,所以要比模糊查询的效率高很多。
如果我们自己使用搜索引擎,也是可以实现全文检索,但是其中可能要繁琐一些,所以我们可以借用已经写好的全文搜索框架,然后搭配对应的搜索引擎即可
使用框架
django-haystack:全文搜索框架,支持whoosh、solr、Xapian、Elasticsearc四种全文检索引擎,参考:http://haystacksearch.org/
whoosh:纯python编写的搜索引擎,性能偏低,但是很稳定,参考:https://whoosh.readthedocs.io/en/latest/
jieba:免费的中文分词包,中文分词包也有收费的,可以自取哟。
使用
安装环境包
1
2
3
4
5
6
7django-haystack依赖环境
pip install setuptools_scm wheel
按照包
pip install django-haystack
pip install whoosh
pip install jieba修改django中settings.py配置文件,配置搜索引擎
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21# 将django-haystack包注册到apps中
INSTALLED_APPS = (
...
'haystack',
)
# 配置中文搜索引擎
HAYSTACK_CONNECTIONS = {
'default': {
# 使用whoosh引擎,这个whoosh_cn_backend文件对应的是django虚拟环境中文件的位置
'ENGINE': 'haystack.backends.whoosh_cn_backend.WhooshEngine',
# 配置索引文件生成的路径,该文件夹不用我们创建,运行的时候会自动创建
'PATH': os.path.join(BASE_DIR, 'whoosh_index'),
}
}
# 配置当添加、修改、删除数据时,自动生成索引
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
# 配置全文搜索每次返回的搜索结果条数,默认是20条,如果配置了,就按照配置的显示
HAYSTACK_SEARCH_RESULTS_PER_PAGE=1在对应的app下创建生成索引的py文件,文件名就叫【search_indexes.py】,这个名称是固定的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25# 定义索引类
from haystack import indexes
# 导入需要生成索引的模型类
from goods.models import GoodsSKU
# 指定对于某个类的某些数据建立索引,这一块的内容都是固定的额,所以直接用即可
# 索引类名格式: 模型类名+Index
class GoodsSKUIndex(indexes.SearchIndex, indexes.Indexable):
# text:索引字段,use_template=True表示根据表中的哪些字段建立索引文件的说明,将其放在一个文件中
# 该文件就是我们一会要编辑的文件
text = indexes.CharField(document=True, use_template=True)
def get_model(self):
# 返回原本的模型类
return GoodsSKU
# 建立索引的数据
def index_queryset(self, using=None):
return self.get_model().objects.all()
# 这主要是一个关联操作,定义我们的数据表的model的索引类
# text:该字段是必须的,
# document=True:只能有一个,
# use_template =true:表示使用模板在静态文件的文件夹【templates】下,依次创建文件,路径格式必须按照:templates/search/indexes/【App名称】/【模型类名小写】_text.txt。如果不通过默认的路径定义,则创建索引类的时候使用template_name指定使用的模版文件
1
2mkdir -p templates/search/indexes/goods/
touch templates/search/indexes/goods/goodssku_text.txt修改索引文件生成模版的内容,用于生成指定的模版文件
1
2
3
4
5
6
7# 根据表中的指定子段生成索引数据
# 格式:object.子段名
{{ object.name }}
{{ object.desc }}
# 同时它也支持通过外键建立索引之间的联系,type是外键名
{{ object.type.detail }}修改网站模板中搜索输入框
1
2
3
4
5
6
7{# 搜索的输入框必须包裹在form表单中,method必须是get,action可以自己定 #}
{# q其实就是提交的关键字,还有一个page就是传递的页码 #}
<form method="get" action="/search">
{# 搜索的输入框的name值必须为q #}
<input type="text" class="input_text fl" name="q" placeholder="搜索商品">
<input type="submit" class="input_btn fr" name="" value="搜索">
</form>在项目的urls.py文件中配置全文搜索的路由
1
2# 该配置用于haystack搜索,它的请求和模板中的action保持一直,它引入的路由一直是include('haystack.urls')
url(r'^search/', include('haystack.urls')),在template下的search目录中创建全文搜索结果的模板文件(文件名固定):search.html
全文搜索出现结果后,则会给search.html模板文件传递如下变量信息:
- query:搜索的关键字
- page:当前页的page对象,遍历该对象,获取到的是SearchResult类的实例对象,内部对象的属性object才是查询数据的模型类的对象,跟直接使用django中的分页元素不大一样
- paginator:分页paginator对象
将haystack框架中使用jieba分词
在haystack的环境包文件夹【haystack/backends/whoosh_cn_backend】中新建文件ChineseAnalyzer.py文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22import jieba
from whoosh.analysis import Tokenizer, Token
class ChineseTokenizer(Tokenizer):
def __call__(self, value, positions=False, chars=False,
keeporiginal=False, removestops=True,
start_pos=0, start_char=0, mode='', **kwargs):
t = Token(positions, chars, removestops=removestops, mode=mode,
**kwargs)
seglist = jieba.cut(value, cut_all=True)
for w in seglist:
t.original = t.text = w
t.boost = 1.0
if positions:
t.pos = start_pos + value.find(w)
if chars:
t.startchar = start_char + value.find(w)
t.endchar = start_char + value.find(w) + len(w)
yield t
def ChineseAnalyzer():
return ChineseTokenizer()修改haystack的环境包中的文件whoosh_backend.py文件的内容,修改前记得备份
1
2
3
4
5
6
7# 在所有导入的最底部追加刚刚创建的ChieseAnalyzer方法,并注视掉原来的分词方法
# from whoosh.analysis import StemmingAnalyzer
from .ChineseAnalyzer import ChineseAnalyzer
# 导入ChineseAnalyzer,并将原有analyzer的StemmingAnalyser替换为ChineseAnalyzer
# 原来的是:analyzer=StemmingAnalyzer()
analyzer=ChineseAnalyzer()
执行命令生成对应的索引文件
1
python manage.py rebuild_index
然后进入前台进行搜索就可以了
拓展
使用haystack过程中一开始遇到查不出结果,但是索引是正常的生成了,就是查不出结果,因此在官网中找了调试类的方式,记录如下:
1 | # 先执行python manage.py shell进入调试页面,然后依次执行如下: |
django删除cookie
1 | response.delete_cookie('username') |