找回密码
 立即注册
首页 业界区 业界 Elasticsearch实现Mysql的Like效果

Elasticsearch实现Mysql的Like效果

表弊捞 2025-6-9 08:34:16
在Mysql数据库中,模糊搜索通常使用LIKE关键字。然而,随着数据量的不断增加,Mysql在处理模糊搜索时可能面临性能瓶颈。因此,引入Elasticsearch作为搜索引擎,以提高搜索性能和用户体验成为一种合理的选择。
1、客户的诉求

在ES中,影响搜索结果的因素多种多样,包括分词器、Match搜索、Term搜索、组合搜索等。有些用户已经养成了在Mysql中使用LIKE进行模糊搜索的习惯。若ES返回的搜索结果不符合用户的预期,可能会引发抱怨,甚至认为系统存在Bug。
谁让客户是上帝,客户是金主爸爸呢,客户有诉求,我们就得安排上。下面我们就聊聊如何用ES实现Mysql的like模糊匹配效果。
如果对Elasticsearch不太熟悉的读者,建议先阅读我之前的文章:
《5000字详说Elasticsearch入门》
《Springboot项目中使用Elasticsearch的RestClient》
《巧记Elasticsearch常用DSL语法》
2、短语匹配match_phrase

2.1、定义

为实现模糊匹配的搜索效果,通常有两种方式,其中之一是match_phrase,先说说match_phrase。
match_phrase短语匹配会对检索内容进行分词,要求这些分词在被检索内容中全部存在,并且顺序必须一致。默认情况下,这些词必须是连续的。
2.2、实验


  • 场景1:创建一个mapping,采用默认分词器(即每个字都当做分词),然后插入两条数据。注意:被搜索的字段先采用text类型。
  1. # 创建mapping,这里的customerName先使用text类型
  2. PUT /search_test
  3. {
  4.   "mappings": {
  5.     "properties": {
  6.       "id": {
  7.         "type": "keyword"
  8.       },
  9.       "customerName": {
  10.         "type": "text"
  11.       }
  12.     }
  13.   },
  14.   "settings": {
  15.     "number_of_shards": 5,
  16.     "number_of_replicas": 1
  17.   }
  18. }
  19. # 插入2条数据
  20. PUT /search_test/_create/1
  21. {
  22.   "id": "111",
  23.   "customerName": "都是生产医院的人"
  24. }
  25. PUT /search_test/_create/2
  26. {
  27.   "id": "222",
  28.   "customerName": "家电清洗"
  29. }
  30. # match_phrase短语匹配查询,可以查出结果
  31. POST search_test/_search
  32. {
  33.   "from": 0,
  34.   "size": 10,
  35.   "query": {
  36.     "bool": {
  37.       "must": [
  38.         {
  39.           "match_phrase": {
  40.             "customerName": "医院的"
  41.           }
  42.         }
  43.       ]
  44.     }
  45.   }
  46. }
复制代码
以上操作结果显示可以查询到数据。如下图:


  • 场景2:创建一个mapping,采用默认分词器,然后插入两条数据。注意:被搜索的字段先采用keyword类型。
  1. # 创建mapping,这里的customerName先使用text类型
  2. PUT /search_test2
  3. {
  4.   "mappings": {
  5.     "properties": {
  6.       "id": {
  7.         "type": "keyword"
  8.       },
  9.       "customerName": {
  10.         "type": "keyword"
  11.       }
  12.     }
  13.   },
  14.   "settings": {
  15.     "number_of_shards": 5,
  16.     "number_of_replicas": 1
  17.   }
  18. }
  19. # 插入2条数据
  20. PUT /search_test2/_create/1
  21. {
  22.   "id": "111",
  23.   "customerName": "都是生产医院的人"
  24. }
  25. PUT /search_test2/_create/2
  26. {
  27.   "id": "222",
  28.   "customerName": "家电清洗"
  29. }
  30. # match_phrase短语匹配查询,可以查出结果
  31. POST search_test2/_search
  32. {
  33.   "from": 0,
  34.   "size": 10,
  35.   "query": {
  36.     "bool": {
  37.       "must": [
  38.         {
  39.           "match_phrase": {
  40.             "customerName": "医院的"
  41.           }
  42.         }
  43.       ]
  44.     }
  45.   }
  46. }
复制代码
以上操作结果显示查不到数据。如下图:

2.3、小结

match_phrase短语匹配适用于text类型的字段,实现了类似Mysql的like模糊匹配。然而,它并不适用于keyword类型的字段。
3、通配符匹配Wildcard

为实现模糊匹配的搜索效果,Wildcard通配符匹配是另一种常见的方式。下面我们详细介绍wildcard通配符查询。下面接着说Wildcard通配符查询。
3.1、定义

Wildcard Query 是使用通配符表达式进行查询匹配。Wildcard Query 支持两个通配符:

  • ?,使用 ? 来匹配任意字符。
  • *,使用 * 来匹配 0 或多个字符。
使用示例:
  1. POST search_test/_search
  2. {
  3.   "query": {
  4.     "wildcard": {
  5.       "customerName": "*测试*"
  6.     }
  7.   }
  8. }
复制代码
3.2、实验


  • 场景1:创建一个mapping,采用默认分词器,然后插入两条数据。注意:被搜索的字段先采用text类型。使用上文已经创建的索引search_test。
  1. # wildcard查询
  2. POST search_test/_search
  3. {
  4.   "from": 0,
  5.   "size": 10,
  6.   "query": {
  7.     "bool": {
  8.       "must": [
  9.         {
  10.           "wildcard": {
  11.             "customerName": {
  12.               "value": "*医院的*"
  13.             }
  14.           }
  15.         }
  16.       ]
  17.     }
  18.   }
  19. }
