Java優(yōu)化模糊搜索體驗(yàn)的方法詳解
場(chǎng)景
假設(shè)有一張表 t_course
,數(shù)據(jù)量在三到四位數(shù),字段 name
需要支持模糊搜索。用普通的 LIKE
語(yǔ)句,比如:
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ù)庫(kù)。為了不動(dòng)生產(chǎn)環(huán)境配置,果斷放棄。
2. Elasticsearch
接著想到 Elasticsearch,但對(duì)這么簡(jiǎn)單的場(chǎng)景來(lái)說(shuō),未免有點(diǎn)“殺雞用牛刀”。于是繼續(xù)尋找更輕量的方案。
3. 自定義分詞 + MySQL INSTR
最后想到一個(gè)“土辦法”:先對(duì)用戶輸入進(jìn)行分詞,再用 MySQL 的 INSTR
函數(shù)匹配。簡(jiǎn)單粗暴,但很實(shí)用。
實(shí)現(xiàn)
分詞工具
一開(kāi)始用了 jcseg
分詞庫(kù),寫了個(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; } }
本地測(cè)試一切正常,但部署到測(cè)試環(huán)境后,分詞結(jié)果卻變了!比如:
- 本地:
[2025, 數(shù)學(xué), 高一, 下]
- 測(cè)試環(huán)境:
[2025, 數(shù), 學(xué), 高, 1, 下]
原因是 jcseg
在 jar 包中加載默認(rèn)配置和詞庫(kù)時(shí)出問(wèn)題了。網(wǎng)上的解決方案大多是外置詞庫(kù),但我懶得折騰,決定自己擼個(gè)簡(jiǎn)易分詞工具。
簡(jiǎn)易分詞工具
最終實(shí)現(xiàn)如下:
public class WordSegmentationUtils { private static final List<String> DICT; private static final String COURSE_SEARCH_KEYWORD_LIST = "數(shù)學(xué),物理,化學(xué),生物,地理,歷史,政治,英語(yǔ),語(yǔ)文,高中,高一,高二,高三"; 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è)簡(jiǎn)單的詞典 DICT
,按詞典中的詞對(duì)輸入文本進(jìn)行分割。比如:
- 輸入:
2025數(shù)學(xué)高一下
- 輸出:
[2025, 數(shù)學(xué), 高一, 下]
效果驗(yàn)證
現(xiàn)在,無(wú)論用戶輸入以下哪種形式,都能成功匹配到 2025年高一數(shù)學(xué)下學(xué)期
:
2025高一數(shù)學(xué)下
2025 高一 數(shù)學(xué)
數(shù)學(xué)高一2025
小結(jié)
這個(gè)方案雖然簡(jiǎn)單,但在小數(shù)據(jù)量場(chǎng)景下,性能和體驗(yàn)都能滿足需求,且實(shí)現(xiàn)成本低。如果遇到特殊情況,可以通過(guò)動(dòng)態(tài)更新詞典來(lái)解決。
當(dāng)然,這種“土辦法”并不適合復(fù)雜場(chǎng)景。如果需求升級(jí),可以再考慮 MySQL 全文索引或 Elasticsearch。
到此這篇關(guān)于Java優(yōu)化模糊搜索體驗(yàn)的方法詳解的文章就介紹到這了,更多相關(guān)Java優(yōu)化模糊搜索內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于ApplicationContext的三個(gè)常用實(shí)現(xiàn)類
這篇文章主要介紹了關(guān)于ApplicationContext的三個(gè)常用實(shí)現(xiàn)類,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06基于SpringMVC實(shí)現(xiàn)網(wǎng)頁(yè)登錄攔截
SpringMVC的處理器攔截器類似于Servlet開(kāi)發(fā)中的過(guò)濾器Filter,用于對(duì)處理器進(jìn)行預(yù)處理和后處理。因此,本文將為大家介紹如何通過(guò)SpringMVC實(shí)現(xiàn)網(wǎng)頁(yè)登錄攔截功能,需要的小伙伴可以了解一下2021-12-12IDEA的Mybatis Generator駝峰配置問(wèn)題
這篇文章主要介紹了IDEA的Mybatis Generator駝峰配置問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11SpringBoot?Validation快速實(shí)現(xiàn)數(shù)據(jù)校驗(yàn)的示例代碼
在實(shí)際開(kāi)發(fā)中,肯定會(huì)經(jīng)常遇到對(duì)參數(shù)字段進(jìn)行校驗(yàn)的場(chǎng)景,通常我們只能寫大量的if else來(lái)完成校驗(yàn)工作,而如果使用SpringBoot Validation則可以輕松的通過(guò)注解來(lái)完成,接下來(lái)小編給大家介紹下利用SpringBoot?Validation快速實(shí)現(xiàn)數(shù)據(jù)校驗(yàn)的示例代碼,需要的朋友參考下吧2022-06-06Java實(shí)現(xiàn)滑動(dòng)驗(yàn)證碼(前端部分)
這篇文章主要為大家介紹了如何用Java語(yǔ)言實(shí)現(xiàn)滑動(dòng)驗(yàn)證碼的生成(前端部分),文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以跟隨小編學(xué)習(xí)一下2022-10-10