whoosh配合jieba实现网站的全局检索功能
By - C灵C
HayStack 是Django的一个开源框架,提供了非常方便的搜索功能,它支持Solr、Elasticsearch、Whoosh、Xapian多种搜索引擎。本文将使用Whoosh搜索引擎,配合中文分词jieba来替换Whoosh的分词组件实现中文全文搜索的功能。
由于本文新建文件较多,请依照下图自行新建文件,不可随意更改文件名称。
search模块下的
urls.py文件为匹配路由
search_indexes.py文件来定义索引类
whoosh_cn_backend.py来将jieba分词组件添加到搜索引擎中使其支持中文搜索
serach.html为前端模板文件
product_text.txt为搜索引擎索引模板文件。
创建完成后,执行以下命令,通过pip安装Whoosh搜索引擎和jieba分词,
pip install Django-haystack
pip install whoosh
pip install jieba
接下来在项目的settings.py文件中注册模块和引入HayStack,具体代码如下:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'haystack',
'apps.search', # 全局搜索模块
]
# 配置HayStack
HAYSTACK_CONNECTIONS = {
'default': {
# 设置搜索引擎,文件是apps下的serach的whoosh_cn_backend.py
# 如果search模块未在apps下请自行替换或者去掉apps
'ENGINE': 'apps.search.whoosh_cn_backend.WhooshEngine',
'PATH': os.path.join(BASE_DIR, 'whoosh_index'),
'INCLUDE_SPELLING': True,
},
}
# 设置每页显示的数据量
HAYSTACK_SEARCH_RESULTS_PER_PAGE = 2
# 当数据库改变时,自动更新索引
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
在Python的安装目录下打开Lib\site-packages\haystack\backends\shoosh_backend.py文件,将全文复制到新建的whoosh_cn_backend.py文件中,修改如下内容:
# 顶部添加 引入jieba分词器模块
from jieba.analyse import ChineseAnalyzer
# 在166行左右找到如下代码
schema_fields[field_class.index_fieldname] = TEXT(stored=True, analyzer=StemmingAnalyzer (), field_boost=field_class.boost, sortable=True)
# 将其修改为如下格式
schema_fields[field_class.index_fieldname] = TEXT(stored=True, analyzer=ChineseAnalyzer(), field_boost=field_class.boost, sortable=True)
修改完成后,在search模块下models.py文件中创建新的数据模型作为搜索引擎的搜索对象,具体代码如下:
from django.db import models
class Poetry(models.Model):
id = models.AutoField('序号', primary_key=True)
name = models.CharField('名称', max_length=50)
author = models.CharField('作者', max_length=20)
detail = models.CharField('内容', max_length=300)
# 设置返回值
def __str__(self):
return self.name
接下来创建搜索引擎的索引,在数据量非常大的时候,我们就需要为指定的数据添加一个索引,来使搜索引擎快速找到符合条件的数据,所以在search_indexes.py中定义该模型的索引类,注意该文件名称不可改变,具体代码如下:
# 本文件名称不允许修改,否则将无法创建索引
from apps.search.models import Poetry
from haystack import indexes
# 类名必须为模型名+Index,比如模型Poetry,则索引类为PoetryIndex
# 其对应的索引模板路径应为/项目的应用模块名称/templates/search/indexes/项目的应用模块名称/模型(小写)_text.txt
class PoetryIndex(indexes.SearchIndex, indexes.Indexable):
# doucument=True代表搜索引擎将使用此字段的内容作为索引进行检索
# use_template=True代表使用索引模板建立索引文件
text = indexes.CharField(document=True, use_template=True)
# 将索引类与模型Poetry进行绑定
def get_model(self):
return Poetry
# 设置索引的查询范围
def index_queryset(self, using=None):
return self.get_model().objects.all()
添加完成后,在索引模板poetry_text.txt文件中设置索引的检索字段,添加如下具体代码:
# templates/search/indexes/search/poetry_text.txt
{{ object.name }}
{{ object.author }}
{{ object.detail }}
现在已经定义了索引类,和索引模板,接下来根据这两者,
通过命令python manage.py rebuild_index 创建索引文件,创建成功后会在项目文件夹下可以看到whoosh_index文件夹,该文件夹中包含索引文件。
最后在Django中实现搜索功能,实现模型Poetry的全文检索。在urls.py文件中定义搜索引擎的URL地址,具体代码如下:
from django.urls import path
from . import views
urlpatterns = [
# 搜索引擎,上一层路由为search
path('find/', views.MySearchView(), name='haystack'),
]
在URL的视图views.py文件中处理具体的业务逻辑,添加如下代码:
from django.shortcuts import render
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.conf import settings
from .models import *
from haystack.views import SearchView
# 视图以通用视图实现
class MySearchView(SearchView):
# 模版文件
template = 'search.html'
# 重写响应方式,如果请求参数q为空,返回模型Poetry的全部数据,否则根据参数q搜索相关数据
def create_response(self):
if not self.request.GET.get('q', ''):
show_all = True
poetry = Poetry.objects.all()
paginator = Paginator(poetry, settings.HAYSTACK_SEARCH_RESULTS_PER_PAGE)
try:
page = paginator.page(int(self.request.GET.get('page', 1)))
except PageNotAnInteger:
# 如果参数page的数据类型不是整型,则返回第一页数据
page = paginator.page(1)
except EmptyPage:
# 用户访问的页数大于实际页数,则返回最后一页的数据
page = paginator.page(paginator.num_pages)
return render(self.request, self.template, locals())
else:
show_all = False
qs = super(MySearchView, self).create_response()
return qs
在HTML模板search.html文件中展示数据,请复制css文件到static文件夹下,以使样式生效。模板中添加代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>搜索引擎</title>
{# 导入CSS样式文件 #}
{% load staticfiles %}
<link type="text/css" rel="stylesheet" href="{% static "css/common.css" %}">
<link type="text/css" rel="stylesheet" href="{% static "css/search.css" %}">
</head>
<body>
<div class="header">
<div class="search-box">
<form id="searchForm" action="" method="get">
<div class="search-keyword">
{# 搜索输入文本框必须命名为q #}
<input id="q" name="q" type="text" class="keyword" maxlength="120"/>
</div>
<input id="subSerch" type="submit" class="search-button" value="搜 索" />
</form>
<div id="suggest" class="search-suggest"></div>
</div>
</div>
<div class="wrapper clearfix" id="wrapper">
<div class="mod_songlist">
<ul class="songlist__header">
<li class="songlist__header_name">名称</li>
<li class="songlist__header_author">作者</li>
<li class="songlist__header_album">内容</li>
</ul>
<ul class="songlist__list">
{# 列出当前分页所对应的数据内容 #}
{% if show_all %}
{% for item in page.object_list %}
<li class="js_songlist__child" mid="1425301" ix="6">
<div class="songlist__item">
<div class="songlist__songname">{{ item.name }}</div>
<div class="songlist__artist">{{item.author}}</div>
<div class="songlist__album">{{ item.detail }}</div>
</div>
</li>
{% endfor %}
{% else %}
{# 导入自带高亮功能 #}
{% load highlight %}
{% for item in page.object_list %}
<li class="js_songlist__child" mid="1425301" ix="6">
<div class="songlist__item">
<div class="songlist__songname">{% highlight item.object.name with query %}</div>
<div class="songlist__artist">{% highlight item.object.author with query %}</div>
<div class="songlist__album">{% highlight item.object.detail with query %}</div>
</div>
</li>
{% endfor %}
{% endif %}
</ul>
{# 分页导航,如有疑问请参考本书分页功能 #}
<div class="page-box">
<div class="pagebar" id="pageBar">
{# 上一页的URL地址 #}
{% if page.has_previous %}
{% if query %}
<a href="{% url 'search:haystack'%}?q={{ query }}&page={{ page.previous_page_number }}" class="prev">上一页</a>
{% else %}
<a href="{% url 'search:haystack'%}?page={{ page.previous_page_number }}" class="prev">上一页</a>
{% endif %}
{% endif %}
{# 列出所有的URL地址 #}
{% for num in page.paginator.page_range %}
{% if num == page.number %}
<span class="sel">{{ page.number }}</span>
{% else %}
{% if query %}
<a href="{% url 'search:haystack' %}?q={{ query }}&page={{ num }}" target="_self">{{num}}</a>
{% else %}
<a href="{% url 'search:haystack' %}?page={{ num }}" target="_self">{{num}}</a>
{% endif %}
{% endif %}
{% endfor %}
{# 下一页的URL地址 #}
{% if page.has_next %}
{% if query %}
<a href="{% url 'search:haystack' %}?q={{ query }}&page={{ page.next_page_number }}" class="next">下一页</a>
{% else %}
<a href="{% url 'search:haystack' %}?page={{ page.next_page_number }}" class="next">下一页</a>
{% endif %}
{% endif %}
</div>
</div>
</div>
</div>
</body>
</html>
添加完成后,启动项目,在浏览器中输入http://127.0.0.1:8000/search/find/就可显示搜索页面,搜索框中输入查询条件后即可查看搜索结果。
文章评论