SpringBoot集成ElasticSearch

1、使用方法

springboot里有两个比较好用的操作ES的工具,

一个是Jest(默认不生效),连接的是es的9200端口(Http Restful)。
需要导入jest的工具包,(io.searchbox.client.JestClient)

一个是spring data elasticsearch,前者连接的是es的9300端口(TCP连接,把自己伪装成ES的一个节点)。

  1. Client节点信息clusterNodes;clusterName
    2)ElasticSearchTemplate操作es
    3)编写一个ElasticSearchRepository的子接口来操作ES。

2、Jest使用方式

pom.xml
<!--SpringBoot默认使用SpringData ElasticSearch模块进行操作-->
		<!--<dependency>-->
			<!--<groupId>org.springframework.boot</groupId>-->
			<!--<artifactId>spring-boot-starter-data-elasticsearch</artifactId>-->
		<!--</dependency>-->

		<!-- https://mvnrepository.com/artifact/io.searchbox/jest -->
		<dependency>
			<groupId>io.searchbox</groupId>
			<artifactId>jest</artifactId>
			<version>6.3.1</version>
		</dependency>
application.properties
spring.elasticsearch.jest.uris=http://192.168.8.101:9200
Bean
public class Article {

    @JestId
    private Integer id;
    private String autor;
    private String title;
    private String content;

    public Article() {
    }

    public Article(Integer id, String autor, String title, String content) {
        this.id = id;
        this.autor = autor;
        this.title = title;
        this.content = content;
    }
/*
   @Getter
   @Setter
*/
@Test
@SpringBootTest
class ElaticsearchApplicationTests {

	@Autowired
	JestClient jestClient;

	@Test
	void contextLoads() {
	}


