Elasticsearch-06 Spring Boot 2.0.9整合ElasticSearch5.6.16

2021-08-17 16:43:27 浏览数 (1)

文章目录

  • 概述
  • 官方JAVA API文档
  • 工程
    • pom.xml
    • es配置文件
    • Es配置类
    • 控制层
      • 简单查询
      • 新增数据
      • 删除数据
      • 更新数据
      • 复合查询
  • 其他
    • 新建索引
    • 删除索引
    • 判断index中某个type是否存在
  • spring-data-elasticsearch 操作ES
  • 代码

概述

前面几篇,学习了ES的基本操作,那我们这里集成到代码中吧。 我们这里没有使用Spring 提供的 spring-boot-starter-data-elasticsearch,使用的是ES原生的API 。


官方JAVA API文档

当前7.0 https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/index.html

5.6

https://www.elastic.co/guide/en/elasticsearch/client/java-api/5.6/index.html

工程

ES服务端的版本 5.6.16 ,工程里es客户端的版本最好和服务端保持一致 。 正好Spring Boot 2.0.9RELEASE版本搭配transport里es是5.6.16版本。


pom.xml

代码语言:javascript复制
<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">
	<modelVersion>4.0.0modelVersion>
	<parent>
		<groupId>org.springframework.bootgroupId>
		<artifactId>spring-boot-starter-parentartifactId>
		<version>2.0.9.RELEASEversion>
		<relativePath /> 
	parent>
	<groupId>masterSpringMvcgroupId>
	<artifactId>springBootElasticSearchartifactId>
	<version>0.0.1-SNAPSHOTversion>
	<name>springBootElasticSearchname>
	<description>masterSpringMvc project for Spring Bootdescription>

	<properties>
		<java.version>1.8java.version>
	properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-webartifactId>
		dependency>

		<dependency>
			<groupId>org.elasticsearch.clientgroupId>
			<artifactId>transportartifactId>
		dependency>
	dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.bootgroupId>
				<artifactId>spring-boot-maven-pluginartifactId>
			plugin>
		plugins>
	build>

project>

es配置文件

代码语言:javascript复制
# Elasticsearch
elasticsearch.ip=127.0.0.1
#9200端口是RESTFul API来访问ElasticSearch的端口,9300端口是es节点通讯的默认端口,给java程序用的配置9300

elasticsearch.port=9300
elasticsearch.pool=5


# 集群的名字,和elasticsearch.yml中的cluster.name保持一致

elasticsearch.cluster.name=artisan

Es配置类

代码语言:javascript复制
package com.artisan.config;

import java.net.InetAddress;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class ESConfig {

	private static final Logger logger = LoggerFactory.getLogger(ESConfig.class);

	@Value("${elasticsearch.ip}")
	private String hostName;

	@Value("${elasticsearch.port}")
	private String port;

	@Value("${elasticsearch.cluster.name}")
	private String clusterName;

	@Value("${elasticsearch.pool}")
	private String poolSize;

	@Bean
	public TransportClient transportClient()  {

		logger.info("Elasticsearch begin to init ");
		TransportClient transportClient = null;
		try {
			// 地址信息
			InetSocketTransportAddress transportAddress = new InetSocketTransportAddress(
					InetAddress.getByName(hostName), Integer.valueOf(port));
			
			// 配置信息
			Settings esSetting = Settings.builder().put("cluster.name", clusterName) // 集群名字
					.put("client.transport.sniff", true)// 增加嗅探机制,找到ES集群
					.put("thread_pool.search.size", Integer.parseInt(poolSize))// 线程池个数
					.build();
			
			// 配置信息Settings自定义
			transportClient = new PreBuiltTransportClient(esSetting);
			transportClient.addTransportAddresses(transportAddress);
		} catch (Exception e) {
			logger.error("TransportClient create error", e);
		}
		return transportClient;
	}
}

控制层

简单起见,我们直接在Controller层操作ES吧,仅仅是为了测试下功能。

简单查询

先写个简单的根据id获取数据的方法来测试下班

代码语言:javascript复制
package com.artisan.controller;

import java.util.Map;

import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class ESController {
	
	@Autowired
	private TransportClient client;
	
	@GetMapping("/book/novel/{id}")
	public ResponseEntity<Map<String, Object>> getByIdFromES(@PathVariable String id){
		
		GetResponse response = this.client.prepareGet("book", "novel", id).get();
		if (!response.isExists()) {
			return new ResponseEntity<>(HttpStatus.NOT_FOUND);
		}
		return new ResponseEntity<Map<String, Object>>(response.getSource(),HttpStatus.OK);
	}

}

启动下服务,在浏览器或者postman中访问下吧

我们通过head插件来看下id=11的数据

OK,可以访问到正确的数据。


