稍微深度踩坑haystack + whoosh + jieba

news2024/10/5 12:58:40

说到django的全文检索,网上基本推荐的都是 haystack + whoosh + jieba 的方案。
由于我的需求对搜索时间敏感度较低,但是要求不能有数据的错漏。
但是没有调试的情况下,搜索质量真的很差,搞得我都想直接用Like搜索数据库算了。
但是峰回路转,还是让我找到几个信息,稍微优化了一下搜索结果。

1.haystack

haystack这层我增加了一个模糊搜索,即原本的text='q’变成了text__contains=‘q’。
搜索从原本的的content切换成了contains,不再需要完全匹配就能命中。
比如词“下雨”在用content的时候搜“雨”是没办法命中的,换成contain之后就可以了。(需要搭配第二点中的自定义全量分词,默认分词不行。具体原因不知,不过应该不是index的问题,因为我测试过用默认分词rebuild_index后,再切换ChineseAnalyzer而不重新rebuild_index依然可以搜索到结果,猜测原因应该是对搜索词的分词,但是我就一个字有啥好分词的?有懂得大佬麻烦教我一下。)

class LynSearchForm(SearchForm):
    def search(self):
        if not self.is_valid():
            return self.no_query_found()

        if not self.cleaned_data.get("q"):
            return self.no_query_found()
            
		# 原设置搜索条件的语句,被我替换成下面的语句
        # sqs = self.searchqueryset.auto_query(self.cleaned_data["q"]) 
        sqs = self.searchqueryset.filter(text__contains=self.cleaned_data["q"])

        if self.load_all:
            sqs = sqs.load_all()

        return sqs

我写这篇文章的时间是2023年8月,haystack已经进行了大的更新。从原本的views.SearchView 切换到了 generic_views.SearchView。把接口都给换了,然后代码里面还是旧的内容,各位看官看的时候要注意。

新流程以SearchView为例:

form = self.get_form(form_class)
self.queryset = form.search()
context = self.get_context_data({self.form_name: form})

get_context_data是django的view的函数,所以我们要做的是在form上面做手脚。

2.whoosh + jieba

除了上面的操作外,还需要对jieba的ChineseAnalyzer进行自定义,修改成全量分词。
代码来源 州的先生 大佬写的MrDoc我从中受益良多,大家有兴趣可以多多支持

# coding:utf-8
# @文件: chinese_analyzer.py
# @创建者:州的先生
# #日期:2020/11/22
# 博客地址:zmister.com

from whoosh.compat import u, text_type
from whoosh.analysis.filters import LowercaseFilter
from whoosh.analysis.filters import StopFilter, STOP_WORDS
from whoosh.analysis.morph import StemFilter
from whoosh.analysis.tokenizers import default_pattern
from whoosh.lang.porter import stem
from whoosh.analysis import Tokenizer, Token
from whoosh.util.text import rcompile
import jieba


