Django+haystack+jieba进行全文检索

最近,在做一个全文检索的功能,找了两个方案:

  1. mysql的全文检索索引
    1. 优点:配置起来简单,改mysql配置即可
    2. 缺点:无法在django使用模型生成,查询语句也无法使用orm,只能用原生sql
  2. 基于Django+haystack+jieba的全文检索
    1. 优点:有第三方库django-haystack直接和django进行关联,还有drf-haystack第三方库支持drf的写法
    2. 缺点:配置比较麻烦,需要自己生成索引,维护索引

综上所述,考虑项目的实际情况,最后考虑使用第二种方法,基于Django+haystack+jieba进行全文检索。

1.相关概念

​ 此方法是在django框架下,使用haystack和中文分词jieba生成索引,索引引擎使用的是默认的引擎whoosh。若有条件或者是数据量大,推荐使用elasticsearch。es的效果比whoosh的效果好很多,且可以做更多的功能和操作。

2.前期准备

​ 先下载依赖包

pip install whoosh
pip install jieba
pip install django-haystack
pip install drf_haystack

3.项目配置

​ 1.首先在django项目中,创建一个app.(repository为自己的app名称)

python manage.py startapp repository

​ 2.在settings的INSTALLED_APPS里注册此app和haystack

INSTALLED_APPS = [
    'haystack', # 放在头部
		 ...
    "rest_framework",
    "repository",
]

​ 3.在settings中配置搜索引擎

HAYSTACK_CONNECTIONS = {
    'default': {
        # 使用whoosh引擎
        'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
        # 索引文件路径
        'PATH': os.path.join(BASE_DIR, 'whoosh_index'),
    }
}

# 当添加、修改、删除数据时,自动生成索引
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'

​ 4.创建模型

from django.db import models


class Repository(models.Models):
    """
    文章
    """
    title = models.CharField(max_length=255, help_text="文章标题")
    content = models.TextField(help_text="文章内容")

    class Meta:
        verbose_name = '文章'

​ 5.在repository的app下创建文件search_indexes.py(此名称固定)

from haystack import indexes  # 导入索引
from .models import Repository  # 导入模型


# RepositoryIndex是固定格式命名,Repository是你models.py中的类名

class RepositoryIndex(indexes.SearchIndex, indexes.Indexable):
  	# document=True: 字段名,所有需要索引的字段,一般命名为 text 一个索引类只会有一个
    text = indexes.CharField(document=True, use_template=True) 
    title = indexes.CharField(model_attr="title") # model_attr指定为对应模型的哪个字段
    content = indexes.CharField(model_attr="content")


    def get_model(self):
        return Repository # 返回需要生成索引的模型

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

​ 6.在templates文件夹创建索引文件,文件的层级为templates/search/indexes/repository,在repository文件夹下创建文件repository_text.txt (文件夹层级为必须固定格式,repository_text.txt格式为app名称_text.txt)。在repository_text.txt中编写如下:

{{ object.title }} # 对应Repository的title
{{ object.content }}# 对应Repository的content

意思为使用Repository模型的title和content作为索引进行查询,也可以创建多个字段。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LNbuiqiS-1649311534405)
在这里插入图片描述

​ 7.设置分词工具为jieba. 在repository包下,创建文件whoosh_cn_backend.py,文件内容为所在虚拟环境下jieba依赖包的

/***/anaconda3/envs/py363/lib/python3.6/site-packages/haystack/backends/whoosh_backend.py(本人环境)。也就是把下载好的haystack的依赖文件whoosh_backend.py复制一份出来到whoosh_cn_backend.py里。然后修改whoosh_cn_backend.py里的代码,在导入包的头部加入from jieba.analyse import ChineseAnalyzer。然后修改旧的StemmingAnalyzerChineseAnalyzer


# 在全局引入的最后一行加入jieba分词器
from jieba.analyse import ChineseAnalyzer # 导包的头部导入 

''''''
''''''

# 找到StemmingAnalyzer 把它改为ChineseAnalyzer
schema_fields[field_class.index_fieldname] = TEXT(
  stored=True,
  # analyzer=field_class.analyzer or StemmingAnalyzer(),
  analyzer=field_class.analyzer or ChineseAnalyzer(),
  field_boost=field_class.boost,
  sortable=True,
)

''''''
''''''


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BuiQyRoQ-1649311534406)
在这里插入图片描述

最后在settings里,把配置改为引用whoosh_cn_backend

