ElasticSearch中text类型的模糊匹配和精确匹配

1,846 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

ElasticSearch中text类型的模糊匹配和精确匹配

需求描述

对text类型的字段需要进行模糊匹配和精确匹配

需求分析

目前文档定义如下:

"diagnoseDoc": {
    "type": "nested",
    "properties": {
        "fieldValue": {
            "type": "text",
            "analyzer": "ik_max_word",
            "search_analyzer": "ik_smart",
            "fields":{
              "keyword": {
                "type": "text",
                "analyzer":"my_analyzer"
              }
            }
        }
    }
},

解决方案一

wildcard 检索

wildcard 检索支持通配符的模糊搜索,类似于mysql中的like模糊匹配

代码

if(StringUtils.isNotEmpty(searchContent)){
   searchContent = "*"+searchContent+"*";
   BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
   WildcardQueryBuilder goodName = QueryBuilders.wildcardQuery("goodName", searchContent);
   boolQueryBuilder.should(goodName);
   boolQuery.must(boolQueryBuilder);
}

效果相当于:

select * from 表名 where goodName like "%searchContent%"

通配符说明

* 代表匹配多个字符

?代表匹配单个字符

存在的问题

官方文档描述:

image.png 避免以*或?开头的模式。这会增加查找匹配项所需的迭代次数并降低搜索性能。

  • 出现问题

用户输入的字符串长度没有做限制,导致首尾通配符中间可能是很长的一个字符串。后果就是对应的wildcard Query执行非常慢,非常消耗CPU。

  • 根本原因

为了加速通配符和正则表达式的匹配速度,Lucene4.0开始会将输入的字符串模式构建成一个DFA (Deterministic Finite Automaton),带有通配符的pattern构造出来的DFA可能会很复杂,开销很大。

当搜索词的数据量很大的时候,会造成严重的性能问题!

解决方案二

Ngram分词

构建索引文档:

PUT test
{
  "settings": {
    "index.max_ngram_diff": 10,
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "my_tokenizer"
        }
      },
      "tokenizer": {
        "my_tokenizer": {
          "type": "ngram",
          "min_gram": 1,
          "max_gram": 2,
          "token_chars": [
            "letter",
            "digit"
          ]
        }
      }
    }
  },
    "mappings": {
        "properties": {
          "goodDoc": {
                "type": "nested",
                "properties": {
                    "desc": {
                        "type": "text",
                        "analyzer": "ik_max_word",
                        "search_analyzer": "ik_smart",//ik分词器
                        "fields":{
                          "keyword": {
                            "type": "text",
                            "analyzer":"my_analyzer"//ngrm分词器
                          }
                        }
                    }
                }
            }
        }
    }
}

对于goodDoc.desc这个字段需要进行模糊匹配和精准匹配,字段类型为text,本身可以进行模糊匹配,比如搜颜色,可以再内置同义词,搜索出白色红色等。这种设计没有办法满足精准匹配,需要再内置fields字段,通过goodDoc.desc.keyword去调用,这个字段再定义一个分词器,就可以满足我们的需求。

GET /rdr_serarch_fenci/_search
{
  "query":{
    "bool" : {
      "must" : [
        {
          "nested" : {
            "query" : {
              "bool" : {
                "must" : [
                  {
                    "match" : {
                      "goodDoc.desc.keyword" : {
                        "query" : "颜色",
                        "operator" : "OR",
                        "prefix_length" : 0,
                        "max_expansions" : 50,
                        "fuzzy_transpositions" : true,
                        "lenient" : false,
                        "zero_terms_query" : "NONE",
                        "auto_generate_synonyms_phrase_query" : true,
                        "boost" : 1.0
                      }
                    }
                  }
                ],
                "adjust_pure_negative" : true,
                "boost" : 1.0
              }
            },
            "path" : "goodDoc",
            "ignore_unmapped" : false,
            "score_mode" : "none",
            "boost" : 1.0,
            "inner_hits" : {
              "name" : "goodDoc",
              "ignore_unmapped" : false,
              "from" : 0,
              "size" : 3,
              "version" : false,
              "seq_no_primary_term" : false,
              "explain" : false,
              "track_scores" : false,
              "highlight" : {
                "fields" : {
                  "goodDoc.desc.keyword" : { }
                }
              }
            }
          }
        }
      ],
      "adjust_pure_negative" : true,
      "boost" : 1.0
    }
  }
}