Django搜索功能实现

搜索可以使用最原始的like的方式进行搜索。当然这种搜索方式对于一些小量的数据是非常合适的。但是随着数据量越来越大。这时候我们就需要使用搜索引擎了。搜索引擎会将所有需要搜索的数据使用算法做一个索引,以后搜索的时候就只需要根据这个索引即可找到相应的数据。搜索引擎做索引的过程会比较慢,但是一旦索引建立完成,那么以后再搜索的时候就会很快了。

django-haystack插件:

这个插件是专门给django提供搜索功能的。django-haystack提供了一个搜索的接口,底层可以根据自己的需求更换搜索引擎。他其实有点类似于Django中的ORM插件,提供了一个操作数据库的接口,但是底层具体使用哪个数据库是可以自己设置的。安装方式非常简单,通过pip install django-haystack即可安装。

搜索引擎:

django-haystack支持的搜索引擎有SolrElasticsearchWhooshXapian等。Whoosh是基于纯Python的搜索引擎,检索速度快,集成方便。这里我们就选择Whoosh来作为haystack的搜索引擎。安装方式同样也是通过pip安装的:pip install whoosh

集成步骤:

1、在项目中安装django-haystack

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',

    # 添加
    'haystack',
]

2、设置搜索引擎:

settings.py中添加以下配置:

HAYSTACK_CONNECTIONS = {
    'default': {
        # 设置haystack的搜索引擎
        'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
        # 设置索引文件的位置
        'PATH': os.path.join(BASE_DIR, 'whoosh_index'),
    }
}

3、创建索引类:

在模型所属的app下创建一个search_indexes.py文件,然后创建索引类。比如要给News创建索引,代码如下:

class NewsIndex(indexes.SearchIndex,indexes.Indexable):
    text = indexes.CharField(document=True,use_template=True)

    def get_model(self):
        return News

    def index_queryset(self, using=None):
        return self.get_model().objects.all()

4、添加url映射

在主urls.py中,添加以下代码:

urlpatterns = [
    path('',views.index,name='index'),
    # 添加search的url映射
    path('search/',include('haystack.urls')),
    path('news/', include("apps.news.urls")),
]

5、添加模板:

templates文件夹下创建以下结构的目录:

templates
    search
        indexes
            news(app的名字)
                news(模型的名字)_text.txt

然后在news_text.txt中添加需要被索引的字段。示例代码如下:

{{ object.title }}
{{ object.content }}

接着在templates文件夹下创建search.html模板文件,haystack会自动的在templates文件夹下寻找这个模板文件渲染,并且会给这个模板文件传入pagepaginatorquery等参数。其中pagepaginator分别是django内置的Page类和Paginator类的对象,query是查询的关键字。我们可以通过page.object_list获取到查找出来的数据。示例代码如下:

<ul class="recommend-list">
    {% for result in page.object_list %}
        {% with result.object as news %}
            <li>
                <div class="thumbnail-group">
                    <a href="#">
                        <img src="{{ news.thumbnail }}" alt="">
                    </a>
                </div>
                <div class="news-group">
                    <p class="title">
                        <a href="#">{{ news.title }}</a>
                    </p>
                    <p class="desc">
                        {{ news.desc }}
                    </p>
                    <p class="more">
                       <span class="category">{{ news.caetgory.name }}</span>
                        <span class="pub-time">{{ news.pub_time }}</span>
                        <span class="author">{{ news.author.username }}</span>
                    </p>
                </div>
            </li>
        {% endwith %}
    {% endfor %}
</ul>

6、建立索引:

在项目的根目录下,使用命令python manage.py rebuild_index创建索引。
如果不想每次数据增删改查后都要手动的创建索引,可以在settings.py中配置:

# 增删改查后自动创建索引
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'

7、使用jieba分词替换Whoosh默认的分词:

Whoosh默认是采用正则表达式进行分词的。这对于英文来说是足够了。但是对于中文却支持不好。因此我们要替换为jieba分词。jieba分词是中文分词中最好用的免费的分词库。要使用jieba分词库,需要通过pip install jieba进行安装。

安装完成后,拷贝django-env/Lib/site-packages/haystack/backends/whoosh_backend.py其中的代码,将他放在项目的其他包中,然后创建一个名叫whoosh_cn_backend.py文件,把刚刚复制的代码粘贴进去,然后再添加以下代码:

import jieba
from whoosh.analysis import Tokenizer, Token

class ChineseTokenizer(Tokenizer):
    def __call__(self, value, positions=False, chars=False,
                 keeporiginal=False, removestops=True,
                 start_pos=0, start_char=0, mode='', **kwargs):
        t = Token(positions, chars, removestops=removestops, mode=mode,
                  **kwargs)
        seglist = jieba.cut(value, cut_all=True)
        for w in seglist:
            t.original = t.text = w
            t.boost = 1.0
            if positions:
                t.pos = start_pos + value.find(w)
            if chars:
                t.startchar = start_char + value.find(w)
                t.endchar = start_char + value.find(w) + len(w)
            yield t

def ChineseAnalyzer():
    return ChineseTokenizer()

然后再将之前的代码中的analyzer=StemmingAnalyzer()替换为analyzer=ChineseAnalyzer()即可。

更多教程:

http://django-haystack.readthedocs.io/en/master/tutorial.html

上一篇:字符串-----7.KMP算法获取子串下标


下一篇:#28. 实现 strStr()