该博客最开始采用的模板是并不包括搜索功能的,在主页只有主页、归档和分类三个部分。最后博主自己添加了搜索框,不过其实不太想让大家使用这个功能,因此将搜索框隐藏了,只有再点击搜索时,才会显现出来。但是这个添加匹配的不太好,导致手机端会有对不齐的现象,以后前端学好了再来修复这个bug。
博客的搜索功能简单来实现的话,通过查询功能查找到符合关键字的对象。但是,对于一个搜索引擎来说,至少应该能够根据用户的搜索关键词对搜索结果进行排序以及高亮关键字。现在我们就来使用 django-haystack 实现这些特性。
django-haystack 是一个专门提供搜索功能的 django 第三方应用,它支持 Solr、Elasticsearch、Whoosh、Xapian 等多种搜索引擎,配合著名的中文自然语言处理库 jieba 分词,就可以为我们的博客提供一个效s果不错的博客文章搜索系统。
1、安装相关软件
进入我们的虚拟环境,安装以下依赖:
pip install whoosh django-haystack jieba
- Whoosh。Whoosh 是一个由纯 Python 实现的全文搜索引擎,没有二进制文件等,比较小巧,配置简单方便。
- jieba 中文分词。由于 Whoosh 自带的是英文分词,对中文的分词支持不是太好,所以使用 jieba 替换Whoosh 的分词组件。
2、配置 Haystack
安装完成后,需要在setting.py中进行相关配置:
INSTALLED_APPS = [
'django.contrib.admin',
# 其它 app...
'haystack',
]
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'blog.whoosh_cn_backend.WhooshEngine',
'PATH': os.path.join(BASE_DIR, 'whoosh_index'),
},
}
HAYSTACK_SEARCH_RESULTS_PER_PAGE = 10
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
HAYSTACK_CONNECTIONS
的 ENGINE 指定了 django haystack 使用的搜索引擎,这里我们使用了 blog.whoosh_cn_backend.WhooshEngine
,虽然目前这个引擎还不存在,但我们接下来会创建它。PATH 指定了索引文件需要存放的位置,我们设置为项目根目录 BASE_DIR 下的 whoosh_index 文件夹(在建立索引是会自动创建)。
HAYSTACK_SEARCH_RESULTS_PER_PAGE
指定如何对搜索结果分页,这里设置为每 10 项结果为一页。
HAYSTACK_SIGNAL_PROCESSOR
指定什么时候更新索引,这里我们使用 haystack.signals.RealtimeSignalProcessor
,作用是每当有文章更新时就更新索引。由于博客文章更新不会太频繁,因此实时更新没有问题。
3、处理数据
接下来就要告诉 django haystack 使用那些数据建立索引以及如何存放索引。如果要对 blog 应用下的数据进行全文检索,做法是在 myblog 应用下建立一个 search_indexes.py 文件,写上如下代码:
from haystack import indexes
from myblog.models import Blog
class BlogIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
def get_model(self):
return Blog
def index_queryset(self, using=None):
return self.get_model().objects.all()
这是 django haystack 的规定。要相对某个 app 下的数据进行全文检索,就要在该 app 下创建一个 search_indexes.py 文件,然后创建一个 XXIndex 类(XX 为含有被检索数据的模型,如这里的 Post),并且继承 SearchIndex 和 Indexable。
为什么要创建索引?索引就像是一本书的目录,可以为读者提供更快速的导航与查找。在这里也是同样的道理,当数据量非常大的时候,若要从这些数据里找出所有的满足搜索条件的几乎是不太可能的,将会给服务器带来极大的负担。所以我们需要为指定的数据添加一个索引(目录),在这里是为 Post 创建一个索引,索引的实现细节是我们不需要关心的,我们只关心为哪些字段创建索引,如何指定。
每个索引里面必须有且只能有一个字段为 document=True,这代表 django haystack 和搜索引擎将使用此字段的内容作为索引进行检索(primary field)。注意,如果使用一个字段设置了document=True,则一般约定此字段名为text,这是在 SearchIndex 类里面一贯的命名,以防止后台混乱,当然名字你也可以随便改,不过不建议改。
并且,haystack 提供了use_template=True 在 text 字段中,这样就允许我们使用数据模板去建立搜索引擎索引的文件,说得通俗点就是索引里面需要存放一些什么东西,例如 Post 的 title 字段,这样我们可以通过 title 内容来检索 Post 数据了。举个例子,假如你搜索 Python ,那么就可以检索出 title 中含有 Python 的Post了,怎么样是不是很简单?数据模板的路径为 templates/search/indexes/youapp/\<model_name>_text.txt
(例如 templates/search/indexes/myblog/post_text.txt)
,其内容为:
{{ object.title }}
{{ object.content }}
这个数据模板的作用是对 Post.title、Post.body 这两个字段建立索引,当检索的时候会对这两个字段做全文检索匹配,然后将匹配的结果排序后作为搜索结果返回。
4、 配置 URL
接下来就是配置 URL,搜索的视图函数和 URL 模式 django haystack 都已经帮我们写好了,只需要项目的 urls.py 中包含它:
url(r'^search/', include('haystack.urls')),
5、修改搜索表单
修改一下搜索表单,让它提交数据到 django haystack 搜索视图对应的 URL:
<form action="{% url 'haystack_search' %}">
<input id="unit" type="search" name="q" placeholder="搜索">
</form>
6、创建搜索结果页面
haystack_search 视图函数会将搜索结果传递给模板 search/search.html,因此创建这个模板文件,对搜索结果进行渲染:
{% extends 'base.html' %}
{% load highlight %}
{% block content %}
<div class="content-wrap">
{% for blog in page.object_list %}
<div>
<a href="{% url 'blog_id' blog.object.id %}">
<h3>{{ forloop.counter }}、{% highlight blog.object.title with query %}</h3>
</a>
<div style="word-wrap: break-word">
{% highlight blog.object.content with query %}
</div>
{% if forloop.counter == page.object_list|length %}
{% else %}
<hr>
{% endif %}
</div>
{% empty %}
<div class="no-post">没有搜索到相关内容,请重新搜索</div>
{% endfor %}
</div>
{% endblock %}
由于 haystack 对搜索结果做了分页,传给模板的变量是一个 page 对象,所以我们从 page 中取出这一页对应的搜索结果,然后对其循环显示,即 {% for blog in page.object_list %}
。另外要取得 博客以显示博客的数据如标题、正文,需要从 blog的 object 属性中获取。query 变量的值即为用户搜索的关键词。
7、关键词高亮
注意到百度的搜索结果页面,含有用户搜索的关键词的地方都是被标红的,在 django haystack 中实现这个效果也非常简单,只需要使用 {% highlight %} 模板标签即可,其用法如下:
# 使用默认值
{% highlight result.summary with query %}
# 这里我们为 {{ result.summary }} 里所有的 {{ query }} 指定了一个<div></div>标签,并且将class设置为highlight_me_please,这样就可以自己通过CSS为{{ query }}添加高亮效果了,怎么样,是不是很科学呢
{% highlight result.summary with query html_tag "div" css_class "highlight_me_please" %}
# 可以 max_length 限制最终{{ result.summary }} 被高亮处理后的长度
{% highlight result.summary with query max_length 40 %}
8、修改搜索引擎为中文分词
我们使用 Whoosh 作为搜索引擎,但在 django haystack 中为 Whoosh 指定的分词器是英文分词器,可能会使得搜索结果不理想,我们把这个分词器替换成 jieba 中文分词器。从你安装的 haystack 中把 haystack/backends/whoosh_backends.py 文件拷贝到 blog/ 下,重命名为 whoosh_cn_backends.py(之前我们在 settings.py 中 的 HAYSTACK_CONNECTIONS 指定的就是这个文件),然后找到如下一行代码:
schema_fields[field_class.index_fieldname] = TEXT(stored=True, analyzer=StemmingAnalyzer(), field_boost=field_class.boost, sortable=True)
将其中的 analyzer 改为 ChineseAnalyzer,当然为了使用它,你需要在文件顶部引入:from jieba.analyse import ChineseAnalyzer
。
9、建立索引文件
最后一步就是建立索引文件了,运行命令 python manage.py rebuild_index
就可以建立索引文件了。
最后,我们可以通过搜索命令给定关键词进行搜索,比如搜索”博客“,便会得到如下结果:
但是,我们可以看到博客的标题只有关键词, 关键词之前的内容被…替代了。这显然不是我们想要的效果,接下来我们通过修改源码来实现博客标题的完全显示以及对搜索结果的分页并使其序号连续显示。
——————————————————————————————————————————
项目的完整代码:django_blog
觉得有用的欢迎给个star。