本文已参与「新人创作礼」活动,一起开启掘金创作之路。
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%"
通配符说明
* 代表匹配多个字符
?代表匹配单个字符
存在的问题
官方文档描述:
避免以*或?开头的模式。这会增加查找匹配项所需的迭代次数并降低搜索性能。
- 出现问题
用户输入的字符串长度没有做限制,导致首尾通配符中间可能是很长的一个字符串。后果就是对应的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
}
}
}