浮头导航网

专注编程技术分享的开发者社区

MySQL 到 Elasticsearch:如何实现全模糊查询

在实际工作中,模糊查询(类似 MySQL 的 LIKE '%keyword%')是常见需求。例如,输入“手机”时,希望返回“智能手机”“手机壳”等商品,MySQL 的 LIKE 查询简单直观,但在大规模数据场景下,全表扫描会导致查询耗时激增。

本文带你实现Elasticsearch 全模糊查询,轻松搞定!

使用wildcard词项搜索

wildcard 查询是最直观的实现方式,语法类似 MySQL 的 LIKE '%keyword%',通过 * 通配符匹配字段中的任意子串。

索引数据

准备一些测试数据:

POST _bulk
{"index":{"_index":"post","_id":1}}
{"title":"Elasticsearch入门到精通"}
{"index":{"_index":"post","_id":2}}
{"title":"MySQL数据库优化"}
{"index":{"_index":"post","_id":3}}
{"title":"21天精通MySQL数据库"}
{"index":{"_index":"post","_id":4}}
{"title":"21天精通SQL语言"}

wildcard 查询

要查找标题中包含“MySQL”的文档,可以使用以下 wildcard 查询:

GET post/_search
{
  "query": {
    "wildcard": {
      "title.keyword": {
        "value": "*MySQL*"
      }
    }
  }
}

查询结果如下图所示:

image.png

虽然wildcard 查询轻松实现了全模糊查询,但性能极差,因为需要遍历倒排索引中的所有词条,在数据量较大(百万级以上)时,可能导致查询延迟高甚至集群崩溃。

N-gram 分词器

为了解决 wildcard 的性能问题,N-gram 分词器是一个更高效的方案。它在索引时将文本拆分成连续的字符片段(grams),从而支持高效的子串匹配。

例如:对于文本 “你好世界”,使用n-gram 分词器(默认配置)会将其拆分为:[ "你","你好", "好","好世","世","世界","界" ]。

配置 N-gram 分词器

PUT post_v2
{
  "settings": {
    "analysis": {
      "analyzer": {
        "ngram_analyzer": {
          "tokenizer": "ngram_tokenizer"
        }
      },
      "tokenizer": {
        "ngram_tokenizer": {
          "type": "ngram",
    "min_gram": 1,
          "max_gram": 2
        }
      }
    }
  },
  // title.ngram 字段专门用于模糊查询
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "fields": {
          "ngram": {  
            "type": "text",
            "analyzer": "ngram_analyzer"
          }
        }
      }
    }
  }
}

min_gram 和 max_gram 控制切片长度,详细说明请查看官方文档。

索引数据

使用与之前相同的测试数据:

POST _bulk
{"index":{"_index":"post_v2","_id":1}}
{"title":"Elasticsearch入门到精通"}
{"index":{"_index":"post_v2","_id":2}}
{"title":"MySQL数据库优化"}
{"index":{"_index":"post_v2","_id":3}}
{"title":"21天精通MySQL数据库"}
{"index":{"_index":"post_v2","_id":4}}
{"title":"21天精通SQL语言"}

执行查询

使用match 查询在title.ngram 字段上搜索“MySQL”:

GET post_v2/_search
{
  "query": {
    "match": {
      "title.ngram": "MySQL"
    }
  }
}

查询结果如下图所示:

image.png

查询结果返回了不符合要求的数据:"21天精通SQL语言",这是因为match查询默认情况下使用 or 逻辑,修改operator = and 即可。

GET post_v2/_search
{
  "query": {
    "match": {
      "title.ngram": {
        "query":"MySQL",
        "operator": "and"
      }
    }
  }
}

缺点:生成了大量的 n-gram 词条,索引会比原来大很多


感谢您的阅读!希望这部分内容对您有所启发。如果您觉得有价值,请不吝点赞支持,并在评论区留下您的想法,一起交流学习!别忘了关注我哦!

在实际开发中,您如何实现Elasticsearch的全模糊,欢迎在评论区分享!

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言