复制代码
以上操作结果显示查不到数据,如下图:

注意:如果将DSL查询语句改成只查“医”,就可以查到数据,这与分词器有关。默认分词器将每个字都切成分词。
  1. # Wildcard查询
  2. POST search_test/_search
  3. {
  4.   "from": 0,
  5.   "size": 10,
  6.   "query": {
  7.     "bool": {
  8.       "must": [
  9.         {
  10.           "wildcard": {
  11.             "customerName": {
  12.               "value": "*医*"
  13.             }
  14.           }
  15.         }
  16.       ]
  17.     }
  18.   }
  19. }
复制代码

  • 场景2:创建一个mapping,采用默认分词器,然后插入两条数据。注意:被搜索的字段先采用keyword类型。使用上文已经创建的索引search_test2。
  1. POST search_test2/_search
  2. {
  3.   "from": 0,
  4.   "size": 10,
  5.   "query": {
  6.     "bool": {
  7.       "must": [
  8.         {
  9.           "wildcard": {
  10.             "customerName": {
  11.               "value": "*医院的*"
  12.             }
  13.           }
  14.         }
  15.       ]
  16.     }
  17.   }
  18. }
复制代码
以上操作结果显示可以查到数据,如下图:

3.3、小结

Wildcard通配符查询适用于keyword类型的字段,实现了类似Mysql的like模糊匹配。然而,它不太适用于text类型的字段。
4、选择分词器

上述实验中均使用了默认分词器的结果。接下来,我们尝试使用IK中文分词器进行实验。
4.1、实验


  • 创建一个名为search_test3的mapping,采用IK中文分词器,然后插入两条数据。注意:被搜索的字段先采用text类型。
  1. PUT /search_test3
  2. {
  3.   "mappings": {
  4.     "properties": {
  5.       "id": {
  6.         "type": "keyword"
  7.       },
  8.       "customerName": {
  9.         "type": "text",
  10.         "analyzer": "ik_max_word",
  11.         "search_analyzer": "ik_smart"
  12.       }
  13.     }
  14.   },
  15.   "settings": {
  16.     "number_of_shards": 5,
  17.     "number_of_replicas": 1
  18.   }
  19. }
  20. PUT /search_test3/_create/1
  21. {
  22.   "id": "111",
  23.   "customerName": "都是生产医院的人"
  24. }
  25. PUT /search_test3/_create/2
  26. {
  27.   "id": "222",
  28.   "customerName": "家电清洗"
  29. }
复制代码

  • 执行搜索,比如搜索“医院的”,无论是match_phrase还是wildcard两种方式都查不到数据。
  1. POST search_test3/_search
  2. {
  3.   "from": 0,
  4.   "size": 10,
  5.   "query": {
  6.     "bool": {
  7.       "must": [
  8.         {
  9.           "match_phrase": {
  10.             "customerName": "医院的"
  11.           }
  12.         }
  13.       ]
  14.     }
  15.   }
  16. }
  17. POST search_test3/_search
  18. {
  19.   "query": {
  20.     "bool": {
  21.       "must": [
  22.         {
  23.           "wildcard": {
  24.             "customerName": {
  25.               "value": "*医院的*"
  26.             }
  27.           }
  28.         }
  29.       ]
  30.     }
  31.   },
  32.   "from": 0,
  33.   "size": 20
  34. }
复制代码

  • 执行搜索,比如搜索“医院”,match_phrase和wildcard两种方式都可以查到数据。
  1. POST search_test3/_search
  2. {
  3.   "from": 0,
  4.   "size": 10,
  5.   "query": {
  6.     "bool": {
  7.       "must": [
  8.         {
  9.           "match_phrase": {
  10.             "customerName": "医院"
  11.           }
  12.         }
  13.       ]
  14.     }
  15.   }
  16. }
  17. POST search_test3/_search
  18. {
  19.   "query": {
  20.     "bool": {
  21.       "must": [
  22.         {
  23.           "wildcard": {
  24.             "customerName": {
  25.               "value": "*医院*"
  26.             }
  27.           }
  28.         }
  29.       ]
  30.     }
  31.   },
  32.   "from": 0,
  33.   "size": 20
  34. }
复制代码
4.2、小结

无论是match_phrase还是wildcard两种方式,它们的效果与选择的分词器密切相关。因为两者都是对分词进行匹配,只有匹配到了分词,才能找到对应的文档。
如果搜索内容正好命中了对应的分词,就可以查询到数据。如果没有命中分词,则查不到。在遇到问题时,可以使用DSL查询查看ES的分词情况:
  1. POST _analyze
  2. {  
  3.     "analyzer": "ik_smart",
  4.     "text": "院的人"  
  5. }
  6. POST _analyze
  7. {  
  8.     "analyzer": "ik_smart",
  9.     "text": "医院的"  
  10. }
  11. POST _analyze
  12. {  
  13.     "analyzer": "ik_max_word",
  14.     "text": "都是生产医院的人"  
  15. }
复制代码
5、总结

match_phrase和wildcard都能实现类似Mysql的like效果。然而,需要注意以下几点:

  • 如果要完全实现Mysql的like效果,最好使用默认分词器,即每个字都切成分词。
  • match_phrase短语匹配,适合于text类型的字段。
  • Wildcard通配符查询,适合于keyword类型的字段。
本篇完结!感谢你的阅读,欢迎点赞 关注 收藏 私信!!!
原文链接:https://mp.weixin.qq.com/s/pXGQsGs1l8msIvP2aJCfWQ

 

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册