HAYSTACK_CONNECTIONS = {
    'default': {
        # 使用whoosh引擎
        # 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
        #
        'ENGINE': 'repository.whoosh_cn_backend.WhooshEngine', # repository下的whoosh_cn_backend文件里的WhooshEngine
        # 索引文件路径
        'PATH': os.path.join(BASE_DIR, 'whoosh_index'),
    }
}

​ 8.生成索引,会在项目中生成索引文件目录.

python manage.py rebuild_index

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Eyg4UTDM-1649311534406)
在这里插入图片描述

编写接口
1.使用drf-haystack编写接口

​ 首先编写视图,查询时,使用text进行查询.(http://127.0.0.1:8000/repository/?text=文章&page=1&page_size=20)

import jieba

from django.db import transaction
from haystack.query import SearchQuerySet
from rest_framework.response import Response
from packages.drf.viewsets import ModelViewSet

from repository.models import Repository
from repository.serializers import RepositorySerializer, RepositoryIndexSerializer
from repository.pages import LargePageNumberPagination


class RepositoryModelViewSet(ModelViewSet):
    queryset = Repository.objects.all()
    serializer_class = RepositorySerializer
    pagination_class = LargePageNumberPagination

    # 如果未传递查询参数 返回的数据将为空 故须根据查询条件调整查询的地方
    # 不查询 查询模型 查询 查询索引数据
    def list(self, request, *args, **kwargs):
        search = request.GET.get("text") # 查询时,使用text进行查询
        if search:
            index_queryset = SearchQuerySet() # 核心 SearchQuerySet为索引查询集
            search_list = jieba.cut_for_search(search)
            for search_name in search_list:
                if not search_name:
                    continue
                index_queryset = index_queryset.filter_and(text=search_name)
						
            # 使用分页器 
            paginator = self.pagination_class()
            page_user_list = paginator.paginate_queryset(index_queryset, self.request, view=self)
            instances = RepositoryIndexSerializer(page_user_list, many=True)
            response = paginator.get_paginated_response(instances.data)
            return response

        return super().list(request, *args, **kwargs)

    @transaction.atomic
    def create(self, request, *args, **kwargs):
        data = request.data
        instance_data = {
            'title': data['title'],
            'content': data["content"],
        }
        instance = Repository.objects.create(**instance_data)
        return Response(instance.id)

路由:
router = SimpleRouter()
router.register(r"repository", RepositoryModelViewSet, basename="repository")
urlpatterns += router.urls
序列化器:
from rest_framework import serializers
from rest_framework.serializers import ModelSerializer
from drf_haystack.serializers import HaystackSerializer
from drf_haystack import serializers as drf_haystack_serializers

from repository.models import Repository
from repository.search_indexes import RepositoryIndex


class RepositoryIndexSerializer(HaystackSerializer):
    """
    全文检索的序列化器
    """
    title = drf_haystack_serializers.HaystackCharField()
    content = drf_haystack_serializers.HaystackCharField()

    class Meta:
        index_classes = [RepositoryIndex]
        search_fields = ["text"]
        fields = ["id", "title", "content"]


class RepositorySerializer(ModelSerializer):

    class Meta:
        model = Repository
        fields = ["id", "title", "content"]


对于SearchQuerySet(),是django-haystack的索引查询集。是查询核心。所有的查询数据从这个查询集合来。它的models方法可以指定索引查询的模型。

SearchQuerySet().filter(content='foo').models(BlogEntry, Comment)

使用SearchQuerySet()查询,可以使用filter或者filter_or或者filter_and。具体的接口参考API文档。

使用SearchQuerySet()进行查询操作,查询到的数据和drf一样正常使用分页器。

使用drf-haystack

​ 使用drf-haystack,用法和django的drf大同小异。需要注意的是,drf-haystack继承了django的drf,故我们可以使用drf的写法操作它。

from drf_haystack import serializers as drf_haystack_serializers

class RepositoryIndexSerializer(HaystackSerializer):
    """
    全文检索的序列化器
    """
    title = drf_haystack_serializers.HaystackCharField()
    content = drf_haystack_serializers.HaystackCharField()
    other_content = drf_haystack_serializers.serializers.SerializerMethodField()


    class Meta:
        index_classes = [RepositoryIndex] # 索引模型
        search_fields = ["text"] # 查询字段
        fields = ["id", "title", "content"] # 返回字段

    def get_other_content(self, obj):
        return obj.object.content

至此,从配置,到生成索引。从编写模型到序列化器,从路由到视图,都可以使用到全文检索。我们可以很快速的编写出全文检索的接口。另外,还有一些参考的文章和相关的API文档,我放在末尾进行参考。

注意:若转发,请附上原文链接!

参考文档

haystack官网文档

drf-haystack官方文档

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值