T O P
Image

Django

HayStack实现本站全局搜索

whoosh配合jieba实现网站的全局检索功能

  • By - C灵C

  • 2019年6月1日 21:12







HayStack Django的一个开源框架,提供了非常方便的搜索功能,它支持SolrElasticsearchWhooshXapian多种搜索引擎。本文将使用Whoosh搜索引擎,配合中文分词jieba来替换Whoosh的分词组件实现中文全文搜索的功能。

由于本文新建文件较多,请依照下图自行新建文件,不可随意更改文件名称。

search.png


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下的serachwhoosh_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 }}&amp;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 }}&amp;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 }}&amp;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/就可显示搜索页面,搜索框中输入查询条件后即可查看搜索结果。