class ChineseTokenizer(Tokenizer):
    """
    使用正则表达式从文本中提取 token 令牌。
    >>> rex = ChineseTokenizer()
    >>> [token.text for token in rex(u("hi there 3.141 big-time under_score"))]
    ["hi", "there", "3.141", "big", "time", "under_score"]
    """
    def __init__(self, expression=default_pattern, gaps=False):
        """
        :param expression: 一个正则表达式对象或字符串,默认为 rcompile(r"\w+(\.?\w+)*")。
            表达式的每一个匹配都等于一个 token 令牌。
            第0组匹配(整个匹配文本)用作 token 令牌的文本。
            如果你需要更复杂的正则表达式匹配处理,只需要编写自己的 tokenizer 令牌解析器即可。
        :param gaps: 如果为 True, tokenizer 令牌解析器会在正则表达式上进行分割,而非匹配。
        """
        self.expression = rcompile(expression)
        self.gaps = gaps

    def __eq__(self, other):
        if self.__class__ is other.__class__:
            if self.expression.pattern == other.expression.pattern:
                return True
        return False

    def __call__(self, value, positions=False, chars=False, keeporiginal=False,
                 removestops=True, start_pos=0, start_char=0, tokenize=True,
                 mode='', **kwargs):
        """
        :param value: 进行令牌解析的 Unicode 字符串。
        :param positions: 是否在 token 令牌中记录 token 令牌位置。
        :param chars: 是否在 token 中记录字符偏移。
        :param start_pos: 第一个 token 的位置。例如,
            如果设置 start_pos=2, 那么 token 的位置将是 2,3,4,...而非 0,1,2,...
        :param start_char: 第一个 token 中第一个字符的偏移量。
            例如, 如果设置 start_char=2, 那么文本 "aaa bbb" 解析的两个字符串位置将体现为 (2,5),(6,9) 而非 (0,3),(4,7).
        :param tokenize: 如果为 True, 文本应该被令牌解析。
        """
        # 判断传入的文本是否为字符串,如果不为字符串则抛出
        assert isinstance(value, text_type), "%s is not unicode" % repr(value)
        t = Token(positions, chars, removestops=removestops, mode=mode,
                  **kwargs)
        if not tokenize:
            t.original = t.text = value
            t.boost = 1.0
            if positions:
                t.pos = start_pos
            if chars:
                t.startchar = start_char
                t.endchar = start_char + len(value)
            yield t
        elif not self.gaps:
            # The default: expression matches are used as tokens
            # 默认情况下,正则表达式的匹配用作 token 令牌
            # for pos, match in enumerate(self.expression.finditer(value)):
            #     t.text = match.group(0)
            #     t.boost = 1.0
            #     if keeporiginal:
            #         t.original = t.text
            #     t.stopped = False
            #     if positions:
            #         t.pos = start_pos + pos
            #     if chars:
            #         t.startchar = start_char + match.start()
            #         t.endchar = start_char + match.end()
            #     yield t
            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
        else:
            # When gaps=True, iterate through the matches and
            # yield the text between them.
            # 当 gaps=True, 遍历匹配项并在它们之间生成文本。
            prevend = 0
            pos = start_pos
            for match in self.expression.finditer(value):
                start = prevend
                end = match.start()
                text = value[start:end]
                if text:
                    t.text = text
                    t.boost = 1.0
                    if keeporiginal:
                        t.original = t.text
                    t.stopped = False
                    if positions:
                        t.pos = pos
                        pos += 1
                    if chars:
                        t.startchar = start_char + start
                        t.endchar = start_char + end
                    yield t
                prevend = match.end()
            # If the last "gap" was before the end of the text,
            # yield the last bit of text as a final token.
            if prevend < len(value):
                t.text = value[prevend:]
                t.boost = 1.0
                if keeporiginal:
                    t.original = t.text
                t.stopped = False
                if positions:
                    t.pos = pos
                if chars:
                    t.startchar = prevend
                    t.endchar = len(value)
                yield t


def ChineseAnalyzer(expression=default_pattern, stoplist=None,
                     minsize=2, maxsize=None, gaps=False, stemfn=stem,
                     ignore=None, cachesize=50000):
    """Composes a RegexTokenizer with a lower case filter, an optional stop
    filter, and a stemming filter.
    用小写过滤器、可选的停止停用词过滤器和词干过滤器组成生成器。
    >>> ana = ChineseAnalyzer()
    >>> [token.text for token in ana("Testing is testing and testing")]
    ["test", "test", "test"]
    :param expression: 用于提取 token 令牌的正则表达式
    :param stoplist: 一个停用词列表。 设置为 None 标识禁用停用词过滤功能。
    :param minsize: 单词最小长度,小于它的单词将被从流中删除。
    :param maxsize: 单词最大长度,大于它的单词将被从流中删除。
    :param gaps: 如果为 True, tokenizer 令牌解析器将会分割正则表达式,而非匹配正则表达式
    :param ignore: 一组忽略的单词。
    :param cachesize: 缓存词干词的最大数目。 这个数字越大,词干生成的速度就越快,但占用的内存就越多。
                      使用 None 表示无缓存,使用 -1 表示无限缓存。
    """
    ret = ChineseTokenizer(expression=expression, gaps=gaps)
    chain = ret | LowercaseFilter()
    if stoplist is not None:
        chain = chain | StopFilter(stoplist=stoplist, minsize=minsize,maxsize=maxsize)
    return chain | StemFilter(stemfn=stemfn, ignore=ignore,cachesize=cachesize)

重点是这个:
在这里插入图片描述
在PS2里面,结巴的大佬有提到有新功能(11年前的)jieba.cut_for_search()也可以试试

然后在whoosh_cn_backend.py里面修改ChineseAnalyzer

