Java利用ElasticSearch实现自动补全功能
作者:Awbeci
前言
最近在学习elasticsearch,想实现跟谷歌和百度类似的功能:下拉补全提示,如图所示:
准备
我使用的版本和依赖包,如下所示:
<properties> <java.version>17</java.version> <spring-boot.version>3.0.2</spring-boot.version> <spring-cloud.version>2022.0.0</spring-cloud.version> <spring-cloud-alibaba.version>2022.0.0.0-RC2</spring-cloud-alibaba.version> </properties> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency> <dependency> <groupId>co.elastic.clients</groupId> <artifactId>elasticsearch-java</artifactId> <version>8.7.1</version> </dependency>
devtools操作
首先,我们必须先创建index
并设置成想要设置成下拉补全提示的字段为completion
类型,如下所示:
PUT index_urls { "mappings": { "properties": { "suggest": { "type": "completion", "analyzer": "ik_smart" } } } }
这样我们创建了index_urls
,并且设置好了suggest
字段是completion
类型。
接着,我们往里面加点数据,并且使用completion suggester
进行查询,如下所示 :
// 自动补全查询 GET /index_urls/_search { "suggest": { "title_suggest": { "text": "n", // 关键字 "completion": { "field": "suggest", // 补全查询的字段 "skip_duplicates": true, // 跳过重复的 "size": 10 // 获取前10条结果 } } } }
查询结果如下所示:
{ "took": 1, "timed_out": false, "_shards": { "total": 1, "successful": 1, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 0, "relation": "eq" }, "max_score": null, "hits": [] }, "suggest": { "title_suggest": [ { "text": "n", "offset": 0, "length": 1, "options": [ { "text": "npm、yarn的安装和设置淘宝镜像源_yarn设置淘宝镜像_cn_ljr的博客-CSDN博客", "_index": "index_urls", "_id": "64d0548bd8239d21cb9fe4e5", "_score": 1, "_source": { "_class": "com.seaurl.searchservice.document.UrlIndex", "id": "64d0548bd8239d21cb9fe4e5", "uid": "6461e6ac25d966329b7d7642", "categoryId": "64b89acfaf50e77e53ef89ef", "categoryName": "户外运动", "categoryParentId": "", "categoryParentName": "", "title": "npm、yarn的安装和设置淘宝镜像源_yarn设置淘宝镜像_cn_ljr的博客-CSDN博客", "suggest": { "input": [ "npm、yarn的安装和设置淘宝镜像源_yarn设置淘宝镜像_cn_ljr的博客-CSDN博客" ] }, "url": "https://blog.csdn.net/cn_ljr/article/details/126319130", "domain": "blog.csdn.net", "description": "安装npm和yarn,配置npm和yarn的镜像源为淘宝镜像源_yarn设置淘宝镜像", "favicon": "https://cdn.seaurl.com/space/url/blog.csdn.net/favicon.ico", "env": "dev", "createdDt": 1691374731421, "updatedDt": 1691374731421 } } ] } ] } }
这样就完成了自动补全提示查询了,接下来我们使用java再实现一遍。
java操作
首先,我们创建实体类,并且定义completion
类型的字段,如下所示:
@Data public class UrlIndex { private String id; private String uid; // 分类id private String categoryId; private String categoryName; private String categoryParentId; private String categoryParentName; private String title; // 下拉提示建议使用的字段,注意:Completion类型字段,不能做筛选用 // @CompletionField(analyzer="ik_smart",searchAnalyzer="ik_smart", maxInputLength = 100) private Completion suggest; private String url; private String domain; private String description; //如果没获取前端显示domain首字母 private String favicon; private Date createdDt;//创建时间 private Date updatedDt;//更新时间 }
接着,我们往es中添加数据,并且把查询转化成java代码,如下所示:
private SearchResponse getEsSuggestionData(String filedName, String fieldValue) { try { String index = getEsIndexByEnv(); Suggester suggester = Suggester.of(s -> s .suggesters("title-suggest", FieldSuggester.of(fs -> fs .completion(cs -> cs.skipDuplicates(true) .size(10) .field(filedName) ) )) .text(fieldValue) ); return elasticsearchClient.search(s -> s.index(index) // 使用source可以返回固定字段 // .source() .suggest(suggester) , Map.class); } catch (Exception ex) { log.info("getEsSuggestionData error={}", ex.getMessage()); return null; } }
这样就完成了查询的功能,接下来,我们要把返回的数据拿出来,并转换之后返回给前端显示,如下所示:
@Override public List<UrlIndexDto> searchSuggestionDataBy(String fieldName, String fieldValue) { SearchResponse response = getEsSuggestionData(fieldName, fieldValue); if (response != null) { Map suggestMap = response.suggest(); if (suggestMap != null) { List<?> titleSuggestionList = (List<?>) suggestMap.get("title-suggest"); if (titleSuggestionList != null) { for (Object titleSuggestion : titleSuggestionList) { if (titleSuggestion instanceof Suggestion) { Suggestion suggestion = (Suggestion) titleSuggestion; if (suggestion.isCompletion()) { CompletionSuggest completionSuggest = suggestion.completion(); List<co.elastic.clients.elasticsearch.core.search.CompletionSuggestOption> options = completionSuggest.options(); List<UrlIndexDto> urlIndexDtos = new ArrayList<>(); for (co.elastic.clients.elasticsearch.core.search.CompletionSuggestOption option : options) { Object obj = option.source(); ObjectMapper objectMapper = new ObjectMapper(); UrlIndexDto urlIndexDto = objectMapper.convertValue(obj, UrlIndexDto.class); urlIndexDtos.add(urlIndexDto); } return urlIndexDtos; } } } } } } return null; }
这样就完成了查询并返回的操作,下面我们讲解下前端如何实现。
前端操作
我们使用react和antd来实现,先定义组件和相关方法,如下所示:
<AutoComplete allowClear className={styles.input} options={options} onSelect={onSelect} value={input} onChange={async (e) => { setInput(e) debouncedOnChange(e); }}> <Input placeholder={'请输入搜索内容'} prefix={<SearchOutlined/>} size={'large'} onPressEnter={onSearch} /> </AutoComplete>
相关方法:
const [input, setInput] = useState('') const [options, setOptions] = useState([]) // 在组件外部定义防抖函数 const debouncedOnChange = _.debounce(async (e) => { // 调用自动补全接口 const res = await dispatch(webSearchSuggestion({ q: e })) const resData = res.payload.data if (resData && resData.length>0){ let list = [] resData.forEach(item =>{ console.log('item resData=', item) list.push({ label: item.title, value: item.title, url: item.url }) }) // 将数据设置到options中 setOptions(list) }else{ setOptions([]) } }, 500); // 搜索框回车事件,调用查询接口(非自动补全接口) async function onSelect(value, option){ dispatch(webSearch({ q: option.value, pageNum: 1, pageSize: 20 })) }
我们使用了ladash的防抖函数来实现搜索。
完成了准备工作,我们来看看实现效果,如下所示:
注意
不是你在实体类中定义了Completion
类型的字段就ok了,要提前声明Completion
才管用,亲自踩过的坑!!!
总结
完成这些代码其实花费了一点时间的,主要是不知道如何把devtools的查询转换成java代码,并且我的版本是最新的,用的依赖包是上面提到的,所以写法上市面上没有找到过,完全靠自己搜索和chatgpt给的提示完成的。
到此这篇关于Java利用ElasticSearch实现自动补全功能的文章就介绍到这了,更多相关Java ElasticSearch内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
您可能感兴趣的文章:
- Java查询Elasticsearch数据根据指定id检索(in查询)、sql权限过滤、多字段匹配检索及数据排序
- Java使用ES Client 调用滚动查询及Elasticsearch滚动查询Scrolling机制
- Java Api实现Elasticsearch的滚动查询功能
- Java+ElasticSearch+Pytorch实现以图搜图功能
- Java中Elasticsearch 实现分页方式(三种方式)
- 关于Java中配置ElasticSearch集群环境账号密码的问题
- Java如何使用elasticsearch进行模糊查询
- JAVA使用ElasticSearch查询in和not in的实现方式
- JAVA操作elastic search的详细过程