新增数据

ESController新增add方法

代码语言:javascript复制
@PostMapping("/book/novel/add")
	public ResponseEntity<String> add(NovelDTO novel) {
		try {
			XContentBuilder builder = XContentFactory.jsonBuilder()
					.startObject()
					.field("title", novel.getTitle())
					.field("author", novel.getAuthor())
					.field("word_count", novel.getWordCount())
					.field("public_date", novel.getPublishDate().getTime())
					.endObject();
			IndexResponse response = this.client.prepareIndex("book", "novel").setSource(builder).get();
			return new ResponseEntity<String>(response.getId(), HttpStatus.OK);
		} catch (IOException e) {
			e.printStackTrace();
			return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR);
		}
	}

我们把数据请求参数封装到了NovelDTO 中

代码语言:javascript复制
package com.artisan.dto;


import java.util.Date;

import org.springframework.format.annotation.DateTimeFormat;

public class NovelDTO {
	
	private String title;
	private String author;
	private String wordCount;
	@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
	private Date publishDate;
	
	// set/get方法 省略
}

启动服务,POST http://localhost:8080/book/novel/add

head插件中查看数据 访问 http://localhost:9100/

新增成功



如果要想用JSON传的话,需要在方法入参前加入 @RequestBody ,postman中更改如下

代码语言:javascript复制
public ResponseEntity<String> add(@RequestBody  NovelDTO novel) {

}

删除数据

代码语言:javascript复制
@DeleteMapping("/book/novel/del")
	public ResponseEntity<String> delete(String id ){
		DeleteResponse response = this.client.prepareDelete("book", "novel", id).get();
		return new ResponseEntity<String>(response.getResult().toString(),HttpStatus.OK);
	}

把刚才新增的数据删掉

通过head插件查看已经没有该记录了,删除OK


更新数据

举个例子,根据id更新title

代码语言:javascript复制
/**
	 * 根据id 修改title
	 * @param id
	 * @param title
	 * @return
	 */
	@PutMapping("/book/novel/update")
	public ResponseEntity<String> update(@RequestParam(name="id")String id ,
			@RequestParam(name="title")	String title){
		
		UpdateRequest updateRequest = new UpdateRequest("book", "novel", id);
		try {
			XContentBuilder builder = XContentFactory.jsonBuilder()
					.startObject()
					.field("title",title)
					.endObject();
			updateRequest.doc(builder);
			UpdateResponse response = this.client.update(updateRequest).get();
			return new ResponseEntity<String>(response.getResult().toString(), HttpStatus.OK);
		} catch (Exception e) {
			e.printStackTrace();
			return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR);
		} 
		
	}

head插件查看


复合查询

代码语言:javascript复制
	/**
	 * 综合查询
	 * @param title
	 * @param author
	 * @param gtWordCount
	 * @param ltWordCount
	 * @return
	 */
	@PostMapping("/book/novel/query")
	public ResponseEntity<List<Map<String, Object>>> query(
			@RequestParam(name="title",required=false)String title , 
			@RequestParam(name="author",required=false)String author ,
			@RequestParam(name="gtWordCount",defaultValue="0") Integer gtWordCount ,
			@RequestParam(name="ltWordCount",required=false) Integer ltWordCount  ) {
		
		BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
		
	
		if (title !=null) {
			boolQueryBuilder.must(QueryBuilders.matchQuery("title", title));
		}
		if (author !=null) {
			boolQueryBuilder.must(QueryBuilders.matchQuery("author", author));
		}
		
		RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("word_count")
				.from(gtWordCount);
		
		if (ltWordCount != null && ltWordCount > 0) {
			rangeQueryBuilder.to(ltWordCount);
		}
		
		// 关联
		boolQueryBuilder.filter(rangeQueryBuilder);
		
		
		SearchRequestBuilder builder = this.client.prepareSearch("book")
			.setTypes("novel")
			.setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
			.setQuery(boolQueryBuilder)
			.setFrom(0)
			.setSize(10);
		
		System.out.println("请求JSON数据:n"   builder);
		
		SearchResponse response = builder.get();
		
		List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
		
		for(SearchHit searchHit : response.getHits()) {
			list.add(searchHit.getSource());
		}
		return new ResponseEntity<List<Map<String, Object>>>(list, HttpStatus.OK);
	}

看下控制台输出的

请求JSON数据:

代码语言:javascript复制
{
  "from" : 0,
  "size" : 10,
  "query" : {
    "bool" : {
      "must" : [
        {
          "match" : {
            "title" : {
              "query" : "Elasticsearch",
              "operator" : "OR",
              "prefix_length" : 0,
              "max_expansions" : 50,
              "fuzzy_transpositions" : true,
              "lenient" : false,
              "zero_terms_query" : "NONE",
              "boost" : 1.0
            }
          }
        }
      ],
      "filter" : [
        {
          "range" : {
            "word_count" : {
              "from" : 500,
              "to" : null,
              "include_lower" : true,
              "include_upper" : true,
              "boost" : 1.0
            }
          }
        }
      ],
      "disable_coord" : false,
      "adjust_pure_negative" : true,
      "boost" : 1.0
    }
  }
}