from .chinese_analyzer import ChineseAnalyzer
# from jieba.analyse import ChineseAnalyzer

PS:这个jieba自带的ChineseAnalyzer也是我换的,如果你第一次搞这个,可以参考

https://blog.csdn.net/qiqiyingse/article/details/110299639

PS

1.HayStack

As of version 2.4 the views in haystack.views.SearchView are
deprecated in favor of the new generic views in
haystack.generic_views.SearchView which use the standard Django
class-based views which are available in every version of Django which
is supported by Haystack.

在这里插入图片描述
新代码,旧流程,可真有你的。

2.CSDN

稍微吐槽下CSDN,STONE大哥的文章里面,下面结巴的大佬在评论区有回复,结果评论显示的是2条,还好我点开看了一下。不然就错过了这么重要的信息。

https://blog.csdn.net/wenxuansoft/article/details/8169842

在这里插入图片描述
然后显示的热评是个灌水回答。。
在这里插入图片描述
3.其他筛选参数
关于筛选的类型,在whoosh_cn_backend.py文件中,基本和django的差不多。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/820280.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

item_search-ks-根据关键词取商品列表

一、接口参数说明&#xff1a; item_search-根据关键词取商品列表&#xff0c;点击更多API调试&#xff0c;请移步注册API账号点击获取测试key和secret 公共参数 请求地址: https://api-gw.onebound.cn/ks/item_search 名称类型必须描述keyString是调用key&#xff08;http:…

一文快速入门Byzer-python

目录 一、Byzer-Python介绍 二、Byzer-python工具语法糖 三、环境依赖 1. Python 环境搭建 2. Ray 环境搭建 3. Byzer-python 与 Ray 四、参数详解 五、数据处理 1. Byzer-python 处理数据 2. Byzer-python 代码说明 3. Byzer-python 读写 Excel 文件 4. Byzer-pytho…

FPGA及其应用

目录 1.什么是FPGA 2.FPGA的硬件结构 3.FPGA与单片机的区别 4.FPGA的具体应用场景 1.什么是FPGA FPGA&#xff08;Field-Programmable Gate Array&#xff09;是一种可编程逻辑器件&#xff0c;它由大量的可编程逻辑单元&#xff08;CLB&#xff09;和可编程连线&#xff08…

解决el-table打印时数据重复显示

1.表格数据比较多加了横向滚动和竖向滚动&#xff0c;导致打印出问题 主要原因是fixed导致&#xff0c;但是又必须得滚动和打印 方法如下&#xff1a; 1. 2. is_fixed: true,//data中定义初始值 3.打印时设置为false,记得要改回true if (key 2) { this.is_fixed false //打…

Image process ----butterworth high pass 滤波器

import numpy as np import matplotlib.pyplot as plt import cv2def Butterworth_Filter_Image():img cv2.imread(r/Users/PycharmProjects/ImageProcess/Butterworth Filter Image/Pasted Graphic 31.png,0)plt.imshow(img)# ———————————————————————…

Sublime操作技巧笔记

同时选中2个文件&#xff1a;自动切换成左右2个界面 格式化代码ctrlshifth&#xff1a; 使用快捷键ctrl shift p调出控制台&#xff0c;输入install package&#xff0c;然后输入html-css-js prettify&#xff0c;进行下载。具体的快捷键在preference > package setting &g…

P1542 包裹快递 (二分答案)(内附封面)

包裹快递 题目描述 小 K 成功地破解了密文。但是乘车到 X 国的时候&#xff0c;发现钱包被偷了&#xff0c;于是无奈之下只好作快递员来攒足路费去 Orz 教主…… 一个快递公司要将 n n n 个包裹分别送到 n n n 个地方&#xff0c;并分配给邮递员小 K 一个事先设定好的路线…

PoseiSwap:首个基于模块化设施构建的订单簿 DEX

在前不久&#xff0c;PoseiSwap 曾以1000万美元的估值&#xff0c;获得了来自于ZebecLabs基金会的150万美元的融资。此后 PoseiSwap 又以2500万美元的估值&#xff0c;从GateLabs、EmurgoVentures、Republic以及CipholioVentures等行业顶级投资机构中&#xff0c;获得了新一轮未…

QMessageBox类

QMessageBox类 静态方法例子 静态方法 调用这一些静态成员函数&#xff0c;就可以得到模态提示框 枚举值为&#xff1a; 例子 头文件&#xff1a; #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <QMessageBox>QT_BEGIN_NAMESPACE…