	@Test
	public void jest_insert() {
		// 给Es中索引(保存)一个文档
		Article article = new Article();
		article.setId(1);
		article.setTitle("Effect Java");
		article.setAutor("Joshua Bloch");
		article.setContent("Hello World");
		// 构建一个索引功能
		Index index = new Index.Builder(article).index("demo_index").type("article").build();

		try {
			//执行
			jestClient.execute(index);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
    //查询数据
    @Test
    public void jest_search(){
        // 查询表达式
        String json = "{\n" +
                "    \"query\" : {\n" +
                "        \"match\" : {\n" +
                "            \"content\" : \"Hello\"\n" +
                "        }\n" +
                "    }\n" +
                "}";
        // 构建搜索操作
        Search search = new Search.Builder(json).addIndex("demo_index").addType("article").build();

        // 执行
        try {
            SearchResult result = jestClient.execute(search);
            System.out.println(result.getJsonString());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    


}

3、SpringData ElasticSearch使用方式

Elasticsearch(ES)有两种连接方式:transport、rest。
通过监听9300端口tcp链接进行数据传输,他可以触摸到es的API和结构。
在Springboot集成ES的两种方式种,一般有spring-boot-starter-data-elasticsearch和Spring-data-elasticsearch。
其中spring-boot-starter-data-elasticsearch。第一个是Springboot官方的整合包,使用更方便。
但是更新缓慢,支持版本较低。而ES版本更新较快。版本不一致直接整合不上。
而Spring-data-elasticsearch对版本支持稍微好一点。版本对应关系。你会发现对新版本支持还是比较差的。
transport通过TCP方式访问ES(只支持java),rest方式通过http API 访问ES(没有语言限制)。
ES官方建议使用rest方式, transport 在7.0版本中不建议使用,在8.X的版本中废弃。

请注意,SpringBoot是2.2.0.RELEASE才兼容elasticsearch 7.x(http方式)

具体使用方法

1)集成ElasticsearchRepository接口

pom.xml
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.1.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
......
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

application.properties
spring.elasticsearch.rest.uris=http://192.168.8.101:9200
repository.BookRepository
public interface BookRepository extends ElasticsearchRepository<Book,Integer>{

}
ElasticsearchRepository
@NoRepositoryBean
public interface ElasticsearchRepository<T, ID> extends ElasticsearchCrudRepository<T, ID> {
    <S extends T> S index(S var1);

    <S extends T> S indexWithoutRefresh(S var1);

    Iterable<T> search(QueryBuilder var1);

    Page<T> search(QueryBuilder var1, Pageable var2);

    Page<T> search(SearchQuery var1);

    Page<T> searchSimilar(T var1, String[] var2, Pageable var3);

    void refresh();

    Class<T> getEntityClass();
}

注意ES7版本以后:
Specifying types in document index requests is deprecated, use the typeless endpoints instead (/{index}/_doc/{id}, /{index}/_doc, or /{index}/_create/{id})."]

[POST http://192.168.8.101:9200/springdataes_index/book?timeout=1m] returned 1 warnings: [299 Elasticsearch-7.4.1-fc0eeb6e2c25915d63d871d344e3d0b45ea0ea1e "[types removal] Specifying types in document index requests is deprecated, use the typeless endpoints instead (/{index}/_doc/{id}, /{index}/_doc, or /{index}/_create/{id})."]
@Autowired
BookRepository bookRepository;
......

@Test
public void springdataes_search(){
    Book book = new Book();
    book.setId(1);
    book.setBookName("BookName");
    book.setAuthor("Author");
    bookRepository.index(book);
}

BookRepository增加自定义方法

public interface BookRepository extends ElasticsearchRepository<Book,Integer>{
    /*
       除了继承,支持自定义方法
     */
    public List<Book> findByBookNameLike(String bookName);

}

执行下面的测试方法会报错,说明对7.4.1 高版本的ES支持还是不好。这里暂时不介绍更多。

List<Book> books= bookRepository.findByBookNameLike("BookName");
for (Book book:books) {
	System.out.println(book);
}

java.lang.NoSuchMethodError: org.elasticsearch.search.SearchHits.getTotalHits()J

	at org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate.doCount(ElasticsearchRestTemplate.java:604)
	at org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate.count(ElasticsearchRestTemplate.java:566)
	at org.springframework.data.elasticsearch.repository.query.ElasticsearchPartQuery.execute(ElasticsearchPartQuery.java:81)

......

4、elasticsearch-rest-high-level-client

pom.xml
<dependency>
      <groupId>org.elasticsearch</groupId>
      <artifactId>elasticsearch</artifactId>
      <version>${elasticsearch.version}</version>
</dependency>
<dependency>
      <groupId>org.elasticsearch.client</groupId>
      <artifactId>elasticsearch-rest-client</artifactId>
      <version>${elasticsearch.version}</version>
</dependency>
<dependency>
       <groupId>org.elasticsearch.client</groupId>
       <artifactId>elasticsearch-rest-high-level-client</artifactId>
       <version>${elasticsearch.version}</version>
</dependency>

rest,不难想到http,restclient就是采用http进行交互。
restclient相比transport最大的好处就是—对于版本兼容性较好。
然而,restclient也分为两种——high—level和low—level两种,两者原理基本一致,区别最大的就是封装性。
low—level各种操作都要你自己封装,并且java本身不支持json还需要引用第三方包。
而high—level是针对elasticsearch的api进行高级封装,和elasticsearch的版本关联大一些。
low—level就行原生爬虫,啥东西都要你自己写,而high—level就像是框架一般,各种方法帮你稍微封装好。使用起来较为方便。

官方明确说明transportclient在elasticsearch高版本会直接遗弃。只支持restclient。
为了顺应ES的潮流,还是要用restclient。并且transportclient在高并发会有性能问题。

ESConfig
@Configuration
public class ESConfig {
    private static String hosts = "192.168.8.101"; // 集群地址,多个用,隔开
    private static int port = 9200; // 使用的端口号
    private static String schema = "http"; // 使用的协议
    private static ArrayList hostList = null;

    private static int connectTimeOut = 1000; // 连接超时时间
    private static int socketTimeOut = 30000; // 连接超时时间
    private static int connectionRequestTimeOut = 500; // 获取连接的超时时间

    private static int maxConnectNum = 100; // 最大连接数
    private static int maxConnectPerRoute = 100; // 最大路由连接数

    static {
        hostList = new ArrayList<>();
        String[] hostStrs = hosts.split(",");
        for (String host : hostStrs) {
            hostList.add(new HttpHost(host, port, schema));
        }
    }

    @Bean
    public RestHighLevelClient client() {
        RestClientBuilder builder = RestClient.builder((HttpHost[]) hostList.toArray(new HttpHost[0]));
        // 异步httpclient连接延时配置
        builder.setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() {
            @Override
            public Builder customizeRequestConfig(Builder requestConfigBuilder) {
                requestConfigBuilder.setConnectTimeout(connectTimeOut);
                requestConfigBuilder.setSocketTimeout(socketTimeOut);
                requestConfigBuilder.setConnectionRequestTimeout(connectionRequestTimeOut);
                return requestConfigBuilder;
            }
        });
        // 异步httpclient连接数配置
        builder.setHttpClientConfigCallback(new HttpClientConfigCallback() {
            @Override
            public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
                httpClientBuilder.setMaxConnTotal(maxConnectNum);
                httpClientBuilder.setMaxConnPerRoute(maxConnectPerRoute);
                return httpClientBuilder;
            }
        });
        RestHighLevelClient client = new RestHighLevelClient(builder);
        return client;
    }

}

Bean

@Document(indexName = "springdataes_index"/*,type = "book"*/)
public class Book {
    private Integer id;
    private String bookName;
    private String author;

    public Book() {
    }

    public Book(Integer id, String bookName, String author) {
        this.id = id;
        this.bookName = bookName;
        this.author = author;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", bookName='" + bookName + '\'' +
                ", author='" + author + '\'' +
                '}';
    }
}

@Test
  /*
    第三种elastic elasticsearch-rest-high-level-client
    */

        @Autowired
	private RestHighLevelClient client;
        ......

	@Test
	public void highLevelClient(){
		
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//        sourceBuilder.from(0);
//        sourceBuilder.size(10);
        sourceBuilder.fetchSource(new String[]{"id","author","bookName"}, new String[]{});
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("author", "Author");
//        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("tag", "体育");
//        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("publishTime");
//        rangeQueryBuilder.gte("2018-01-26T08:00:00Z");
//        rangeQueryBuilder.lte("2018-01-26T20:00:00Z");
        BoolQueryBuilder boolBuilder = QueryBuilders.boolQuery();
        boolBuilder.must(matchQueryBuilder);
//        boolBuilder.must(termQueryBuilder);
//        boolBuilder.must(rangeQueryBuilder);
        sourceBuilder.query(boolBuilder);
        SearchRequest searchRequest = new SearchRequest("springdataes_index");
//        searchRequest.types(type);
        searchRequest.source(sourceBuilder);
        try {
            SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);

			SearchHits hits = response.getHits();
			SearchHit[] searchHits = hits.getHits();
			for (SearchHit hit : searchHits) {
				System.out.println("search -> " + hit.getSourceAsString());
				/*
				search -> {"author":"Author","id":2,"bookName":"BookName"}
				search -> {"author":"Author","id":1,"bookName":"BookName"}
				 */
				JSONObject bookJson = JSONObject.parseObject(hit.getSourceAsString());
				Book book = JSON.toJavaObject(bookJson,Book.class);
				System.out.println(book);
			}

//            System.out.println(JSON.toJSONString(response));
            /*
            {
                "took": 106,
                "timed_out": false,
                "_shards": {
                    "total": 5,
                    "successful": 5,
                    "skipped": 0,
                    "failed": 0
                },
                "hits": {
                    "total": {
                        "value": 2,
                        "relation": "eq"
                    },
                    "max_score": 0.2876821,
                    "hits": [{
                        "_index": "springdataes_index",
                        "_type": "book",
                        "_id": "2",
                        "_score": 0.2876821,
                        "_source": {
                            "author": "Author"
                        }
                    }, {
                        "_index": "springdataes_index",
                        "_type": "book",
                        "_id": "1",
                        "_score": 0.2876821,
                        "_source": {
                            "author": "Author"
                        }
                    }]
                }
            }
             */
        } catch (IOException e) {
            e.printStackTrace();
        }
	}
多条件查询
@Test
	public void highLevelClientDemo(){
		SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
		sourceBuilder.fetchSource(new String[]{"id","author","bookName"}, new String[]{});
		MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("author", "Author");
		MatchQueryBuilder matchQueryBuilder1 = QueryBuilders.matchQuery("id", "2");
		BoolQueryBuilder boolBuilder = QueryBuilders.boolQuery();
/******************************************************************************************/
		boolBuilder.must(matchQueryBuilder).must(matchQueryBuilder1);
/******************************************************************************************/
		sourceBuilder.query(boolBuilder);
		SearchRequest searchRequest = new SearchRequest("springdataes_index");
		searchRequest.source(sourceBuilder);
		try {
			SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
			SearchHits hits = response.getHits();
			SearchHit[] searchHits = hits.getHits();
			for (SearchHit hit : searchHits) {
				System.out.println("search -> " + hit.getSourceAsString());
				/*
				search -> {"author":"Author","id":2,"bookName":"BookName"}
				 */
				JSONObject bookJson = JSONObject.parseObject(hit.getSourceAsString());
				Book book = JSON.toJavaObject(bookJson,Book.class);
				System.out.println(book);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
ELK日志搜索的具体例子
@Test
	public void highLevelClientDemo1(){

		SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
		sourceBuilder.fetchSource(new String[]{"log_level","log_thread","log_date","log_class","log_content"}, new String[]{});
		MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("log_content", "xx1");
		BoolQueryBuilder boolBuilder = QueryBuilders.boolQuery();
		boolBuilder.must(matchQueryBuilder);
		sourceBuilder.query(boolBuilder);
		SearchRequest searchRequest = new SearchRequest("log-2019.11.24");
		searchRequest.source(sourceBuilder);
		try {
			SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);

			SearchHits hits = response.getHits();
			SearchHit[] searchHits = hits.getHits();
			for (SearchHit hit : searchHits) {
				System.out.println("search -> " + hit.getSourceAsString());
				/*上节基于ELK的日志搜索
				search -> {"log_thread":"restartedMain","log_date":"2019-03-21 09:20:01,104","log_level":"INFO","log_class":"com.xxx.xxx.XXXApplication","log_content":"xx1"}
				 */

			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
更新id为1的 Book对象的author字段"Author"-》UpdatedAuthor
	/*
	   更新id为1的 Book对象的author字段"Author"-》UpdatedAuthor
	 */
	@Test
	public void update() throws IOException {
		String index = "springdataes_index";
		String type = "";
		Book update_book = new Book();
		update_book.setId(1);
		update_book.setAuthor("UpdatedAuthor");
		UpdateRequest request = new UpdateRequest(index, update_book.getId().toString());
		request.doc(JSON.toJSONString(update_book), XContentType.JSON);
		UpdateResponse updateResponse = client.update(request, RequestOptions.DEFAULT);
		System.out.println("update: " + JSON.toJSONString(updateResponse));
	}
posted @ 2019-11-25 18:18  xidianzxm  阅读(3394)  评论(0编辑  收藏  举报