前言: 实战操作SpringBoot集成ElasticSearch,
安装环境
本文环境:
ES版本为: 6.8.4
SpringBoot版本为: 2.1.3.RELEASE
Spring-boot-starter-data-elasticsearch本为:2.1.3.RELEASE
环境安装请参考上一篇文章:《ElasticSearch深度剖析》想轻松学会ES这篇文章必看
搭建SpringBoot工程
SpringBoot项目搭建请自行解决,本文不予累述,如有必要后续会单开篇文章介绍!
添加依赖
org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASEorg.springframework.boot spring-boot-starter-data-elasticsearch org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-webflux com.alibaba fastjson 1.2.62org.springframework.boot spring-boot-starter-test testio.projectreactor reactor-test test
编写yml文件
spring: data: elasticsearch: cluster-name: elasticsearch cluster-nodes: 127.0.0.1:9300 repositories: enabled: true
编写代码
Document编写
@Document(indexName = "orders", type = "product")public class ProductDocument implements Serializable { @Id private String id; private String productName; private String productDesc; private Date createTime; private Date updateTime; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getProductName() { return productName; } public void setProductName(String productName) { this.productName = productName; } public String getProductDesc() { return productDesc; } public void setProductDesc(String productDesc) { this.productDesc = productDesc; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public Date getUpdateTime() { return updateTime; } public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; }}
创建者模式(使用loombok更简洁,本文只是顺便介绍一种实现方式)
public class ProductDocumentBuilder { private static ProductDocument productDocument; // create start public static ProductDocumentBuilder create() { productDocument = new ProductDocument(); return new ProductDocumentBuilder(); } public ProductDocumentBuilder addId(String id) { productDocument.setId(id); return this; } public ProductDocumentBuilder addProductName(String productName) { productDocument.setProductName(productName); return this; } public ProductDocumentBuilder addProductDesc(String productDesc) { productDocument.setProductDesc(productDesc); return this; } public ProductDocumentBuilder addCreateTime(Date createTime) { productDocument.setCreateTime(createTime); return this; } public ProductDocumentBuilder addUpdateTime(Date updateTime) { productDocument.setUpdateTime(updateTime); return this; } public ProductDocument builder() { return productDocument; }}
DAO层编写
@Componentpublic interface ProductDocumentRepository extends ElasticsearchRepository {}
Service面向接口编程
public interface EsSearchService extends BaseSearchService { /** * 保存 * * @date: 2019/12/13 16:02 */ void save(ProductDocument... productDocuments); /** * 删除 * * @param id */ void delete(String id); /** * 清空索引 */ void deleteAll(); /** * 根据ID查询 * * @param id * @return */ ProductDocument getById(String id); /** * 查询全部 * * @return */ List getAll();}
Service实现类编写
@Servicepublic class EsSearchServiceImpl extends BaseSearchServiceImpl implements EsSearchService { private Logger log = LoggerFactory.getLogger(getClass()); @Resource private ElasticsearchTemplate elasticsearchTemplate; @Resource private ProductDocumentRepository productDocumentRepository; @Override public void save(ProductDocument... productDocuments) { elasticsearchTemplate.putMapping(ProductDocument.class); if (productDocuments.length > 0) { /*Arrays.asList(productDocuments).parallelStream() .map(productDocumentRepository::save) .forEach(productDocument -> log.info("【保存数据】:{}", JSON.toJSONString(productDocument)));*/ log.info("【保存索引】:{}", JSON.toJSONString(productDocumentRepository.saveAll(Arrays.asList(productDocuments)))); } } @Override public void delete(String id) { productDocumentRepository.deleteById(id); } @Override public void deleteAll() { productDocumentRepository.deleteAll(); } @Override public ProductDocument getById(String id) { return productDocumentRepository.findById(id).get(); } @Override public List getAll() { List list = new ArrayList<>(); productDocumentRepository.findAll().forEach(list::add); return list; }}
Service基础类编写
@Servicepublic class BaseSearchServiceImpl implements BaseSearchService { private Logger log = LoggerFactory.getLogger(getClass()); @Resource private ElasticsearchTemplate elasticsearchTemplate; @Override public List query(String keyword, Class clazz) { SearchQuery searchQuery = new NativeSearchQueryBuilder() .withQuery(new QueryStringQueryBuilder(keyword)) .withSort(SortBuilders.scoreSort().order(SortOrder.DESC)) // .withSort(new FieldSortBuilder("createTime").order(SortOrder.DESC)) .build(); return elasticsearchTemplate.queryForList(searchQuery,clazz); } /** * 高亮显示 * @date: 2019/12/13 21:22 */ @Override public List> queryHit(String keyword,String indexName,String ... fieldNames) { // 构造查询条件,使用标准分词器. QueryBuilder matchQuery = createQueryBuilder(keyword,fieldNames); // 设置高亮,使用默认的highlighter高亮器 HighlightBuilder highlightBuilder = createHighlightBuilder(fieldNames); // 设置查询字段 SearchResponse response = elasticsearchTemplate.getClient().prepareSearch(indexName) .setQuery(matchQuery) .highlighter(highlightBuilder) .setSize(10000) // 设置一次返回的文档数量,最大值:10000 .get(); // 返回搜索结果 SearchHits hits = response.getHits(); return getHitList(hits); } /** * 高亮显示,返回分页 * @date: 2019/12/18 10:29 */ @Override public Page> queryHitByPage(int pageNo,int pageSize, String keyword, String indexName, String... fieldNames) { // 构造查询条件,使用标准分词器. QueryBuilder matchQuery = createQueryBuilder(keyword,fieldNames); // 设置高亮,使用默认的highlighter高亮器 HighlightBuilder highlightBuilder = createHighlightBuilder(fieldNames); // 设置查询字段 SearchResponse response = elasticsearchTemplate.getClient().prepareSearch(indexName) .setQuery(matchQuery) .highlighter(highlightBuilder) .setFrom((pageNo-1) * pageSize) .setSize(pageNo * pageSize) // 设置一次返回的文档数量,最大值:10000 .get(); // 返回搜索结果 SearchHits hits = response.getHits(); Long totalCount = hits.getTotalHits(); Page> page = new Page<>(pageNo,pageSize,totalCount.intValue()); page.setList(getHitList(hits)); return page; } /** * 构造查询条件 * @date: 2019/12/18 10:42 */ private QueryBuilder createQueryBuilder(String keyword, String... fieldNames){ // 构造查询条件,使用标准分词器. return QueryBuilders.multiMatchQuery(keyword,fieldNames) // matchQuery(),单字段搜索 .analyzer("ik_max_word") .operator(Operator.OR); } /** * 构造高亮器 * @date: 2019/12/18 10:44 */ private HighlightBuilder createHighlightBuilder(String... fieldNames){ // 设置高亮,使用默认的highlighter高亮器 HighlightBuilder highlightBuilder = new HighlightBuilder() // .field("productName") .preTags("") .postTags(""); // 设置高亮字段 for (String fieldName: fieldNames) highlightBuilder.field(fieldName); return highlightBuilder; } /** * 处理高亮结果 * @date: 2019/12/18 10:48 */ private List> getHitList(SearchHits hits){ List> list = new ArrayList<>(); Map map; for(SearchHit searchHit : hits){ map = new HashMap<>(); // 处理源数据 map.put("source",searchHit.getSourceAsMap()); // 处理高亮数据 Map hitMap = new HashMap<>(); searchHit.getHighlightFields().forEach((k,v) -> { String hight = ""; for(Text text : v.getFragments()) hight += text.string(); hitMap.put(v.getName(),hight); }); map.put("highlight",hitMap); list.add(map); } return list; } @Override public void deleteIndex(String indexName) { elasticsearchTemplate.deleteIndex(indexName); }}
Controller编写
@RestControllerpublic class EsSearchController { private Logger log = LoggerFactory.getLogger(getClass()); @Resource private EsSearchService esSearchService; /** * 新增 / 修改索引 * @return */ @RequestMapping("save") public String add(@RequestBody ProductDocument productDocument) { esSearchService.save(productDocument); return "success"; } /** * 删除索引 * @return */ @RequestMapping("delete/{id}") public String delete(@PathVariable String id) { esSearchService.delete(id); return "success"; } /** * 清空索引 * @return */ @RequestMapping("delete_all") public String deleteAll(@PathVariable String id) { esSearchService.deleteAll(); return "success"; } /** * 根据ID获取 * @return */ @RequestMapping("get/{id}") public ProductDocument getById(@PathVariable String id){ return esSearchService.getById(id); } /** * 根据获取全部 * @return */ @RequestMapping("get_all") public List getAll(){ return esSearchService.getAll(); } /** * 搜索 * @param keyword * @return */ @RequestMapping("query/{keyword}") public List query(@PathVariable String keyword){ return esSearchService.query(keyword,ProductDocument.class); } /** * 搜索,命中关键字高亮 * http://localhost:8080/query_hit?keyword=无印良品荣耀&indexName=orders&fields=productName,productDesc * @param keyword 关键字 * @param indexName 索引库名称 * @param fields 搜索字段名称,多个以“,”分割 * @return */ @RequestMapping("query_hit") public List> queryHit(@RequestParam String keyword, @RequestParam String indexName, @RequestParam String fields){ String[] fieldNames = {}; if(fields.contains(",")) fieldNames = fields.split(","); else fieldNames[0] = fields; return esSearchService.queryHit(keyword,indexName,fieldNames); } /** * 搜索,命中关键字高亮 * http://localhost:8080/query_hit_page?keyword=无印良品荣耀&indexName=orders&fields=productName,productDesc&pageNo=1&pageSize=1 * @param pageNo 当前页 * @param pageSize 每页显示的数据条数 * @param keyword 关键字 * @param indexName 索引库名称 * @param fields 搜索字段名称,多个以“,”分割 * @return */ @RequestMapping("query_hit_page") public Page> queryHitByPage(@RequestParam int pageNo,@RequestParam int pageSize ,@RequestParam String keyword, @RequestParam String indexName, @RequestParam String fields){ String[] fieldNames = {}; if(fields.contains(",")) fieldNames = fields.split(","); else fieldNames[0] = fields; return esSearchService.queryHitByPage(pageNo,pageSize,keyword,indexName,fieldNames); } /** * 删除索引库 * @param indexName * @return */ @RequestMapping("delete_index/{indexName}") public String deleteIndex(@PathVariable String indexName){ esSearchService.deleteIndex(indexName); return "success"; }}
测试
http://localhost:8080/get_all