You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在ElasticSearch中实现类似LIKE的部分匹配查询?

在Elasticsearch中实现类似SQL LIKE的包含查询

嘿,我来帮你搞定这个Elasticsearch的模糊查询问题!你想要的是类似SELECT * FROM table WHERE firstname LIKE '%ma%'的效果对吧?先说说你之前的尝试为啥没生效,再给你几个实用的解决方案。

为啥你之前的查询没起作用?

咱们逐个看你试的语句:

  1. {"match": {"text": ".*ma.*"}}match查询是基于分词的全文匹配,它会把.*ma.*当成普通字符串去匹配,而不是正则表达式——Elasticsearch不会把这里的.*解析成正则符号。
  2. {"match": {"text": "*ma*"}}:同样的问题,match不支持通配符语法,*在这里只是普通字符,所以只会匹配包含*ma*的文本,而不是包含ma的文本。
  3. {"match":{"text":{"query":"ma","fuzziness":"AUTO","prefix_length":1}}}:这个是模糊匹配,它会找和ma编辑距离相近的词(比如manmad),但不是包含ma的任意位置的文本——比如Emma里的ma就匹配不到,因为它是在中间位置,模糊匹配是基于整个词的相似度,不是包含关系。

正确的实现方法

根据你的需求,这里有几种靠谱的方案,你可以根据场景选择:

1. 使用wildcard查询(快速实现,适合小数据量)

wildcard查询原生支持通配符*(匹配任意字符)和?(匹配单个字符),注意要针对keyword类型字段查询(或者字段的keyword子字段,默认mapping里text字段都会带一个),因为text字段会被分词,直接查可能得不到预期结果:

{
  "query": {
    "wildcard": {
      "firstname.keyword": "*ma*"
    }
  }
}

如果你的firstname字段本身就是keyword类型,直接写firstname就行。

2. 使用query_string查询(支持类似SQL的LIKE语法)

query_string支持更灵活的查询语法,包括通配符,写法更接近SQL的LIKE:

{
  "query": {
    "query_string": {
      "default_field": "firstname.keyword",
      "query": "*ma*"
    }
  }
}

同样,建议用keyword子字段来保证匹配原始文本。

3. 使用ngram分词器(高性能,适合频繁查询)

如果你的查询是高频需求,上面两种方法用*开头会导致Elasticsearch做全索引扫描,数据量大的时候性能很差。这时候可以提前给字段设置ngram分词器,把文本拆成连续的字符片段(比如2个字符一组),这样查询时就能利用索引高效匹配:

第一步:创建索引时配置ngram分析器

{
  "settings": {
    "analysis": {
      "analyzer": {
        "ngram_analyzer": {
          "tokenizer": "ngram_tokenizer"
        }
      },
      "tokenizer": {
        "ngram_tokenizer": {
          "type": "ngram",
          "min_gram": 2,
          "max_gram": 2 // 这里拆成2字符的片段,刚好匹配你的"ma"需求
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "firstname": {
        "type": "text",
        "analyzer": "ngram_analyzer", // 索引时用ngram拆分
        "search_analyzer": "standard" // 查询时用标准分词,避免重复拆分
      }
    }
  }
}

第二步:用普通match查询即可

{
  "query": {
    "match": {
      "firstname": "ma"
    }
  }
}

这种方法性能最优,因为索引已经提前把所有可能的2字符片段存好了,查询时直接匹配索引就行。

总结

  • 临时查询、数据量小:用wildcardquery_string
  • 高频查询、数据量大:提前配置ngram分词器,用match查询

内容的提问来源于stack exchange,提问作者Baldráni

火山引擎 最新活动