一文带你彻底搞懂Elasticsearch中的模糊查询

写在前面

Elasticsearch(以下简称ES)中的模糊查询官方是建议慎用的,因为的它的性能不是特别好。不过这个性能不好是相对ES自身的其它查询(term,match)而言的,如果跟其它的搜索工具相比ES的模糊查询性能还是不错的。

ES都多种方法可以支持模糊查询,比如wildcard,query_string等,这篇文章可能是全网最全的关于模糊查询的技术博客(哈哈)。

可以支持模糊查询的方案

wildcard

wildcard的用法是这样的,

GET kibana_sample_data_flights/_search
{
  "query": {
    "wildcard": {
      "OriginCityName": {
        "value": "Frankfurt*"
      }
    }
  }
}

*或者?也可以放在前面,但是不建议这么做,最好是前缀开始避免太大的性能消耗。查询的字段可以是text类型也可以是keyword类型,两种都支持。

大小写的话默认情况下,是根据字段本身是否对大小写敏感决定的。什么意思呢?比如上面那个查询,OriginCityName字段是keyword类型,我们知道keyword是要求精确匹配,自然就是大小写敏感的。所以如果用下面的这个查询,结果是不一样的:

GET kibana_sample_data_flights/_search
{
  "query": {
    "wildcard": {
      "OriginCityName": {
        "value": "frankfurt*"
      }
    }
  }
}

如果查询的字段是text类型,wildcard模糊查询的时候就是大小写不敏感的。

前面说过,模糊查询的性能都不高,wildcard也不例外。不过在ES7.9中引入了一种新的wildcard 字段类型,该字段类型经过优化,可在字符串值中快速查找模式。

PUT my-index
{
  "mappings": {
    "properties": {
      "my_wildcard": {
        "type": "wildcard"
      }
    }
  }
}

在引入这个字段类型之前,wildcard要么是在text类型字段查找,要么是keyword类型。而wildcard类型做了特殊的处理,如果某个字段指定了wildcard类型,

  • 与 text 字段不同,它不会将字符串视为由标点符号分隔的单词的集合。
  • 与 keyword 字段不同,它可以快速地搜索许多唯一值,并且没有大小限制。

wildcard字段类型通过两种优化的数据结构提高模糊查询的性能,一种使用n-gram分词器,这个分词器不打算在这里详细讲,只需要知道它会把单词在继续细分存储就行,比如,

POST _analyze
{
  "tokenizer": "ngram",
  "text": "Quick Fox"
}

输出的是,

[ Q, Qu, u, ui, i, ic, c, ck, k, "k ", " ", " F", F, Fo, o, ox, x ]

相当于把可能用于模糊查询的词项都提前拆分好存储了,这样就减少了查询阶段需要比较的词项。

第二种数据结构是binary doc value,可以自动查询验证由 n-gram 语法匹配产生的匹配候选,关于它的具体介绍可以参考下面这篇文章:

https://www.amazingkoala.com.cn/Lucene/DocValues/2019/0412/49.html

fuzzy

fuzzy也是一种模糊查询,我理解它其实属于比较轻量级别的模糊查询。fuzzy中有个编辑距离的概念,编辑距离是对两个字符串差异长度的量化,及一个字符至少需要处理多少次才能变成另一个字符,比如lucene和lucece只差了一个字符他们的编辑距离是1。

因为可以限制编辑距离,它的性能相对会好一些,毕竟它不是完全的“模糊”。

这样说可能有点抽象,看个例子,

先写入一些测试数据,

POST /my_index/_bulk
{ "index": { "_id": 1 }}
{ "text": "Surprise me!"}
{ "index": { "_id": 2 }}
{ "text": "That was surprising."}
{ "index": { "_id": 3 }}
{ "text": "I wasn't surprised."}

然后我们可以这样查询,

GET /my_index/_search
{
  "query": {
    "fuzzy": {
      "text": "surprize"
    }
  }
}

