在Mysql資料庫中,模糊搜索通常使用LIKE關鍵字。然而,隨著數據量的不斷增加,Mysql在處理模糊搜索時可能面臨性能瓶頸。因此,引入Elasticsearch作為搜索引擎,以提高搜索性能和用戶體驗成為一種合理的選擇。 1、客戶的訴求 在ES中,影響搜索結果的因素多種多樣,包括分詞器、Match搜 ...
在Mysql資料庫中,模糊搜索通常使用LIKE關鍵字。然而,隨著數據量的不斷增加,Mysql在處理模糊搜索時可能面臨性能瓶頸。因此,引入Elasticsearch作為搜索引擎,以提高搜索性能和用戶體驗成為一種合理的選擇。
1、客戶的訴求
在ES中,影響搜索結果的因素多種多樣,包括分詞器、Match搜索、Term搜索、組合搜索等。有些用戶已經養成了在Mysql中使用LIKE進行模糊搜索的習慣。若ES返回的搜索結果不符合用戶的預期,可能會引發抱怨,甚至認為系統存在Bug。
誰讓客戶是上帝,客戶是金主爸爸呢,客戶有訴求,我們就得安排上。下麵我們就聊聊如何用ES實現Mysql的like模糊匹配效果。
如果對Elasticsearch不太熟悉的讀者,建議先閱讀我之前的文章:
《Springboot項目中使用Elasticsearch的RestClient》
2、短語匹配match_phrase
2.1、定義
為實現模糊匹配的搜索效果,通常有兩種方式,其中之一是match_phrase,先說說match_phrase。
match_phrase短語匹配會對檢索內容進行分詞,要求這些分詞在被檢索內容中全部存在,並且順序必須一致。預設情況下,這些詞必須是連續的。
2.2、實驗
- 場景1:創建一個mapping,採用預設分詞器(即每個字都當做分詞),然後插入兩條數據。註意:被搜索的欄位先採用text類型。
# 創建mapping,這裡的customerName先使用text類型
PUT /search_test
{
"mappings": {
"properties": {
"id": {
"type": "keyword"
},
"customerName": {
"type": "text"
}
}
},
"settings": {
"number_of_shards": 5,
"number_of_replicas": 1
}
}
# 插入2條數據
PUT /search_test/_create/1
{
"id": "111",
"customerName": "都是生產醫院的人"
}
PUT /search_test/_create/2
{
"id": "222",
"customerName": "家電清洗"
}
# match_phrase短語匹配查詢,可以查出結果
POST search_test/_search
{
"from": 0,
"size": 10,
"query": {
"bool": {
"must": [
{
"match_phrase": {
"customerName": "醫院的"
}
}
]
}
}
}
以上操作結果顯示可以查詢到數據。如下圖:
- 場景2:創建一個mapping,採用預設分詞器,然後插入兩條數據。註意:被搜索的欄位先採用keyword類型。
# 創建mapping,這裡的customerName先使用text類型
PUT /search_test2
{
"mappings": {
"properties": {
"id": {
"type": "keyword"
},
"customerName": {
"type": "keyword"
}
}
},
"settings": {
"number_of_shards": 5,
"number_of_replicas": 1
}
}
# 插入2條數據
PUT /search_test2/_create/1
{
"id": "111",
"customerName": "都是生產醫院的人"
}
PUT /search_test2/_create/2
{
"id": "222",
"customerName": "家電清洗"
}
# match_phrase短語匹配查詢,可以查出結果
POST search_test2/_search
{
"from": 0,
"size": 10,
"query": {
"bool": {
"must": [
{
"match_phrase": {
"customerName": "醫院的"
}
}
]
}
}
}
以上操作結果顯示查不到數據。如下圖:
2.3、小結
match_phrase短語匹配適用於text類型的欄位,實現了類似Mysql的like模糊匹配。然而,它並不適用於keyword類型的欄位。
3、通配符匹配Wildcard
為實現模糊匹配的搜索效果,Wildcard通配符匹配是另一種常見的方式。下麵我們詳細介紹wildcard通配符查詢。下麵接著說Wildcard通配符查詢。
3.1、定義
Wildcard Query 是使用通配符表達式進行查詢匹配。Wildcard Query 支持兩個通配符:
- ?,使用 ? 來匹配任意字元。
- *,使用 * 來匹配 0 或多個字元。
使用示例:
POST search_test/_search
{
"query": {
"wildcard": {
"customerName": "*測試*"
}
}
}
3.2、實驗
- 場景1:創建一個mapping,採用預設分詞器,然後插入兩條數據。註意:被搜索的欄位先採用text類型。使用上文已經創建的索引
search_test
。
# wildcard查詢
POST search_test/_search
{
"from": 0,
"size": 10,
"query": {
"bool": {
"must": [
{
"wildcard": {
"customerName": {
"value": "*醫院的*"
}
}
}
]
}
}
}
以上操作結果顯示查不到數據,如下圖:
註意:如果將DSL查詢語句改成只查“醫”,就可以查到數據,這與分詞器有關。預設分詞器將每個字都切成分詞。
# Wildcard查詢
POST search_test/_search
{
"from": 0,
"size": 10,
"query": {
"bool": {
"must": [
{
"wildcard": {
"customerName": {
"value": "*醫*"
}
}
}
]
}
}
}
- 場景2:創建一個mapping,採用預設分詞器,然後插入兩條數據。註意:被搜索的欄位先採用keyword類型。使用上文已經創建的索引
search_test2
。
POST search_test2/_search
{
"from": 0,
"size": 10,
"query": {
"bool": {
"must": [
{
"wildcard": {
"customerName": {
"value": "*醫院的*"
}
}
}
]
}
}
}
以上操作結果顯示可以查到數據,如下圖:
3.3、小結
Wildcard通配符查詢適用於keyword類型的欄位,實現了類似Mysql的like模糊匹配。然而,它不太適用於text類型的欄位。
4、選擇分詞器
上述實驗中均使用了預設分詞器的結果。接下來,我們嘗試使用IK中文分詞器
進行實驗。
4.1、實驗
- 創建一個名為
search_test3
的mapping,採用IK中文分詞器
,然後插入兩條數據。註意:被搜索的欄位先採用text類型。
PUT /search_test3
{
"mappings": {
"properties": {
"id": {
"type": "keyword"
},
"customerName": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart"
}
}
},
"settings": {
"number_of_shards": 5,
"number_of_replicas": 1
}
}
PUT /search_test3/_create/1
{
"id": "111",
"customerName": "都是生產醫院的人"
}
PUT /search_test3/_create/2
{
"id": "222",
"customerName": "家電清洗"
}
- 執行搜索,比如搜索“醫院的”,無論是match_phrase還是wildcard兩種方式都查不到數據。
POST search_test3/_search
{
"from": 0,
"size": 10,
"query": {
"bool": {
"must": [
{
"match_phrase": {
"customerName": "醫院的"
}
}
]
}
}
}
POST search_test3/_search
{
"query": {
"bool": {
"must": [
{
"wildcard": {
"customerName": {
"value": "*醫院的*"
}
}
}
]
}
},
"from": 0,
"size": 20
}
- 執行搜索,比如搜索“醫院”,match_phrase和wildcard兩種方式都可以查到數據。
POST search_test3/_search
{
"from": 0,
"size": 10,
"query": {
"bool": {
"must": [
{
"match_phrase": {
"customerName": "醫院"
}
}
]
}
}
}
POST search_test3/_search
{
"query": {
"bool": {
"must": [
{
"wildcard": {
"customerName": {
"value": "*醫院*"
}
}
}
]
}
},
"from": 0,
"size": 20
}
4.2、小結
無論是match_phrase還是wildcard兩種方式,它們的效果與選擇的分詞器密切相關。因為兩者都是對分詞進行匹配,只有匹配到了分詞,才能找到對應的文檔。
如果搜索內容正好命中了對應的分詞,就可以查詢到數據。如果沒有命中分詞,則查不到。在遇到問題時,可以使用DSL查詢查看ES的分詞情況:
POST _analyze
{
"analyzer": "ik_smart",
"text": "院的人"
}
POST _analyze
{
"analyzer": "ik_smart",
"text": "醫院的"
}
POST _analyze
{
"analyzer": "ik_max_word",
"text": "都是生產醫院的人"
}
5、總結
match_phrase和wildcard都能實現類似Mysql的like效果。然而,需要註意以下幾點:
- 如果要完全實現Mysql的like效果,最好使用預設分詞器,即每個字都切成分詞。
- match_phrase短語匹配,適合於text類型的欄位。
- Wildcard通配符查詢,適合於keyword類型的欄位。
本篇完結!感謝你的閱讀,歡迎點贊 關註 收藏 私信!!!
原文鏈接:https://mp.weixin.qq.com/s/pXGQsGs1l8msIvP2aJCfWQ