1、接上一篇介绍索引实体类中相关注解
正常指定索引类型可根据自己的业务设置 @Field(type=FieldType.Integer),当前涉及中文分词设置 @Field(type=FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
如果索引字段需要中文分词和拼音分词可以这样设置: @MultiField(mainField = @Field(type=FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_max_word"),
otherFields = @InnerField(type=FieldType.Text,suffix = "pinyin",analyzer = "pinyin"))
2、分词
中文分词目前用的较多的是ik分词插件 注意的点是需要进行分词的字段需要设置type=FieldType.Text,不需要分词的可以指定type = FieldType.Keyword
ik分词器支持最大分词ik_max_word和最小分词ik_smart,这个需要根据自己的业务选择。比如本猿开发的是C端商品搜索,所以选择了最大分词ik_max_word配合全文索引matchQuery使用。
3、分词查询
match query 既能全文检索又能精确查询,配合ik分词后能符合大部分查询。
例如:你需要对商品名称进行分词查询,不在乎分词后的各词根的距离与顺序 只需要全部满足用户输入的所有关键词,那么你可以这样查询:
QueryBuilders.matchQuery("skuTitle",keyWord).operator(Operator.AND)
skuTitle是商品标题,keyWord是用户输入的关键词,Operator.AND表示商品标题要完全匹配用户输入的关键词才能命中索引
Operator.OR 表示商品标题中只要有任一词匹配上就会命中索引 当然这需要结合评分排序 评分越高应该越排在前面。
4、短语查询、前缀查询、普通termQuery查询 模糊查询
短语查询matchPhrasePrefixQuery 完全匹配,关键词的顺序必须一致
前缀查询matchBoolPrefixQuery 顾名思义,前缀匹配查询
termQuery精确查询 完全匹配
模糊查询wildcardQuery 等价于sql中的like 左模糊和全模糊慎用
多字段组合查询 multiMatchQuery,例如:
Map<String,Float> fieldMap = Maps.newHashMap();
fieldMap.put("skuTitle",10f);
fieldMap.put("productAlias",10f);
QueryBuilders.multiMatchQuery(indexReqDTO.getKeyStr(),"skuTitle","virtualTitle","productAlias").operator(Operator.AND).fields(fieldMap);
其中的fieldMap 是设置字段的查询权重。
5、模糊查询优化
ES中需要慎用 模糊查询wildcardQuery,特别是全模糊和左模糊,而且还要限制进行模糊查询的关键词长度。
原因是为了加速通配符和正则表达式的匹配速度,Lucene4.0开始会将输入的字符串模式构建成一个DFA (Deterministic Finite Automaton),带有通配符的pattern构造出来的DFA可能会很复杂,开销很大,CPU消耗很高。
所以此处提供两种方案来代替wildcardQuery,一、ES7.9中引入了一种新的wildcard 字段类型,该字段类型经过优化,可在字符串值中快速查找模式,也就是需要升级你的ES版本至7.9及以上。
二、使用nGram分词 + match_phrase查询,nGram是Es自带的默认分词,只需要设置setting,例如:
final String setting = " {\"analysis\": {\"analyzer\": {\"ngram_analyzer\":{\"tokenizer\":\"ngram_tokenizer\"}},\"tokenizer\": {\"ngram_tokenizer\":{\"type\":\"ngram\",\"min_gram\":1,\"max_gram\":2,\"token_chars\":[\"letter\",\"digit\"]}}}}";
if(!elasticsearchRestTemplate.indexOps(SuggestWordIndex.class).exists()){
Document settingDoc = Document.parse(setting);
IndexOperations indexOps = elasticsearchRestTemplate.indexOps(SuggestWordIndex.class);
indexOps.create(settingDoc);
Document document= indexOps.createMapping(SuggestWordIndex.class);
indexOps.putMapping(document);
}
elasticsearchRestTemplate.save(suggestWordIndexList);
然后在索引实体类的对应字段上@Field(type = FieldType.Text, analyzer = "ngram_analyzer"),这样使用QueryBuilders.matchPhraseQuery("hitWordNgram",word)就可以实现模糊查询的效果了。