Django Haystack 全文检索与关键词高亮

我们已经实现的搜索功能实在过于简单,没有多大的实用性。对于一个搜索引擎来说,至少应该能够根据用户的搜索关键词对搜索结果进行排序以及高亮关键字。

Django Haystack 简介

    django-haystack 是一个专门提供搜索功能的 django 第三方应用,它支持 Solr、Elasticsearch、Whoosh、Xapian 等多种搜索引擎配合著名的中文自然语言处理库 jieba 分词,就可以为我们的博客提供一个效果不错的博客文章搜索系统。

安装必要依赖

     激活虚拟环境执行:
pip install whoosh django-haystack jieba -i https://pypi.douban.com/simple/

配置 Haystack

    首先是把 django haystack 加入到 INSTALLED_APPS 选项里.
然后加入如下配置项:
    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 使用的搜索引擎。
    PATH 指定了索引文件(搜索引擎需要建立索引文件)需要存放的位置。
    HAYSTACK_SEARCH_RESULTS_PER_PAGE 指定如何对搜索结果分页,这里设置为每 10 项结果为一页。
    HAYSTACK_SIGNAL_PROCESSOR 指定什么时候更新索引,这里我们使用 haystack.signals.RealtimeSignalProcessor,作用是每当有文章更新时就更新索引。由于博客文章更新不会太频繁,因此实时更新没有问题。

处理数据

    接下来就要告诉 django haystack 使用哪些数据建立索引以及如何存放索引。
如果要对 blog 应用下的数据进行全文检索,做法是在 blog 应用下建立一个search_indexes.py 文件,写上如下代码:
    from haystack import indexes
    from .models import Post
    
    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()
之所以写上述代码是因为,haystack规定:要想对某个 app 下的数据进行全文检索,
就要在该 app 下创建一个 search_indexes.py 文件,
然后创建一个 XXIndex 类(XX 为含有被检索数据的模型,
如这里的 Post),并且继承 SearchIndex(注意不是SearchField) 和 Indexable。
上述代码创建了一个针对Post的索引。为什么创建索引,是因为创建索引可以加快搜索速度减小服务器压力。有点类似数据库索引。
每个索引里面必须有且只能有一个字段为 document=True,
这代表 django haystack 和搜索引擎将使用此字段的内容
作为索引进行检索(primary field)。
如果使用一个字段设置了document=True,则一般约定此字段名为text
这是在 SearchIndex 类里面一贯的命名,以防止后台混乱。
当然名字你也可以随便改,不过不建议改。就和request和response命名一样。
haystack 提供了use_template=True 在 text 字段中,这样就允许我们使用数据模板去建立搜索引擎索引的文件.
    模板路径为:templates/search/indexes/blog/post_text.txt
    其内容为:
        {{ object.title }} 
        {{ object.content }}
    这个数据模板的作用是对 Post.title、Post.content 
    这两个字段建立索引,当检索的时候会对这两个字段做
    全文检索匹配,然后将匹配的结果排序后作为搜索结果返回。

配置 URL

    搜索的视图函数和 URL 模式 django haystack 都已经帮我们写好了,直接include(haystack.urls)即可。
同时删掉我们之前配的url,以防止冲突。url(r'^haystack/', include('haystack.urls')),

修改搜索表单

    修改一下搜索表单,让它提交数据到 django haystack 搜索视图对应的 URL:    
    <form role="search" method="get" id="searchform" action="{% url 'haystack_search' %}">

创建搜索结果页面

    默认情况下,haystack_search 视图函数会将搜索结果传递给模板 search/search.html
因此创建这个模板文件,对搜索结果进行渲染。
search.html模板详情见项目目录。
这个模板基本和 blog/index.html 一样,只是由于 haystack 对搜索结果做了分页,传给模板的变量是一个 page 对象,所以我们从 page 中取出这一页对应的搜索结果,然后对其循环显示,即 {% for result in page.object_list %}。

高亮关键词

    haystack 中实现关键词高亮效果非常简单,只需要使用 {% highlight %} 模板标签即可。
{% highlight result.content with query %}
高亮处理的原理其实就是给文本中的关键字包上一个 span 标签并且为其添加 highlighted 样式。
我们还要给 highlighted 类指定样式,在 base.html 中添加即可:

<head>
<style> 
    span.highlighted { color: red; } 
</style>

</head>

扫描二维码关注公众号,回复: 2603541 查看本文章

修改搜索引擎为中文分词

    Whoosh 作为搜索引擎,但在 django haystack 中为 Whoosh 指定的分词器是英文分词器,可能会使得搜索结果不理想。
我们把这个分词器替换成 jieba 中文分词器。
从安装的site-packages目录的 haystack 目录中把 haystack/backends/whoosh_backend.py 文件拷贝到 blog/ 下,重命名为 whoosh_cn_backend.py
然后找到如下一行代码:
【blog/ whoosh_cn_backend.py】 
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。

建立索引文件

    最后一步就是建立索引文件了,运行命令 python manage.py rebuild_index 就可以建立索引文件了。

search.py

{% extends 'base.html' %}
{% load highlight %}

{% block main %}
    {% if query %}
        {% for result in page.object_list %}
            <article class="post post-{{ result.object.pk }}">
                <header class="entry-header">
                    <h1 class="entry-title">
                        <a href="{{ result.object.get_absolute_url }}">{% highlight result.object.title with query %}</a>
                    </h1>
                    <div class="entry-meta">
                    <span class="post-category">
                        <a href="{% url 'blog:category' result.object.category.id %}">
                            {{ result.object.category.name }}</a></span>
                        <span class="post-date"><a href="#">
                            <time class="entry-date" datetime="{{ result.object.create_time }}">
                                {{ result.object.create_time }}</time></a></span>
                        <span class="post-author"><a href="#">{% highlight result.object.author with query %}</a></span>
                        <span class="comments-link">
                        <a href="{{ result.object.get_absolute_url }}#comment-area">
                            {{ result.object.comment_set.count }} 评论</a></span>
                        <span class="views-count"><a
                                href="{{ result.object.get_absolute_url }}">{{ result.object.views }} 阅读</a></span>
                    </div>
                </header>
                <div class="entry-content clearfix">
                    <p>{% highlight result.object.content with query %}</p>
                    <div class="read-more cl-effect-14">
                        <a href="{{ result.object.get_absolute_url }}" class="more-link">继续阅读 <span
                                class="meta-nav">→</span></a>
                    </div>
                </div>
            </article>
        {% empty %}
            <div class="no-post">没有搜索到你想要的结果!</div>
        {% endfor %}
        {% if page.has_previous or page.has_next %}
            <div>
                {% if page.has_previous %}
                    <a href="?q={{ query }}&amp;page={{ page.previous_page_number }}">{% endif %}&laquo; Previous
                {% if page.has_previous %}</a>{% endif %}
                |
                {% if page.has_next %}<a href="?q={{ query }}&amp;page={{ page.next_page_number }}">{% endif %}Next
                &raquo;{% if page.has_next %}</a>{% endif %}
            </div>
        {% endif %}
    {% else %}
        请输入搜索关键词,例如 django,rilegou
    {% endif %}
{% endblock main %}

猜你喜欢

转载自blog.csdn.net/yx1179109710/article/details/81304036