ORB算法在opencv中实现方法

在OPenCV中实现ORB算法&#xff0c;使用的是&#xff1a; 1.实例化ORB orb cv.xfeatures2d.orb_create(nfeatures)参数&#xff1a; nfeatures: 特征点的最大数量 2.利用orb.detectAndCompute()检测关键点并计算 kp,des orb.detectAndCompute(gray,None)参数&#xff1a…

跟踪项目进度,项目经理可以通过这三个方面进行

项目实施过程中&#xff0c;常常会出现一些不确定性因素&#xff0c;如未确定项目的轻重缓急、优先级变化、任务不明确、团队成员对具体内容和实施流程不清楚等。 此外&#xff0c;对项目资源的使用情况不明确也是导致项目延期的因素之一。因此&#xff0c;在项目实施前&…

Linux之 centos、Ubuntu 安装常见程序 (-) Mysql 5.7 版本和8.0版本

CentOS 安装 MySql 注意 需要有root权限 安装5.7版本 – 由于MySql并不在CentOS的官方仓库中&#xff0c;所以需要通过rmp命令&#xff1a; 导入MySQL仓库密钥 1、配置MySQL的yum仓库 配置yum仓库 更新密钥 rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022 安装…

Windows 环境Kubernetes安装

目录 前言 安装 Docker 安装 Kubernetes Windows 安装 kubectl 介绍 安装 开启 Kubernetes 前言 Docker作为当前最流行的容器化平台&#xff0c;为Kubernetes提供了强大的容器化技术基础。Kubernetes与Docker的结合&#xff0c;使得容器化应用程序在大规模集群中得以简…

C. Ski Resort (逐步累加滑动求连续子序列)

题目&#xff1a;Problem - C - Codeforces 总结&#xff1a; 对于样例1 3 1 5 -5 0 -10 转化 n3 //天数 k1 //最小天数 q5 //最适温度 设最后输出值为num;(num最初为0) 操作一&#xff1a; 从-5统计 -5 小于最适温度5 可取 可取…

简单的python有趣小程序,有趣的代码大全python

这篇文章主要介绍了python简单有趣的程序源代码&#xff0c;具有一定借鉴价值&#xff0c;需要的朋友可以参考下。希望大家阅读完这篇文章后大有收获&#xff0c;下面让小编带着大家一起了解一下。

服务器中了360后缀勒索病毒怎么解决,360后缀勒索病毒解密数据恢复

某医药公司是一家小型企业&#xff0c;拥有自己的服务器存储重要数据和文件。某天早上&#xff0c;IT管理员发现企业服务器中了360后缀的勒索病毒&#xff0c;所有数据文件都被加密了。这个病毒的入侵让公司业务受到严重影响&#xff0c;企业立即启动了勒索病毒解密数据恢复的措…

HCIP-datacom-821题库真题和机构资料

HCIP-Datacom-Core Technology考试内容 HCIP-Datacom-Core Technology V1.0考试覆盖数据通信领域各场景通用核心知识&#xff0c;包括路由基础、OSPF、IS-IS、BGP、路由和流量控制、以太网交换技术、组播、IPv6、网络安全、网络可靠性、网络服务与管理、WLAN、网络解决方案。 机…

QDialog类

QDialog类 QDialog类api 使用方式调用exec()槽函数调用accept槽函数调用reject槽函数调用done槽函数 例子 QDialog类 QWedget类中的函数&#xff0c;在QDialog中都可以使用 api // 构造函数 QDialog::QDialog(QWidget *parent nullptr, Qt::WindowFlags f Qt::WindowFlags()…

Numpy基础操作:数组之间形状相互转换

ndarray对象提供了一些可以便捷地改变数组基础形状的属性和方法&#xff0c;例如&#xff0c;将一个3行4列的二维数组转换成6行2列的二维数组&#xff0c;关于这些属性和方法的具体说明如表9-3所示。 上述这些方法都能够改变数组的形状&#xff0c;但是&#xff0c;reshape()、…

JDK1.8 切换版本之TLS协议导致项目链接数据库报错

JDK1.8 切换版本之TLS协议导致项目链接数据库报错_jdk1.8 不支持的tls1.2 吗_Lim0816的博客-CSDN博客