返回文章列表文章索引阅读全文
STARBLOG / 阅读手记
Django

Django Full-Text Search Implementation: HayStack + Whoosh + Jieba

Tools involved haystack is Django's open-source search framework. This framework supports **Solr**, Elasticsearch, Whoosh, **Xapian search engines; you can switch engines directly without changing code, reducing the amount of code. The search engine used is Whoosh, a full-text search engine implemented in pure Python. It has no binary files and the like, is relatively compact, and fairly simple to configure, though of course its performance is naturally a bit lower. Chinese word segmentation Jieba: since Whoosh comes with English tokenization, Chinese

曦远 (DealiAxy)约 20 分钟
目录10 个章节

Tools Involved

  • haystack is Django's open-source search framework. This framework supports Solr, Elasticsearch, Whoosh, **Xapian search engines; you can switch engines directly without changing code, reducing the amount of code.

  • The search engine used is Whoosh, a full-text search engine implemented in pure Python. It has no binary files and the like, is relatively compact, and fairly simple to configure, though of course its performance is naturally a bit lower.

  • Chinese word segmentation with Jieba: since Whoosh comes with English tokenization and does not support Chinese word segmentation very well, jieba is used to replace whoosh's tokenizer component.

Model Configuration

class Post(models.Model):
    """post 文章页面"""
    id = models.AutoField(primary_key=True, max_length=20)
    name = models.CharField(max_length=200, help_text="Post文章的URL链接名称")
    post_category = models.ForeignKey(PostCategory, blank=True, null=True, default=None, on_delete=models.SET_NULL)
    post_author = models.ForeignKey(PostAuthor, blank=True, null=True, default=None, on_delete=models.SET_NULL)
    post_date = models.DateTimeField(default=timezone.now, help_text="创建日期")
    post_modified = models.DateTimeField(default=timezone.now, help_text="修改日期")
    post_content = models.TextField(help_text="html格式的页面内容,仅在page类型才可用")
    post_title = models.CharField(max_length=200)
    post_summary = models.TextField(max_length=300, blank=True)
    post_status = models.CharField(choices=(('publish', '已发布'), ('draft', '草稿')), default='publish', max_length=20,
                                   help_text="页面状态")

    # post_slug = models.CharField(max_length=200, help_text="URL链接名称")

    def __str__(self):
        return self.post_title

Installing Python Modules

  • whoosh
  • django-haystack
  • jieba

Modifying Whoosh's Analyzer

Copy the file whoosh_backend.py (the file path is python路径/lib/python版本/site-packages/haystack/backends/whoosh_backend.py) into the app folder, and modify it as follows Add from jieba.analyse import ChineseAnalyzer

Change it to the following

schema_fields[field_class.index_fieldname] =
    TEXT(stored=True, analyzer=ChineseAnalyzer(),
            field_boost=field_class.boost)

Modifying settings.py

  • Add Haystack to Django's INSTALLED_APPS
HAYSTACK_CONNECTIONS = {
    'default': {
        'ENGINE': 'Core.whoosh_backend.WhooshEngine',
        'PATH': os.path.join(BASE_DIR, 'Resources', 'index', 'whoosh'),
    },
}

Note:

  • Core is my App name; please replace it with your own App folder name as appropriate
  • PATH is the folder for storing Whoosh index files.
  • For configurations of other engines, please consult the official documentation.

Creating the Index

Create a search_indexes.py file in the App folder that needs search functionality, used to create the index.

The contents are as follows:

import datetime
from haystack import indexes
from Core import models


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

    post_author = indexes.CharField(model_attr='post_author')
    post_date = indexes.DateTimeField(model_attr='post_date')

    def get_model(self):
        return models.Post

    def index_queryset(self, using=None):
        """Used when the entire index for model is updated."""
        return self.get_model().objects.filter(post_date__lte=datetime.datetime.now())

Each index must have one and only one field with document=True, which means haystack and the search engine will use this field's content as the index for retrieval (primary field). Other fields are merely auxiliary attributes for convenient access and are not used as retrieval data.

Note: If a field is set with document=True, it is generally convention that this field is named text. This is the consistent naming in the SearchIndex class to prevent backend confusion. Of course you can change the name as you like, but it is not recommended.

