SpringBoot集成ElasticSearch前缀提示功能

SpringBoot集成ElasticSearch前缀提示功能,第1张

SpringBoot集成ElasticSearch前缀提示功能 1.问题起因

最近在做项目的时候遇到了一个问题,就是在做搜索功能的时候,需要像各类搜索网站和电商网站一样,可以在输入第一个字或者拼音的时候提示出后续数据库内存在的内容供用户自行选择或者继续输入,如下:

因为用的是ElasticSearch,所以在网上给出的答案是:

那么类似的功能在Elasticsearch里如何实现呢? 答案就在Suggesters API。 Suggesters基本的运作原理是将输入的文本分解为token,然后在索引的字典里查找相似的term并返回。 根据使用场景的不同,Elasticsearch里设计了4种类别的Suggester,分别是:
Term Suggester
Phrase Suggester
Completion Suggester
Context Suggester
精准程度上(Precision)看: Completion >  Phrase > term, 而召回率上(Recall)则反之。从性能上看,Completion Suggester是最快的,如果能满足业务需求,只用Completion Suggester做前缀匹配是最理想的。 Phrase和Term由于是做倒排索引的搜索,相比较而言性能应该要低不少,应尽量控制suggester用到的索引的数据量,最理想的状况是经过一定时间预热后,索引可以全量map到内存。

遂决定用Completion Suggester,后续还可以根据需要添加Phrase Suggester,当需要用户填写错误时提供近似的提示词。但是翻了一圈下来发现要不然就是只有直接讲ElasticSearch语法的,要不然就是用6.x版本时期的单体插件的教程,让已经使用了和SpringBoot集成版本的我十分捉急,又找不到集成版的官方文档或者教程一类,只能自己慢慢摸索,在这里就重新记录一下如何 *** 作这个事情。

2.前期准备

这里还是要稍微说一下,ElasticSearch的版本是7.6.1,集成在SpringBoot2.3.2版本上。

  1. 导入依赖:

    
        org.springframework.boot
        spring-boot-starter-data-elasticsearch
    
    
  2. 配置类

    @Configuration
    public class ElasticSearchClientConfig {
        @Value("${el.host}")
        private String host;
    
        @Value("${el.port}")
        private int port;
    
        @Bean
        public RestHighLevelClient restHighLevelClient () {
            RestHighLevelClient client = new RestHighLevelClient(
                    RestClient.builder(new HttpHost(host,port,"http"))
            );
            return client;
        }
    }
    
3. *** 作
  1. 首先是创建索引,因为需要使用suggest,索引属性类型,type=Completion,不然会报错:

    type=illegal_argument_exception, reason=Field [suggest] is not a completion suggest field]

    //由于用到suggest所以需要手动创建一下索引对suggest赋值类型
    CreateIndexRequest createIndexRequest = new CreateIndexRequest("news");
    Map jsonMap = new HashMap<>();
    Map suggest = new HashMap<>();
    suggest.put("type", "completion");
    Map properties = new HashMap<>();
    properties.put("suggest", suggest);
    Map news = new HashMap<>();
    newsInfo.put("properties", properties);
    jsonMap.put("news", news);
    createIndexRequest.mapping("news", jsonMap);
    //由于版本问题这里create的时候会提示已过时,将会在版本8中删除。
    client.indices().create(createIndexRequest,RequestOptions.DEFAULT);
    
  2. 根据自身需要导入数据

    newsInfoDao.selectList(null).stream().forEach(item -> {
         bulkRequest.add(new IndexRequest("news").source(JSONUtil.toJsonStr(item), XContentType.JSON));
    });
    
    public class News {
        private Long newsId;
        private String title;
        private String content;
        private String suggest;
    }
    
  3. serviceImpl的检索功能,如果需要添加拼音查询的话,还可以多建立一个CompletionSuggestionBuilder,add到suggestBuilder里面。

    @Override
    public List> completeKeywordInfo(String keyword) {
        //标题的Completion Suggestion
        CompletionSuggestionBuilder suggestion  =
                SuggestBuilders.completionSuggestion("suggest").prefix(keyword).skipDuplicates(true).size(5);
        SuggestBuilder suggestBuilder = new SuggestBuilder();
        suggestBuilder.addSuggestion("suggest_title", suggestion);
    
        // 构建搜索
        SearchRequest searchRequest = new SearchRequest("news");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.suggest(suggestBuilder);
        sourceBuilder.timeout(new Timevalue(60, TimeUnit.SECONDS));
        searchRequest.source(sourceBuilder);
    
        //检索
        SearchResponse search = null;
        try {
            search = client.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    
        //处理结果
        Set suggestionWord = new HashSet<>();
        Suggest.Suggestion cnSuggestion = search.getSuggest().getSuggestion("suggest_title");
        generateSuggestion(cnSuggestion, suggestionWord);
    
        return getResult(suggestionWord);
    }
    
    private void generateSuggestion(Suggest.Suggestion suggestion, Set suggestionWord) {
        // suggestion.getEntries()方法得到的是对应的建议,因为在分词的情况下,可能有多个建议
        List entries = suggestion.getEntries();
    
        if(entries.size() > 0) {
            for(Object obj : entries) {
                // 其实查出来的 obj 的类型为 CompletionSuggestion.Entry
                if(obj instanceof CompletionSuggestion.Entry) {
                    CompletionSuggestion.Entry entry = (CompletionSuggestion.Entry)obj;
                    // 获取具体的 option
                    List optionsList = entry.getOptions();
                    if(optionsList.size() > 0) {
                        for(CompletionSuggestion.Entry.Option op : optionsList) {
                            Text text = op.getText();
                            suggestionWord.add(text.toString());
                        }
                    }
                }
            }
        }
    }
    
    private List> getResult(Set set) {
        List> resultList = new linkedList<>();
        set.forEach(suggestion -> {
            Map map = new HashMap<>();
            map.put("value", suggestion);
            resultList.add(map);
        });
        return resultList;
    }
    
4.后记

仅以此文记录在ElasticSearch中遇到的前缀提示问题。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5698521.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存