springboot集成elasticsearch
添加pom
<!--elasticsearch--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency> <dependency> <groupId>io.searchbox</groupId> <artifactId>jest</artifactId> <version>5.3.3</version> </dependency> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>x-pack-transport</artifactId> <version>5.5.0</version> </dependency>
完整的pom
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>zhong</artifactId> <groupId>com.zh</groupId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>zhong-search</artifactId> <packaging>jar</packaging> <properties> <elasticsearch.version>5.6.10</elasticsearch.version> </properties> <dependencies> <!--公共模块--> <dependency> <groupId>com.zh</groupId> <artifactId>zhong-common-api</artifactId> <version>${zhong.version}</version> </dependency> <dependency> <groupId>com.zh</groupId> <artifactId>zhong-common-core</artifactId> <version>${zhong.version}</version> </dependency> <!--配置中心客户端--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <!--web 模块--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <!--排除tomcat依赖--> <exclusion> <artifactId>spring-boot-starter-tomcat</artifactId> <groupId>org.springframework.boot</groupId> </exclusion> </exclusions> </dependency> <!--undertow容器--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency> <!--elasticsearch--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency> <dependency> <groupId>io.searchbox</groupId> <artifactId>jest</artifactId> <version>5.3.3</version> </dependency> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>x-pack-transport</artifactId> <version>5.5.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
bootstrap.yml配置
spring:
data:
elasticsearch:
cluster‐name: my-application
cluster‐nodes: 192.168.1.107:9300
添加entity实体
package com.zh.search.entity; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.data.annotation.Id; import org.springframework.data.elasticsearch.annotations.*; import java.time.LocalDateTime; import java.util.Date; /** * @Auther: suruozhong * @Date: 2019/11/14 17:52 * @Description: */ @Data @AllArgsConstructor @NoArgsConstructor @Document(indexName = "search-entity",type = "search", shards = 1, replicas = 0) @Setting(settingPath = "/search-setting.json") //自动创建索引 @Mapping(mappingPath = "/search-mapping.json") public class Search { /** * @Id注解必须是springframework包下的 */ @Id private String id; //id是model的id拼接模块module private String title; //标题 private String content;// 内容 private String model; // 对象 private String module; // 模块 private String createTime; }
添加索引的设置,索引创建,分词,索引映射
在resource下添加两个文件
search-mapping.json
{ "search": { "_all": { "enabled": true }, "properties": { "id": { "type": "text" }, "title": { "type": "text", "analyzer": "ikSearchAnalyzer", "search_analyzer": "ikSearchAnalyzer", "fields": { "pinyin": { "type": "text", "analyzer": "pinyinSimpleIndexAnalyzer", "search_analyzer": "pinyinSimpleIndexAnalyzer" } } }, "content": { "type": "text", "analyzer": "ikSearchAnalyzer", "search_analyzer": "ikSearchAnalyzer", "fields": { "pinyin": { "type": "text", "analyzer": "pinyinSimpleIndexAnalyzer", "search_analyzer": "pinyinSimpleIndexAnalyzer" } } }, "model": { "type": "text" }, "module": { "type": "text" }, "createTime": { "type": "date", "format": "yyyy-MM-dd HH:mm:ss" } } } }
search-setting.json
{ "index": { "analysis": { "filter": { "edge_ngram_filter": { "type": "edge_ngram", "min_gram": 1, "max_gram": 50 }, "pinyin_simple_filter": { "type": "pinyin", "first_letter": "prefix", "padding_char": " ", "limit_first_letter_length": 50, "lowercase": true } }, "char_filter": { "tsconvert": { "type": "stconvert", "convert_type": "t2s" } }, "analyzer": { "ikSearchAnalyzer": { "type": "custom", "tokenizer": "ik_max_word", "char_filter": [ "tsconvert" ] }, "pinyinSimpleIndexAnalyzer": { "tokenizer": "keyword", "filter": [ "pinyin_simple_filter", "edge_ngram_filter", "lowercase" ] } } } } }
添加dao
package com.zh.search.dao; import com.zh.search.entity.Search; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; /** * @Auther: suruozhong * @Date: 2019/11/14 16:41 * @Description: */ public interface SearchRepository extends ElasticsearchRepository<Search,String> { }
代码实例
package com.zh.search.service.impl; import com.zh.common.core.kit.DateKit; import com.zh.common.core.kit.RandomKit; import com.zh.common.core.util.R; import com.zh.search.dao.SearchRepository; import com.zh.search.entity.Search; import com.zh.search.service.SearchService; import org.elasticsearch.action.admin.indices.analyze.AnalyzeAction; import org.elasticsearch.action.admin.indices.analyze.AnalyzeRequest; import org.elasticsearch.action.admin.indices.analyze.AnalyzeRequestBuilder; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder.Field; import org.elasticsearch.action.admin.indices.analyze.AnalyzeResponse; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.Client; import org.elasticsearch.common.text.Text; import org.elasticsearch.index.query.DisMaxQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHits; import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; import org.springframework.data.elasticsearch.core.SearchResultMapper; import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage; import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl; import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; import org.springframework.data.elasticsearch.core.query.SearchQuery; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import javax.annotation.Resource; import java.util.*; /** * @Auther: suruozhong * @Date: 2019/11/14 13:59 * @Description: */ @Service public class SearchServiceImpl implements SearchService { @Autowired private SearchRepository searchRepository; @Resource private ElasticsearchTemplate elasticsearchTemplate; /** * 添加数据 * @param title */ @Override public void saveM(String title){ Search model = new Search(); model.setId(RandomKit.getDateNumber()); model.setTitle(title); model.setContent(title); model.setCreateTime(DateKit.format(new Date(),"yyyy-MM-dd HH:mm:ss")); searchRepository.save(model); } /** * 获取所有数据 * @return */ @Override public R getAll(){ return new R(searchRepository.findAll()); } /** * 分页分词高亮关键词查询 * @param keyword * @param current * @param size * @return */ @Override public Page page(String keyword,Integer current,Integer size){ // 页码 Page<Search> page = null; try { // 构建查询 NativeSearchQueryBuilder searchQuery = new NativeSearchQueryBuilder(); // 索引查询 searchQuery.withIndices("search-entity"); BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); QueryBuilder queryBuilder = QueryBuilders.matchQuery("title", keyword).boost(2f); boolQueryBuilder.must(queryBuilder); //时间范围查询 // boolQueryBuilder.must(QueryBuilders.rangeQuery("createTime") // .from(DateKit.format(DateKit.getDayBegin(),"yyyy-MM-dd HH:mm:ss")) // .to(DateKit.format(DateKit.getDayBegin(),"yyyy-MM-dd HH:mm:ss"))); searchQuery.withQuery(boolQueryBuilder); // 高亮设置 List<String> highlightFields = new ArrayList<String>(); highlightFields.add("title"); Field[] fields = new Field[highlightFields.size()]; for (int x = 0; x < highlightFields.size(); x++) { fields[x] = new HighlightBuilder.Field(highlightFields.get(x)).preTags("<high>").postTags("</high>"); } searchQuery.withHighlightFields(fields); // 分页设置 searchQuery.withPageable(PageRequest.of(current-1, size)); page = elasticsearchTemplate.queryForPage(searchQuery.build(), Search.class, new SearchResultMapper() { @Override public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) { // 获取高亮搜索数据 List<Search> list = new ArrayList<Search>(); SearchHits hits = response.getHits(); for (SearchHit searchHit : hits) { if (hits.getHits().length <= 0) { return null; } Search model = new Search(); // 公共字段 model.setId(searchHit.getId()); model.setTitle(String.valueOf(searchHit.getSourceAsMap().get("title"))); model.setContent(String.valueOf(searchHit.getSourceAsMap().get("content"))); Object createTime = searchHit.getSourceAsMap().get("createTime"); //高亮字段 if (!StringUtils.isEmpty(searchHit.getHighlightFields().get("title"))) { Text[] text = searchHit.getHighlightFields().get("title").getFragments(); model.setTitle(text[0].toString()); } list.add(model); } if (list.size() > 0) { AggregatedPage<T> result = new AggregatedPageImpl<T>((List<T>) list, pageable, response.getHits().getTotalHits()); return result; } return null; } }); } catch (Exception e) { e.printStackTrace(); } return page; } /** * 分词查询 * @param title * @return */ @Override public List<Search> search(String title) { //使用中文拼音混合搜索,取分数最高的,具体评分规则可参照: // https://blog.csdn.net/paditang/article/details/79098830 SearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(structureQuery(title)) .build(); List<Search> list = searchRepository.search(searchQuery).getContent(); return list; } /** * 高亮关键词查询 * @param title * @return */ @Override public List<Search> searchHigh(String title) { Client client = elasticsearchTemplate.getClient(); QueryBuilder query = structureQuery(title); // 加入查询中 HighlightBuilder highlightBuilder = new HighlightBuilder(); highlightBuilder.preTags("<high>");//设置前缀 highlightBuilder.postTags("</high>");//设置后缀 highlightBuilder.field("title");//设置高亮字段 // highlightBuilder.field("title.pinyin");//设置拼音高亮字段 SearchResponse response = client.prepareSearch("search-entity") .setQuery(query).highlighter(highlightBuilder).execute().actionGet(); // 遍历结果, 获取高亮片段 SearchHits searchHits = response.getHits(); Search entity = null; List<Search> result = new ArrayList<>(); for (SearchHit hit : searchHits) { Map<String, Object> entityMap = hit.getSourceAsMap(); entity = new Search(); if (!StringUtils.isEmpty(hit.getHighlightFields().get("title"))) { Text[] text = hit.getHighlightFields().get("title").getFragments(); entity.setTitle(text[0].toString()); } // if (!StringUtils.isEmpty(hit.getHighlightFields().get("title.pinyin"))) { // Text[] text = hit.getHighlightFields().get("title.pinyin").getFragments(); // entity.setTitle(text[0].toString()); // } result.add(entity); } return result; } /** * 组成搜索条件,中文、拼音混合搜索 * * @param content the content * @return dis max query builder */ public DisMaxQueryBuilder structureQuery(String content) { //使用dis_max直接取多个query中,分数最高的那一个query的分数即可 DisMaxQueryBuilder disMaxQueryBuilder = QueryBuilders.disMaxQuery(); //boost 设置权重,只搜索匹配name QueryBuilder ikNameQuery = QueryBuilders.matchQuery("title", content).boost(2f); // QueryBuilder pinyinNameQuery = QueryBuilders.matchQuery("title.pinyin", content); disMaxQueryBuilder.add(ikNameQuery); // disMaxQueryBuilder.add(pinyinNameQuery); return disMaxQueryBuilder; } /** * 获取分词结果 * @param keyword * @param type type取值,拼音-pinyin,IK-ik_max_word,..... * @return */ @Override public List analyze(String keyword,String type){ AnalyzeRequest analyzeRequest = new AnalyzeRequest("search-entity") .text(keyword) .analyzer(Optional.ofNullable(type).orElse("ik_max_word")); List<AnalyzeResponse.AnalyzeToken> tokens = elasticsearchTemplate.getClient().admin().indices() .analyze(analyzeRequest) .actionGet() .getTokens(); return tokens; } }