ES当中大部分的内容都已经学习完了,今天呢算是对前面内容的查漏补缺,把ES中非常实用的功能整理一下,在以后的项目开发中,这些功能肯定是对你的项目加分的,我们来看看吧。
高亮
高亮在搜索功能中是十分重要的,我们希望搜索的内容在搜索结果中重点突出,让用户聚焦在搜索的内容上。我们看看在ES当中是怎么实现高亮的,我们还用之前的索引ik_index,前面的章节,我们搜索过香蕉好吃,但是返回的结果中并没有高亮,那么想要在搜索结果中,对香蕉好吃高亮该怎么办呢?我们看看,
1POST /ik_index/_search 2{ 3 "query": { 4 "bool": { 5 "must": { 6 "match": { 7 "desc": "香蕉好吃" 8 } 9 } 10 } 11 }, 12 "highlight": { 13 "fields": { 14 "desc": {} 15 } 16 } 17}
我们重点看一下请求体中的highlight部分,这部分就是对返回结果高亮的设置,fields字段中,指定哪些字段需要高亮,我们指定了desc字段,执行一下,看看结果吧。
1{ 2 "took": 73, 3 "timed_out": false, 4 "_shards": { "total": 1,"successful": 1,"skipped": 0,"failed": 0}, 5 "hits": { 6 "total": { 7 "value": 5, 8 "relation": "eq" 9 }, 10 "max_score": 1.3948275, 11 "hits": [ 12 { 13 "_index": "ik_index", 14 "_type": "_doc", 15 "_id": "2", 16 "_score": 1.3948275, 17 "_source": { 18 "id": 1, 19 "title": "香蕉", 20 "desc": "香蕉真好吃" 21 }, 22 "highlight": { 23 "desc": [ 24 "<em>香蕉</em>真<em>好吃</em>" 25 ] 26 } 27 } 28 ……
我们看到在返回的结果中,增加了highlight,highlight里有我们指定的高亮字段desc,它的值是<em>香蕉</em>真<em>好吃</em>,其中“香蕉”和“好吃”字段在<em>标签中,前端的小伙伴就可以针对这个<em>标签写样式了。我们再看看程序当中怎么设置高亮,继续使用上一节中的搜索的程序,
1 public void searchIndex() throws IOException { 2 SearchRequest searchRequest = new SearchRequest("ik_index"); 3 SearchSourceBuilder ssb = new SearchSourceBuilder(); 4 QueryBuilder qb = new MatchQueryBuilder("desc","香蕉好吃"); 5 ssb.query(qb); 6 7 HighlightBuilder highlightBuilder = new HighlightBuilder(); 8 highlightBuilder.field("desc"); 9 10 ssb.highlighter(highlightBuilder); 11 12 searchRequest.source(ssb); 13 SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT); 14 15 SearchHit[] hits = response.getHits().getHits(); 16 for (SearchHit hit : hits) { 17 String record = hit.getSourceAsString(); 18 HighlightField highlightField = hit.getHighlightFields().get("desc"); 19 20 for (Text fragment : highlightField.getFragments()) { 21 System.out.println(fragment.string()); 22 } 23 } 24 }
我们重点关注一下HighlightBuilder,我们在发送请求前,创建HighlightBuilder,并指定高亮字段为desc。搜索结束后,我们取结果,从hit当中取出高亮字段desc,然后打印出fragment,运行一下,看看结果吧,
1<em>香蕉</em>真<em>好吃</em> 2<em>香蕉</em>真<em>好吃</em> 3橘子真<em>好吃</em> 4桃子真<em>好吃</em> 5苹果真<em>好吃</em>
完全符合预期,“香蕉好吃”被分词后,在搜索结果中都增加了<em>标签,我们可不可以自定义高亮标签呢?当然是可以的,我们稍微改一下程序就可以了,
1HighlightBuilder highlightBuilder = new HighlightBuilder(); 2highlightBuilder.field("desc"); 3 4highlightBuilder.preTags("<b>"); 5highlightBuilder.postTags("</b>"); 6 7ssb.highlighter(highlightBuilder);
在HighlightBuilder中,使用preTags添加起始标签,指定为<b>,用postTags添加闭合标签,指定为</b>,再运行一下,看看结果,
1<b>香蕉</b>真<b>好吃</b> 2<b>香蕉</b>真<b>好吃</b> 3橘子真<b>好吃</b> 4桃子真<b>好吃</b> 5苹果真<b>好吃</b>
结果完全正确,用<b>替换了<em>,是不是很灵活。接下来我们再看看搜索建议。
搜索建议
“搜索建议”这个功能也是相当实用的,当我们在搜索框中输入某个字时,与这个字的相关搜索内容就会罗列在下面,我们选择其中一个搜索就可以了,省去了敲其他字的时间。我们看看ES中是怎么实现“搜索建议”的。
如果要在ES中使用“搜索建议”功能,是需要特殊设置的,要设置一个类型为completion的字段,由于之前的索引中已经有了数据,再添加字段是会报错的,索引我们新建一个索引,
1PUT /my_suggester 2{ 3 "settings":{ 4 "analysis":{ 5 "analyzer":{ 6 "default":{ 7 "type":"ik_max_word" 8 } 9 } 10 } 11 }, 12 "mappings":{ 13 "dynamic_date_formats": [ 14 "MM/dd/yyyy", 15 "yyyy/MM/dd HH:mm:ss", 16 "yyyy-MM-dd", 17 "yyyy-MM-dd HH:mm:ss" 18 ], 19 "properties":{ 20 "suggest":{ 21 "type":"completion" 22 } 23 } 24 } 25}
这已经成了我们新建索引的一个标配了,指定分词器为ik中文分词,动态字段的时间映射格式,以及搜索建议字段,注意suggest字段的类型为completion。我们再添加字段的时候,就要为suggest字段添加值了,如下:
1POST /my_suggester/_doc 2{ 3 "title":"天气", 4 "desc":"今天天气不错", 5 "suggest": { 6 "input": "天气" 7 } 8} 9 10POST /my_suggester/_doc 11{ 12 "title":"天空", 13 "desc":"蓝蓝的天空,白白的云", 14 "suggest": { 15 "input": "天空" 16 } 17}
我们向索引中添加了两条数据,大家需要额外注意的是suggest字段的赋值方法,要使用input,我们看一下数据,

suggest字段并没有像其他字段那样展示出来,说明它和其他字段是不一样的。现在我们如果只输入一个“天”字,看看搜索建议能不能给出提示,如下:
1POST /my_suggester/_search 2{ 3 "suggest": { 4 "s-test": { 5 "prefix": "天", 6 "completion": { 7 "field": "suggest" 8 } 9 } 10 } 11}
在请求体中,suggest就是“搜索建议”的标识,s-test是自定义的一个名称,prefix是前缀,也就是我们输入的“天”字,completion指定搜索建议的字段,我们看看查询的结果,
1…… 2"suggest": { 3 "s-test": [ 4 { 5 "text": "天", 6 "offset": 0, 7 "length": 1, 8 "options": [{ 9 "text": "天气", 10 "_index": "my_suggester", 11 "_type": "_doc", 12 "_id": "QtgAWnIBOZNtuLQtJgpt", 13 "_score": 1, 14 "_source": { "title": "天气","desc": "今天天气不错","suggest": { "input": "天气"}} 15 } 16 , 17 { 18 "text": "天空", 19 "_index": "my_suggester", 20 "_type": "_doc", 21 "_id": "T9gAWnIBOZNtuLQtWQoX", 22 "_score": 1, 23 "_source": { "title": "天空","desc": "蓝蓝的天空,白白的云","suggest": { "input": "天空"}} 24 } 25 ] 26 } 27 ] 28}
在s-test.options里,包含了两条记录,text字段就是我们写的建议字段,后面_source里还包含对应的数据,下面我们再看看程序里怎么使用“搜索建议”,
1public void searchSuggest(String prefix) throws IOException { 2 SearchRequest searchRequest = new SearchRequest("my_suggester"); 3 SearchSourceBuilder ssb = new SearchSourceBuilder(); 4 5 CompletionSuggestionBuilder suggest = SuggestBuilders 6 .completionSuggestion("suggest") 7 .prefix(prefix); 8 9 SuggestBuilder suggestBuilder = new SuggestBuilder(); 10 suggestBuilder.addSuggestion("s-test",suggest); 11 12 ssb.suggest(suggestBuilder); 13 14 searchRequest.source(ssb); 15 SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT); 16 17 CompletionSuggestion suggestion = response.getSuggest().getSuggestion("s-test"); 18 for (CompletionSuggestion.Entry.Option option : suggestion.getOptions()) { 19 System.out.println(option.getText().string()); 20 } 21 22} 23 24@Test 25public void searchSuggest() throws IOException { 26 eService.searchSuggest("天"); 27}
我们创建了CompletionSuggestionBuilder,通过方法completionSuggestion指定“搜索建议”字段suggest,并且指定前缀为方法传入的prefix,我们在测试的时候传入"天"字。然后,我们自定义“搜索建议”的名字为s-test,传入前面构造好的suggest。
发送请求后,在响应中获取前面自定义的s-test,然后循环options,取出text字段,这就是搜索建议的字段,我们运行一下,看看结果,
1天气 2天空
完全符合预期,这样用户在搜索的时候,就会给出提示信息了。
好了,今天这两个ES的知识点就全部OK了~ 大家有问题在评论区留言。