使用postman测试下 ,返回了3条数据,符合预期。


其他

上面通过代码实现了数据的CRUD操作,那么我们把多索引和type的操作也尝试写下吧

工具类

代码语言:javascript复制
package com.artisan.utils;

import javax.annotation.PostConstruct;

import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequest;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class ESUtil {

	private static final Logger logger = LoggerFactory.getLogger(ESUtil.class);

	@Autowired
    private TransportClient transportClient;

    private static TransportClient client;

   
    @PostConstruct
    public void init() {
        client = this.transportClient;
    }

	/**
	 * 创建索引
	 *
	 * @param index
	 * @return
	 */

	public static boolean createIndex(String index) {
		if (!isIndexExist(index)) {
			logger.info("Index is not exits!");
			CreateIndexResponse indexresponse = client.admin().indices().prepareCreate(index).execute().actionGet();
			logger.info("执行建立成功?"   indexresponse.isAcknowledged());
			return indexresponse.isAcknowledged();
		}
		return false;
	}

	/**
	 * 删除索引
	 *
	 * @param index
	 * @return
	 */
	public static boolean deleteIndex(String index) {
		if (!isIndexExist(index)) {
			logger.info("Index is not exits!");
		}
		DeleteIndexResponse dResponse = client.admin().indices().prepareDelete(index).execute().actionGet();
		if (dResponse.isAcknowledged()) {
			logger.info("delete index "   index   "  successfully!");
		} else {
			logger.info("Fail to delete index "   index);
		}
		return dResponse.isAcknowledged();
	}

	/**
	 * 判断索引是否存在
	 *
	 * @param index
	 * @return
	 */
	public static boolean isIndexExist(String index) {
		IndicesExistsResponse inExistsResponse = client.admin().indices().exists(new IndicesExistsRequest(index))
				.actionGet();
		if (inExistsResponse.isExists()) {
			logger.info("Index ["   index   "] is exist!");
		} else {
			logger.info("Index ["   index   "] is not exist!");
		}
		return inExistsResponse.isExists();
	}

	/**
	 * 判断index下指定type是否存在
	 * 
	 * @param index
	 * @param type
	 * @return
	 */
	public static boolean isTypeExist(String index, String type) {
		return isIndexExist(index)
				? client.admin().indices().prepareTypesExists(index).setTypes(type).execute().actionGet().isExists()
				: false;
	}

}

新建索引

代码语言:javascript复制
@SuppressWarnings({ "rawtypes", "unchecked" })
	@PostMapping("/index/create")
	public ResponseEntity create(String index){
		
		if (ESUtil.createIndex(index)) {
			return new ResponseEntity(Boolean.TRUE,HttpStatus.OK);
		}else {
			return new ResponseEntity(HttpStatus.FOUND);
		}
		
	}

测试

head查看下


删除索引

代码语言:javascript复制
	@SuppressWarnings({ "rawtypes", "unchecked" })
	@PostMapping("/index/delete")
	public ResponseEntity deleteIndex(String index){
		
		if (ESUtil.deleteIndex(index)) {
			return new ResponseEntity(Boolean.TRUE,HttpStatus.OK);
		}else {
			return new ResponseEntity(HttpStatus.NOT_FOUND);
		}
		
	}

测试下

head查看,已经删除成功


判断index中某个type是否存在

代码语言:javascript复制
	@SuppressWarnings({ "rawtypes", "unchecked" })
	@PostMapping("/index/isTypeExist")
	public ResponseEntity isTypeExist(String index,String type){
		
		if (ESUtil.isTypeExist(index,type)) {
			return new ResponseEntity(Boolean.TRUE,HttpStatus.OK);
		}else {
			return new ResponseEntity(HttpStatus.NOT_FOUND);
		}
		
	}

测试一下


spring-data-elasticsearch 操作ES

https://docs.spring.io/spring-data/

我们也可以用Spring给我们提供的封装好的 ElasticsearchTemplate 更方便的操作ES,这里我就不演示了,和data jpa操作很像,比较简单。

https://spring.io/projects/spring-data-elasticsearch

https://github.com/spring-projects/spring-data-elasticsearch


如果也是使用spring boot集成的话,就用 spring-boot-starter-data-elasticsearch 这个maven的依赖,带有starter的这种。


代码

Github: https://github.com/yangshangwei/springBootElasticSearch

0 人点赞