Java|小数据量场景的模糊搜索体验优化

在小数据量场景下,如何优化模糊搜索体验?本文分享一个简单实用的方案,虽然有点"土",但效果还不错。

场景

假设有一张表 t_course,数据量在三到四位数,字段 name 需要支持模糊搜索。用普通的 LIKE 语句,比如:

sql 复制代码
SELECT id, name FROM t_course WHERE name LIKE '%2025数学高一下%';

结果却查不到 2025年高一数学下学期。这就很尴尬了,用户体验直接拉胯。

方案探索

1. MySQL 全文索引

首先想到 MySQL 的全文索引,但要支持中文分词得改 ngram_token_size 配置,还得重启数据库。为了不动生产环境配置,果断放弃。

2. Elasticsearch

接着想到 Elasticsearch,但对这么简单的场景来说,未免有点"杀鸡用牛刀"。于是继续寻找更轻量的方案。

3. 自定义分词 + MySQL INSTR

最后想到一个"土办法":先对用户输入进行分词,再用 MySQL 的 INSTR 函数匹配。简单粗暴,但很实用。

实现

分词工具

一开始用了 jcseg 分词库,写了个工具类:

arduino 复制代码
public class JcSegUtils {
    private static final SegmenterConfig CONFIG = new SegmenterConfig(true);
    private static final ADictionary DIC = DictionaryFactory.createSingletonDictionary(CONFIG);

    public static List<String> segment(String text) throws IOException {
        ISegment seg = ISegment.NLP.factory.create(CONFIG, DIC);
        seg.reset(new StringReader(text));
        IWord word;
        List<String> result = new ArrayList<>();
        while ((word = seg.next()) != null) {
            String wordText = word.getValue();
            if (StringUtils.isNotBlank(wordText)) {
                result.add(wordText);
            }
        }
        return result;
    }
}

本地测试一切正常,但部署到测试环境后,分词结果却变了!比如:

  • 本地:[2025, 数学, 高一, 下]

  • 测试环境:[2025, 数, 学, 高, 1, 下]

原因是 jcseg 在 jar 包中加载默认配置和词库时出问题了。网上的解决方案大多是外置词库,但我懒得折腾,决定自己撸个简易分词工具。

简易分词工具

最终实现如下:

arduino 复制代码
public class WordSegmentationUtils {
    private static final List<String> DICT;
    private static final String COURSE_SEARCH_KEYWORD_LIST = "数学,物理,化学,生物,地理,历史,政治,英语,语文,高中,高一,高二,高三";

    static {
        DICT = new ArrayList<>();
        for (int i = 2018; i <= 2099; i++) {
            DICT.add(String.valueOf(i));
        }
        DICT.addAll(Arrays.asList(COURSE_SEARCH_KEYWORD_LIST.split(",")));
    }

    public static List<String> segment(String text) {
        if (StringUtils.isBlank(text)) {
            return new ArrayList<>();
        }
        List<String> segments = new ArrayList<>();
        segments.add(text);
        for (String word : DICT) {
            segments = segment(segments, word);
        }
        return segments;
    }

    private static List<String> segment(List<String> segments, String word) {
        List<String> newSegments = new ArrayList<>();
        for (String segment : segments) {
            if (segment.contains(word)) {
                newSegments.add(word);
                String[] split = segment.split(word);
                for (String s : split) {
                    if (StringUtils.isNotBlank(s)) {
                        newSegments.add(s.trim());
                    }
                }
            } else {
                newSegments.add(segment);
            }
        }
        return newSegments;
    }
}

这个工具基于一个简单的词典 DICT,按词典中的词对输入文本进行分割。比如:

  • 输入:2025数学高一下

  • 输出:[2025, 数学, 高一, 下]

效果验证

现在,无论用户输入以下哪种形式,都能成功匹配到 2025年高一数学下学期

  • 2025高一数学下
  • 2025 高一 数学
  • 数学高一2025

小结

这个方案虽然简单,但在小数据量场景下,性能和体验都能满足需求,且实现成本低。如果遇到特殊情况,可以通过动态更新词典来解决。

当然,这种"土办法"并不适合复杂场景。如果需求升级,可以再考虑 MySQL 全文索引或 Elasticsearch。

最后,自己一个人负责开发和运维就是任性!如果有团队一起评审,这方案可能早就被否了吧......额,达摩克利斯之剑高悬。


如果读完文章有收获,可以关注我的微信公众号「闷骚的程序员」并🌟设为星标🌟,随时阅读更多内容。

相关推荐
多多*7 分钟前
算法竞赛相关 Java 二分模版
java·开发语言·数据结构·数据库·sql·算法·oracle
爱喝酸奶的桃酥10 分钟前
MYSQL数据库集群高可用和数据监控平台
java·数据库·mysql
唐僧洗头爱飘柔95271 小时前
【SSM-SSM整合】将Spring、SpringMVC、Mybatis三者进行整合;本文阐述了几个核心原理知识点,附带对应的源码以及描述解析
java·spring·mybatis·springmvc·动态代理·ioc容器·视图控制器
骑牛小道士1 小时前
Java基础 集合框架 Collection接口和抽象类AbstractCollection
java
alden_ygq1 小时前
当java进程内存使用超过jvm设置大小会发生什么?
java·开发语言·jvm
triticale2 小时前
【Java】网络编程(Socket)
java·网络·socket
淘源码d2 小时前
什么是ERP?ERP有哪些功能?小微企业ERP系统源码,SpringBoot+Vue+ElementUI+UniAPP
java·源码·erp·erp源码·企业资源计划·企业erp·工厂erp
源码方舟2 小时前
【基于ALS模型的教育视频推荐系统(Java实现)】
java·python·算法·音视频
蜗牛沐雨2 小时前
Rust 中的 `PartialEq` 和 `Eq`:深入解析与应用
开发语言·后端·rust
Python私教2 小时前
Rust快速入门:从零到实战指南
开发语言·后端·rust