Additionally, haystack provides use_template=True on the text field, which allows us to use a data template to build the search engine index file. It is convenient to use (officially recommended; of course there are other more complex ways to build index files, which I don't know yet). The data template path is yourapp/templates/search/indexes/yourapp/classname_text.txt, for example in this case Resources/templates/search/indexes/Core/post_text.txt The filename must be 类名_text.txt of the class to be indexed, and its contents are:

{{ object.post_title }}
{{ object.post_author }}
{{ object.post_content }}
{{ object.post_summary }}

The purpose of this data template is to build indexes for

  • Post.post_title
  • Post.post_author
  • Post.post_content
  • Post.post_summary

these four fields. When searching, full-text search matching will be performed on these three fields.

Configuring URLs

Add the following to the URL configuration

    path('search/', include('haystack.urls')),

Writing search.html

Add a search.html file under the templates/search/ folder:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h2>Search</h2>

<form method="get" action=".">
    <table>
        {{ form.as_table }}
        <tr>
            <td> </td>
            <td>
                <input type="submit" value="Search">
            </td>
        </tr>
    </table>

    {% if query %}
        <h3>Results</h3>

        {% for result in page.object_list %}
            <p>
                <a href="{{ result.object.get_absolute_url }}">{{ result.object.post_title }}</a>
            </p>
        {% empty %}
            <p>No results found.</p>
        {% endfor %}

        {% if page.has_previous or page.has_next %}
            <div>
                {% if page.has_previous %}<a href="?q={{ query }}&page={{ page.previous_page_number }}">{% endif %}«
                Previous{% if page.has_previous %}</a>{% endif %}
                |
                {% if page.has_next %}<a href="?q={{ query }}&page={{ page.next_page_number }}">{% endif %}Next »
                {% if page.has_next %}</a>{% endif %}
            </div>
        {% endif %}
    {% else %}
        {# Show some example queries to run, maybe query syntax, something else? #}
    {% endif %}
</form>
</body>
</html>

Rebuilding the Index

Use python manage.py rebuild_index or use the update_index command.

Add the following configuration in settings.py to automatically refresh the index.

HAYSTACK_SIGNAL_PROCESSOR = "haystack.signals.RealtimeSignalProcessor"

That's it! You can open 网站/search and try out the search feature~

A First Look at SearchQuerySet

I found that none of the materials online covered this, so I looked it up on the official site. Documentation URL: https://django-haystack.readthedocs.io/en/v2.8.1/searchqueryset_api.html#ref-searchqueryset-api

This SearchQuerySet is similar to the QuerySet in the Django ORM framework. Those familiar with Django should get the hang of it quickly, though handling the results can be rather troublesome. Using SearchQuerySet is generally for Ajax APIs. I found that serializers previously used for QuerySet cannot be used for SearchQuerySet; some modifications would still be needed to make it work. I'll continue working on this when I have time. Or I'll look at the official docs again and study it.

The usage is pretty simple; just look at the official documentation, it's already written very clearly~

_EOF_

Django Full-Text Search Implementation: HayStack + Whoosh + Jieba

本文作者:
程序设计实验室
版权声明:
本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

相关文章

配置Django-TinyMCE组件支持上传图片功能

Django自带的Admin后台,好用,TinyMCE作为富文本编辑器,也蛮好用的,这两者结合起来在做博客的时候很方便(当然博客可能更适合用Markdown来写),但是Django-TinyMCE这个组件默认没有图片上传功能的,需要我们自己实现,本文将一步步带大家实现这个图片上传功能。 读者也可以举一反三实现其他需要和Django结合的功能。 编写上传图片逻辑 在任一views.py里添加代码:

2021-12-18 · Django · 约 1 分钟

Django更换数据库和迁移数据方案

前言 双十一光顾着买东西都没怎么写文章,现在笔记里还有十几篇半成品文章没写完… 今天来分享一下 Django 项目切换数据库和迁移数据的方案,网络上找到的文章方法不一,且使用中容易遇到各类报错,本文根据 Django 官方文档和工作中的经验,稳定可靠,在博客中长期更新~ 如果你遇到同样的问题,阅读本文应该能得到比较好的解决方案。 基本步骤 Django 默认使用 SQLite 数据库方便开发,同时

2022-11-12 · Django · 约 1 分钟

Django中间件之实现Admin后台IP白名单

前言 Django自带的Admin管理后台很方便,但是实际生产环境真的会有挺多安全问题的,在admin的安全防护这方面,我之前就研究实现了给admin加上登录验证码和限流功能,可以参考这篇文章:给Django Admin添加验证码和多次登录尝试限制 不过就在内部使用的话就好很多,因此我们可以通过Django的中间件来实现IP过滤,所以本文中涉及到两个知识点,一个是Django的中间件,另外一个是P

2022-08-26 · Django · 约 1 分钟

讨论

共 0 条

良言一句三冬暖,恶语伤人六月寒。

加载评论中…