查询结果是文档1和文档3会被查询出来,surprise 比较 surprise 和 surprised 都在编辑距离 2 以内。为什么默认值2呢,其实fuzzy有个fuzziness参数,可以赋值为0,1,2和AUTO,默认其实是AUTO。

AUTO的意思是,根据查询的字符串长度决定允许的编辑距离,规则是:

  • 0..2 完全匹配(就是不允许模糊)
  • 3..5 编辑距离是1
  • 大于5 编辑距离是2

其实我们仔细想一下,即使限制了编辑距离,查询的字符串比较长的情况下需要查询的词项也是非常巨大的。所以fuzzy还有一个选项是prefix_length,表示不能被 “模糊化” 的初始字符数,通过限制前缀的字符数量可以显著降低匹配的词项数量。

query string

query string query是ES的一种高级搜索,它支持复杂的搜索方式比如操作符,可以用类似

"query": "this AND that"

这样的组合操作语法。

query string支持wildcard,并且查询的字段名和查询字符串都可以使用wildcard,比如:

GET /_search
{
  "query": {
    "query_string" : {
      "fields" : ["city.*"],
      "query" : "this AND that OR thus"
    }
  }
}
GET /_search
{
  "query": {
    "query_string" : {
      "query" : "city.\\*:(this AND that OR thus)"
    }
  }
}

所以query string对模糊搜索的支持本质上还是wildcard。

prefix 前缀查询

这种只支持前缀查询,属于模糊查询的子集。比如要查找所有以 W1 开始的邮编,可以使用简单的 prefix 查询。

GET /my_index/_search
{
    "query": {
        "prefix": {
            "postcode": "W1"
        }
    }
}

prefix的工作原理这里也简单说下。我们知道文档在写入ES时会建立倒排索引,倒排索引都会将包含词的文档 ID 列入 倒排表(postings list),下面是一个示例:

Term

Doc IDs

"SW5 0BE"

5

"W1F 7HW"

3

"W1V 3DG"

1

"W2F 8HW"

2

"WC1N 1LZ"

4

查询的步骤是:

  1. 扫描postings list并查找到第一个以 W1 开始的词。
  2. 搜集关联的文档 ID 。
  3. 移动到下一个词。如果这个词也是以 W1 开头,查询跳回到第二步再重复执行,直到下一个词不以 W1 为止。

可以看到,如果倒排表比较大,满足前缀的词项比较多的情况下,查询的代价也是非常大的。不过对于前缀查询ES提供了一种名叫index_prefixes的机制来提高查询性能。

原理也比较简单,就是字段在mapping中指定index_prefixes,然后ES在索引的时候就会把指定范围的前缀都先存起来,这样查询的时候需要比较的次数就会大大降低。

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "body_text": {
        "type": "text",
        "index_prefixes": { }    
      }
    }
  }
}

regexp正则表达式模糊查询

regexp对模糊查询的支持更智能,它能支持更为复杂的匹配模式。比如下面这个示例

GET /my_index/_search
{
    "query": {
        "regexp": {
            "postcode": "W[0-9].+" 
        }
    }
}

这个正则表达式要求词必须以 W 开头,紧跟 0 至 9 之间的任何一个数字,然后接一或多个其他字符。

regexp 查询的工作方式与 prefix 查询基本是一样的,需要扫描倒排索引中的词列表才能找到所有匹配的词,然后依次获取每个词相关的文档 ID。

参考:

  • https://www.elastic.co/guide/en/elasticsearch/reference/7.11/index.html
  • https://www.elastic.co/cn/blog/find-strings-within-strings-faster-with-the-new-elasticsearch-wildcard-field

