Django通过haystack搜索文章
像现在的博客网站写了一些文章,有时需要搜索符合部分关键词的笔记,可以使用django-haystack来快速实现搜索。这个功能已经加了很久了,到现在才回过头来总结一下笔记。
按haystack的官网说明,它支持Solr, Elasticsearch, Whoosh, Xapian这些搜索引擎。对django来说,我选择纯python的Whoosh,不过由于Whoosh是自带英文分词,需要额外安装jieba来做中文分词。
配置
先安装这几个组件
pip install whoosh django-haystack jieba
接着在settings.py中添加haystack
INSTALLED_APPS = [
...
'haystack',
...
]
# haystack
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'blog.whoosh_cn_backend.WhooshEngine',
'PATH': os.path.join(BASE_DIR, 'whoosh_index'),
},
}
HAYSTACK_SEARCH_RESULTS_PER_PAGE = 5
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
指定model对象和建立视图模板
接下来需要告诉haystack对哪些数据建立索引。比如我想对博客文章索引,那么在blog这个app的目录下新建一个文件search_indexes.py并引入Post模型对象(这个要看自己的定义的名字)。
from .models import Post
from haystack import indexes
class PostIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
def get_model(self):
return Post
def index_queryset(self, using=None):
return self.get_model().objects.all()
在这里,我们对text这个字段用了use_template=True,代表着我们将用一个数据模板来帮助搜索引擎建立索引。
目前博客并非前后端分离,于是存在名为templates的目录,我们需要在templates这个文件夹下为搜索结果建立一个视图模板。
在templates下新建文件夹search/indexes/blog,并在里面新建一个文件post_text.txt,内容如下:
{{ object.title }} {{ object.text }}
这就相当于会搜索文章标题和文章正文。
搜索出来的结果也需要定义如何展示,于是在templates/search下新建一个search.html,如下图。
代码样例如下。搜索页面还是由base页面扩展而来,我这里是加了共同的header和footer,以此保持同样的风格。
{% for post in page.object_list %}这里则是对搜索结果的循环并展示在页面上。
{% extends 'blog/base.html' %}
{% load highlight %}
{% block content %}
<div class="container" style="margin-top: 40px;">
<div class="row">
<div class="col-lg-8 col-md-10 mx-auto">
{% for post in page.object_list %}
{% if post.object.visiable.name != 'private' or user.is_authenticated %}
<div class="post-preview">
<p class="post-meta">
{{ post.object.created_date|date }} - 由{{post.object.author}}   
<span class='comment-count'>{{ post.object.comment_set.count }} 评论</span>   
<span class='view-count'>{{ post.object.views }} 阅读</span>
</p>
{% if post.object.visiable.name == 'private' %}
<h3 class="post-title"><a href="{% url 'post_detail' post.object.id %}">[private] - {{ post.object.title }}</a></h3>
{% else %}
<h3><a href="{% url 'post_detail' post.object.id %}">{{ post.object.title }}</a></h3>
{% endif %}
<h6 class="post-subtitle">{% highlight post.object.text with query max_length 40 %}</h6>
<div class="read-more" style="padding:0 0 20px 0;">
<a href="{% url 'post_detail' post.object.id %}" class="btn btn-outline-primary">继续阅读</a>
</div>
{% if post.object.category %}
<div class="category">
<img src="/media/icons/icons8-category-40px-1.png" width="20" height="20">
<span class="badge badge-success">{{ post.object.category.name }}</span>
</div>
{% endif %}
{% if post.object.tag.all %}
<div class="tag"><img src="/media/icons/icons8-tag-64px-1.png" width="20" height="20">
{% for t in post.object.tag.all %}
<span class="badge badge-info">{{ t.name }}</span>
{% endfor %}
</div>
{% endif %}
</div>
<hr>
{% endif %}
{% empty %}
<h1>您搜索的条件没有数据</h1>
{% endfor %}
</div>
</div>
</div>
{% endblock %}
接着需要添加haystack的路由,在url.py中添加如下。
urlpatterns = [
...
path('search/', include('haystack.urls')),
...
]
高亮搜索结果
当然,搜索也是可以高亮关键词,在上面的search.html中已经定义了{ % load highlight % }。不过还需要定义一下highlight的css属性,比如我在base.html中定义高亮为红色。
<style>
span.highlighted {
color: red;
}
</style>
可以在这个链接下看看效果:https://www.byincd.com/bobjiang/search/?q=django
中文搜索
不过如果想要中文搜索,还需要修改jieba作为中文分词器,否则搜索结果会不太理想。
把haystack的whoosh_backend.py复制到blog这个app目录中,比如我用了virtualenv,路径类似于/venv/lib/python3.6/site-packages/haystack/backends/whoosh_backend.py,更名为whoosh_cn_backend.py。
并在该文件顶部引入jieba的ChineseAnalyzer。
from jieba.analyse import ChineseAnalyzer
163行也要换成ChineseAnalyzer。
#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) # 新增的
整个过程就是:用户打开页面查看文章,haystack就会对标题和正文建立索引,然后当用户搜索时,haystack的视图函数会将搜索结果返回到search.html来渲染,最终呈现出来。当然也可以运行python manage.py rebuild_index来生成索引。