Java優(yōu)化模糊搜索體驗(yàn)的方法詳解
場景
假設(shè)有一張表 t_course,數(shù)據(jù)量在三到四位數(shù),字段 name 需要支持模糊搜索。用普通的 LIKE 語句,比如:
SELECT id, name FROM t_course WHERE name LIKE '%2025數(shù)學(xué)高一下%';
結(jié)果卻查不到 2025年高一數(shù)學(xué)下學(xué)期。這就很尷尬了,用戶體驗(yàn)直接拉胯。
方案探索
1. MySQL 全文索引
首先想到 MySQL 的全文索引,但要支持中文分詞得改 ngram_token_size 配置,還得重啟數(shù)據(jù)庫。為了不動(dòng)生產(chǎn)環(huán)境配置,果斷放棄。
2. Elasticsearch
接著想到 Elasticsearch,但對(duì)這么簡單的場景來說,未免有點(diǎn)“殺雞用牛刀”。于是繼續(xù)尋找更輕量的方案。
3. 自定義分詞 + MySQL INSTR
最后想到一個(gè)“土辦法”:先對(duì)用戶輸入進(jìn)行分詞,再用 MySQL 的 INSTR 函數(shù)匹配。簡單粗暴,但很實(shí)用。

實(shí)現(xiàn)
分詞工具
一開始用了 jcseg 分詞庫,寫了個(gè)工具類:
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;
}
}
本地測試一切正常,但部署到測試環(huán)境后,分詞結(jié)果卻變了!比如:
- 本地:
[2025, 數(shù)學(xué), 高一, 下] - 測試環(huán)境:
[2025, 數(shù), 學(xué), 高, 1, 下]
原因是 jcseg 在 jar 包中加載默認(rèn)配置和詞庫時(shí)出問題了。網(wǎng)上的解決方案大多是外置詞庫,但我懶得折騰,決定自己擼個(gè)簡易分詞工具。
簡易分詞工具
最終實(shí)現(xiàn)如下:
public class WordSegmentationUtils {
private static final List<String> DICT;
private static final String COURSE_SEARCH_KEYWORD_LIST = "數(shù)學(xué),物理,化學(xué),生物,地理,歷史,政治,英語,語文,高中,高一,高二,高三";
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;
}
}
這個(gè)工具基于一個(gè)簡單的詞典 DICT,按詞典中的詞對(duì)輸入文本進(jìn)行分割。比如:
- 輸入:
2025數(shù)學(xué)高一下 - 輸出:
[2025, 數(shù)學(xué), 高一, 下]
效果驗(yàn)證
現(xiàn)在,無論用戶輸入以下哪種形式,都能成功匹配到 2025年高一數(shù)學(xué)下學(xué)期:
2025高一數(shù)學(xué)下2025 高一 數(shù)學(xué)數(shù)學(xué)高一2025
小結(jié)
這個(gè)方案雖然簡單,但在小數(shù)據(jù)量場景下,性能和體驗(yàn)都能滿足需求,且實(shí)現(xiàn)成本低。如果遇到特殊情況,可以通過動(dòng)態(tài)更新詞典來解決。
當(dāng)然,這種“土辦法”并不適合復(fù)雜場景。如果需求升級(jí),可以再考慮 MySQL 全文索引或 Elasticsearch。
到此這篇關(guān)于Java優(yōu)化模糊搜索體驗(yàn)的方法詳解的文章就介紹到這了,更多相關(guān)Java優(yōu)化模糊搜索內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Jenkins源代碼管理SVN實(shí)現(xiàn)步驟解析
這篇文章主要介紹了Jenkins源代碼管理SVN實(shí)現(xiàn)步驟解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09
Calcite使用SQL實(shí)現(xiàn)查詢excel內(nèi)容
因?yàn)閏alcite本身沒有excel的適配器,?所以本文將模仿calcite-file,?搞一個(gè)calcite-file-excel實(shí)現(xiàn)查詢excel內(nèi)容,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-01-01
如何使用java.security.SecureRandom安全生成隨機(jī)數(shù)和隨機(jī)字符串工具類
這篇文章主要給大家介紹了關(guān)于如何使用java.security.SecureRandom安全生成隨機(jī)數(shù)和隨機(jī)字符串工具類的相關(guān)資料,SecureRandom擴(kuò)展了Random類,并通過在java 8中添加的新方法得到了豐富,需要的朋友可以參考下2024-05-05
java redis 實(shí)現(xiàn)簡單的用戶簽到功能
這篇文章主要介紹了java redis 實(shí)現(xiàn)簡單的用戶簽到功能,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2020-12-12
解決JAVA項(xiàng)目啟動(dòng)卡住,無任何異常信息的問題
這篇文章主要介紹了解決JAVA項(xiàng)目啟動(dòng)卡住,無任何異常信息的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
SpringBoot解析JSON報(bào)錯(cuò)400 Bad Request的解決方案
在Spring Boot開發(fā)中,400 Bad Request錯(cuò)誤是常見的客戶端錯(cuò)誤之一,本文將從問題分析,排查步驟到解決方案進(jìn)行系統(tǒng)性講解,幫助開發(fā)者高效定位并修復(fù)問題2025-07-07