注:以上内容来源于一文带你彻底搞懂Elasticsearch中的模糊查询 - 云+社区 - 腾讯云

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Elasticsearch,可以使用模糊查询来查找包含与搜索术语相似的文档。Elasticsearch提供了不同类型的模糊查询: 1. 通配符查询(Wildcard Query):使用通配符匹配查询关键字的任何字符,*代表零个或多个字符,?代表一个字符。 例如,要查找包含单词"colours"的文档,可以使用通配符查询:"colo*" 2. 正则表达式查询(Regular Expression Query):使用正则表达式匹配查询关键字的任何字符。 例如,要查找包含单词"color"或"colour"的文档,可以使用正则表达式查询:"/colou?r/" 3. 模糊查询(Fuzzy Query):通过模糊匹配查询关键字的单词来查找文档。模糊查询使用编辑距离算法计算相似度。 例如,要查找包含单词"color"或"colour"的文档,可以使用模糊查询:"color~1",其1表示编辑距离为1。 4. 模糊匹配查询(Match Query with Fuzziness):与模糊查询类似,但是可以使用match查询来搜索特定字段。 例如,要在标题字段查找包含单词"color"或"colour"的文档,可以使用模糊匹配查询:"match": { "title": { "query": "color", "fuzziness": "1" } },其fuzziness表示编辑距离为1。 请注意,模糊查询可能会导致搜索结果的数量大大增加,因此建议使用时谨慎。 ### 回答2: Elasticsearch是一个开源的分布式搜索和分析引擎,提供强大的全文搜索功能。在Elasticsearch模糊查询是一种可以匹配包含部分关键词的搜索查询。 Elasticsearch模糊查询主要通过两种方式实现:通配符查询和模糊查询。 通配符查询使用通配符符号(*)来匹配任意字符或字符序列。例如,如果我们想要找到包含以"el"开头的单词,我们可以使用查询字符串"el*"进行模糊查询。这将匹配到"elastic"、"elephant"等单词。通配符查询虽然强大,但是它的性能相对较低并且不会被缓存,因此在实际使用应该谨慎使用。 另一种模糊查询的方式是使用模糊查询语法。模糊查询可以通过添加模糊符号(~)来匹配指定相似度的单词。例如,如果我们想要找到包含类似于"elastic"的单词,我们可以使用查询字符串"elastic~"进行模糊查询模糊查询默认的相似度是0.5,如果我们想要调整相似度,可以使用~后面添加一个介于0和1之间的小数来指定。 除了以上两种方式,Elasticsearch还提供了其他一些模糊查询的功能,如正则表达式查询、模糊匹配和距离匹配等。这些功能可以根据具体的需求进行使用,提供更精确的模糊查询结果。 总而言之,Elasticsearch模糊查询功能可以通过通配符查询和模糊查询语法来实现,可以根据具体的需求选择合适的方式进行查询。模糊查询是搜索引擎常用的一种查询方式,可以帮助用户找到包含部分关键词的相关文档。 ### 回答3: Elasticsearch是一个开源搜索引擎,具有强大的模糊查询功能。模糊查询是指在搜索过程,允许用户进行模糊匹配,以便找到与查询条件相似的结果。 在Elasticsearch模糊查询可以通过使用通配符、近似匹配模糊匹配来实现。以下是几种常见的模糊查询方法: 1. 通配符查询:可以使用通配符符号(*)表示任意字符或字符序列。例如,如果想要搜索包含"elast"开头的单词,可以使用通配符查询"elast*"。 2. 近似匹配Elasticsearch可以使用编辑距离算法进行近似匹配。编辑距离是通过添加、删除或替换一个字符来将一个字符串转换为另一个字符串所需的最小操作次数。可以通过设置fuzziness参数来控制模糊度,从而实现近似匹配。例如,可以搜索与"elastic"相似(编辑距离为1)的单词,使用模糊查询"elastic~1"。 3. 模糊匹配模糊匹配是一种使用模糊度来匹配查询条件的方法。可以通过设置fuzziness参数来控制模糊度的程度。例如,使用模糊查询"elastic"可以匹配到类似"eleastic"或"elasstic"的单词。 值得注意的是,模糊查询可能会导致搜索结果的准确性下降,因为它允许不严格匹配。因此,在使用模糊查询时需要权衡结果的准确性和查询的覆盖范围。 总之,Elasticsearch模糊查询功能可以通过通配符、近似匹配模糊匹配来实现。这些功能可以灵活地满足用户对模糊查询的需求,并在搜索过程提供更好的匹配和相关性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值