Spring Boot整合ElasticSearch实现多版本兼容的方法详解

网友投稿 497 2023-02-05


Spring Boot整合ElasticSearch实现多版本兼容的方法详解

前言

在上一篇学习SpringBoot中,整合了Mybatis、Druid和PageHelper并实现了多数据源的操作。本篇主要是介绍和使用目前最火的搜索引擎ElastiSearch,并和SpringBoot进行结合使用。

ElasticSearch介绍

ElasticSearch是一个基于Lucene的搜索服务器,其实就是对Lucene进行封装,提供了 REST API 的操作接口 ElasticSearch作为一个高度可拓展的开源全文搜索和分析引擎,可用于快速地对大数据进行存储,搜索和分析。

ElasticSearch主要特点:分布式、高可用、异步写入、多API、面向文档 。

ElasticSearch核心概念:近实时,集群,节点(保存数据),索引,分片(将索引分片),副本(分片可设置多个副本) 。它可以快速地储存、搜索和分析海量数据。

ElasticSearch使用案例:维基百科、Stack Overflow、github 等等。

SpringBoot整合Elasticsearch

在使用SpringBoot整合Elasticsearch 之前,我们应该了解下它们之间对应版本的关系。

Spring Boot Version (x)

Spring Data Elasticsearch Version (y)

Elasticsearch Version (z)

x <= 1.3.5

y <= 1.3.4

z <= 1.7.2*

x >= 1.4.x

2.0.0 <=y < 5.0.0**

2.0.0 <= z < 5.0.0**

这里我们使用的SpringBoot的版本是1.5.9,Elasticsearch的版本是2.3.5。

使用SpringBoot整合Elasticsearch,一般都是使用 SpringData 进行封装的,然后再dao层接口继承ElasticsearchRepository 类,该类实现了很多的方法,比如常用的CRUD方法。

SpringData的使用

首先,在使用之前,先做好相关的准备。

Maven的配置如下:

org.springframework.boot

spring-boot-starter-web

1.5.9.RELEASE

org.springframework.boot

spring-boot-starter-data-elasticsearch

1.5.9.RELEASE

application.properties的配置

spring.data.elasticsearch.repositories.enabled = true

spring.data.elasticsearch.cluster-nodes =127.0.0.1\:9300

注: 9300 是 java 客户端的端口。9200 是支持 Restful HTTP 的接口。

更多的配置:

spring.data.elasticsearch.cluster-name Elasticsearch 集群名。(默认值: elasticsearch)

spring.data.elasticsearch.cluster-nodes 集群节点地址列表,用逗号分隔。如果没有指定,就启动一个客户端节点。

spring.data.elasticsearch.propertie 用来配置客户端的额外属性。

spring.data.elasticsearch.repositories.enabled 开启 Elasticsearch 仓库。(默认值:true。)

代码编写

实体类

@Document(indexName = "userindex", type = "user")

public class User implements Serializable{

/**

*

*/

private static final long serialVersionUID = 1L;

/** 编号 */

private Long id;

/** 姓名 */

private String name;

/** 年龄 */

private Integer age;

/** 描述 */

private String description;

/** 创建时间 */

private String createtm;

// getter和setter 略

使用SpringData的时候,它需要在实体类中设置indexName 和type ,如果和传统型数据库比较的话,就相当于库和表。

需要注意的是indexName和type都必须是小写!!!

dao层

public interface UserDao extends ElasticsearchRepository{

}

dao层这里就比较简单了,只需继承ElasticsearchRepository该类就行了。其中主要的方法就是 save、delete和search。其中save方法相当如insert和update,没有就新增,有就覆盖。delete方法主要就是删除数据以及索引库。至于search就是查询了,包括一些常用的查询,如分页、权重之类的。

Service层

@Service

public class UserServiceImpl implements UserService {

@Autowired

private UserDao userDao;

@Override

public boolean insert(User user) {

boolean falg=false;

try{

userDao.save(user);

falg=true;

}catch(Exception e){

e.printStackTrace();

}

return falg;

}

@Override

public List search(String searchContent) {

QueryStringQueryBuilder builder = new QueryStringQueryBuilder(searchContent);

System.out.println("查询的语句:"+builder);

Iterable searchResult = userDao.search(builder);

Iterator iterator = searchResult.iterator();

List list=new ArrayList();

while (iterator.hasNext()) {

list.add(iterator.next());

}

return list;

}

@Override

public List searchUser(Integer pageNumber, Integer pageSize,String searchContent) {

// 分页参数

Pageable pageable = new PageRequest(pageNumber, pageSize);

QueryStringQueryBuilder builder = new QueryStringQueryBuilder(searchContent);

SearchQuery searchQuery = new NativeSearchQueryBuilder().withPageable(pageable).withQuery(builder).build();

System.out.println("查询的语句:" + searchQuery.getQuery().toString());

Page searchPageResults = userDao.search(searchQuery);

return searchPageResults.getContent();

}

@Override

public List searchUserByWeight(String searchContent) {

// 根据权重进行查询

FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery()

.add(QueryBuilders.boolQuery().should(QueryBuilders.matchQuery("name", searchContent)),

ScoreFunctionBuilders.weightFactorFunction(10))

.add(QueryBuilders.boolQuery().should(QueryBuilders.matchQuery("description", searchContent)),

ScoreFunctionBuilders.weightFactorFunction(100)).setMinScore(2);

System.out.println("查询的语句:" + functionScoreQueryBuilder.toString());

Iterable searchResult = userDao.search(functionScoreQueryBuilder);

Iterator iterator = searchResult.iterator();

List list=new ArrayList();

while (iterator.hasNext()) {

list.add(iterator.next());

}

return list;

}

}

这里我就简单的写了几个方法,其中主要的方法是查询。查询包括全文搜索,分页查询和权重查询。其中需要说明的是权重查询这块,权重的分值越高,查询的结果也越靠前,如果没有对其它的数据设置分值,它们默认的分值就是1,如果不想查询这些语句,只需使用setMinScore将其设为大于1即可。

代码测试

调用接口进行添加数据

新增数据:

POST http://localhost:8086/api/user

{"id":1,"name":"张三","age":20,"description":"张三是个Java开发工程师","createtm":"2018-4-25 11:07:42"}

{"id":2,"name":"李四","age":24,"description":"李四是个测试工程师","createtm":"1980-2-15 19:01:32"}

{"id":3,"name":"王五","age":25,"description":"王五是个运维工程师","createtm":"2016-8-21 06:11:32"}

进行全文查询

请求

http://localhost:8086/api/user?searchContent=工程师

返回

[{"id":2,"name":"李四","age":14,"description":"李四是个测试工程师","createtm": "1980-2-15 19:01:32"},

{"id":1,"name":"张三","age":20,"description":"张三是个Java开发工程师", "createtm": "2018-4-25 11:07:42"},

{"id":3,"name":"王五","age":25,"description":"王五是个运维工程师","createtm": "2016-8-21 06:11:32"}]

进行分页查询

ylQwxlwk

请求

http://localhost:8086/api/user?pageNumber=0&pageSize=2&searchContent=工程师

返回

[{"id":2,"name":"李四","age":14,"description":"李四是个测试工程师"},{"id":1,"name":"张三","age":20,"description":"张三是个Java开发工程师"}]

进行权重查询

请求

http://localhost:8086/api/user2?searchContent=李四

返回

[{"id":2,"name":"李四","age":24,"description":"李四是个测试工程师","createtm":"1980-2-15 19:01:32"}]

权重查询打印的语句:

查询的语句:{{

"function_score" : {

"functions" : [ {

"filter" : {

"bool" : {

"should" : {

"match" : {

"name" : {

"query" : "李四",

"type" : "boolean"

}

}

}

}

},

"weight" : 10.0

}, {

"filter" : {

"bool" : {

"should" : {

"match" : {

"description" : {

"query" : "李四",

"type" : "boolean"

}

}

}

}

},

"weight" : 100.0

} ],

"min_score" : 2.0

}

}

注:测试中,因为设置了setMinScore最小权重分为2的,所以无关的数据是不会显示出来的。如果想显示的话,在代码中去掉即可。

新增完数据之后,可以在浏览器输入:http://localhost:9200/_plugin/head/

然后点击基本查询,便可以查看添加的数据。如果想用语句查询,可以将程序中控制台打印的查询语句粘贴到查询界面上进行查询!

注:这里的ElasticSearch是我在windows上安装的,并安装了ES插件head,具体安装步骤在文章末尾。

除了SpringData之外,其实还有其它的方法操作ElasticSearch的。

比如使用原生ElasticSearch的Api,使用TransportClient类实现。

或者使用由Spring封装,只需在Service层,进行注入Bean即可。

示例:

@Autowired

ElasticsearchTemplate elasticsearchTemplate;

但是,上述方法中都有其局限性,也就是随着ElasticSearch的版本变更,相关的Java API也在做不断的调整,就是ElasticSearch服务端版本进行更改之后,客户端的代码可能需要重新编写。

因此介绍一个相当好用的第三方工具JestClient,它对ElasticSearch进行封装,填补了 ElasticSearch HttpRest接口 客户端的空白,它适用于ElasticSearch2.x以上的版本,无需因为ElasticSearch服务端版本更改而对代码进行更改!

JestClient

首先在Maven中添加如下依赖:

io.searchbox

jest

5.3.3

然后编写相关的测试代码。

代码中的注释应该很完整,所以这里就不再对代码过多的讲述了。

import java.util.ArrayList;

import java.util.List;

import org.elasticsearch.index.query.QueryBuilders;

import org.elasticsearch.search.builder.SearchSourceBuilder;

import com.pancm.pojo.User;

import io.searchbox.client.JestClient;

import io.searchbox.client.JestClientFactory;

import io.searchbox.client.JestResult;

import io.searchbox.client.config.HttpClientConfig;

import io.searchbox.core.Bulk;

import io.searchbox.core.BulkResult;

import io.searchbox.core.Delete;

import io.searchbox.core.DocumentResult;

import io.searchbox.core.Index;

import io.searchbox.core.Search;

import io.searchbox.indices.CreateIndex;

import io.searchbox.indices.DeleteIndex;

import io.searchbox.indices.mapping.GetMapping;

import io.searchbox.indices.mapping.PutMapping;

pubylQwxlwklic class JestTest {

private static JestClient jestClient;

private static String indexName = "userindex";

// private static String indexName = "userindex2";

private static String typeName = "user";

private static String elasticIps="http://192.169.2.98:9200";

// private static String elasticIps="http://127.0.0.1:9200";

public static void main(String[] args) throws Exception {

jestClient = getJestClient();

insertBatch();

serach1();

serach2();

serach3();

jestClient.close();

}

private static JestClient getJestClient() {

JestClientFactory factory = new JestClientFactory();

factory.setHttpClientConfig(new HttpClientConfig.Builder(elasticIps).connTimeout(60000).readTimeout(60000).multiThreaded(true).build());

return factory.getObject();

}

public static void insertBatch() {

List objs = new ArrayList();

objs.add(new User(1L, "张三", 20, "张三是个Java开发工程师","2018-4-25 11:07:42"));

objs.add(new User(2L, "李四", 24, "李四是个测试工程师","1980-2-15 19:01:32"));

objs.add(new User(3L, "王五", 25, "王五是个运维工程师","2016-8-21 06:11:32"));

boolean result = false;

try {

result = insertBatch(jestClient,indexName, typeName,objs);

} catch (Exception e) {

e.printStackTrace();

}

System.out.println("批量新增:"+result);

}

/**

* 全文搜索

*/

public static void serach1() {

String query ="工程师";

try {

SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

searchSourceBuilder.query(QueryBuilders.queryStringQuery(query));

//分页设置

searchSourceBuilder.from(0).size(2);

System.out.println("全文搜索查询语句:"+searchSourceBuilder.toString());

System.out.println("全文搜索返回结果:"+search(jestClient,indexName, typeName, searchSourceBuilder.toString()));

} catch (Exception e) {

e.printStackTrace();

}

}

/**

* 精确搜索

*/

public static void serach2() {

try {

SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

searchSourceBuilder.query(QueryBuilders.termQuery("age", 24));

System.out.println("精确搜索查询语句:"+searchSourceBuilder.toString());

System.out.println("精确搜索返回结果:"+search(jestClient,indexName, typeName, searchSourceBuilder.toString()));

} catch (Exception e) {

e.printStackTrace();

}

}

/**

* 区间搜索

*/

public static void serach3() {

String createtm="createtm";

String from="2016-8-21 06:11:32";

String to="2018-8-21 06:11:32";

try {

SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

searchSourceBuilder.query(QueryBuilders.rangeQuery(createtm).gte(from).lte(to));

System.out.println("区间搜索语句:"+searchSourceBuilder.toString());

System.out.println("区间搜索返回结果:"+search(jestClient,indexName, typeName, searchSourceBuilder.toString()));

} catch (Exception e) {

e.printStackTrace();

}

}

/**

* 创建索引

* @param indexName

* @return

* @throws Exception

*/

public boolean createIndex(JestClient jestClient,String indexName) throws Exception {

JestResult jr = jestClient.execute(new CreateIndex.Builder(indexName).build());

return jr.isSucceeded();

}

/**

* 新增数据

* @param indexName

* @param typeName

* @param source

* @return

* @throws Exception

*/

public boolean insert(JestClient jestClient,String indexName, String typeName, String source) throws Exception {

PutMapping putMapping = new PutMapping.Builder(indexName, typeName, source).build();

JestResult jr = jestClient.execute(putMapping);

return jr.isSucceeded();

}

/**

* 查询数据

* @param indexName

* @param typeName

* @return

* @throws Exception

*/

public static String getIndexMapping(JestClient jestClient,String indexName, String typeName) throws Exception {

GetMapping getMapping = new GetMapping.Builder().addIndex(indexName).addType(typeName).build();

JestResult jr =jestClient.execute(getMapping);

return jr.getJsonString();

}

/**

* 批量新增数据

* @param indexName

* @param typeName

* @param objs

* @return

* @throws Exception

*/

public static boolean insertBatch(JestClient jestClient,String indexName, String typeName, List objs) throws Exception {

Bulk.Builder bulk = new Bulk.Builder().defaultIndex(indexName).defaultType(typeName);

for (Object obj : objs) {

Index index = new Index.Builder(obj).build();

bulk.addAction(index);

}

BulkResult br = jestClient.execute(bulk.build());

return br.isSucceeded();

}

/**

* 全文搜索

* @param indexName

* @param typeName

* @param query

* @return

* @throws Exception

*/

public static String search(JestClient jestClient,String indexName, String typeName, String query) throws Exception {

Search search = new Search.Builder(query)

.addIndex(indexName)

.addType(typeName)

.build();

JestResult jr = jestClient.execute(search);

// System.out.println("--"+jr.getJsonString());

// System.out.println("--"+jr.getSourceAsObject(User.class));

return jr.getSourceAsString();

}

/**

* 删除索引

* @param indexName

* @return

* @throws Exception

*/

public boolean delete(JestClient jestClient,String indexName) throws Exception {

JestResult jr = jestClient.execute(new DeleteIndex.Builder(indexName).build());

return jr.isSucceeded();

}

/**

* 删除数据

* @param indexName

* @param typeName

* @param id

* @return

* @throws Exception

*/

public boolean delete(JestClient jestClient,String indexName, String typeName, String id) throws Exception {

DocumentResult dr = jestClient.execute(new Delete.Builder(id).index(indexName).type(typeName).build());

return dr.isSucceeded();

}

注:测试之前先说明下,本地windows系统安装的是ElasticSearch版本是2.3.5,linux服务器上安装的ElasticSearch版本是6.2。

测试结果

全文搜索

全文搜索查询语句:{

"from" : 0,

"size" : 2,

"query" : {

"query_string" : {

"query" : "工程师"

}

}

}

全文搜索返回结果:{"id":1,"name":"张三","age":20,"description":"张三是个Java开发工程师","createtm":"2018-4-25 11:07:42"},{"id":2,"name":"李四","age":24,"description":"李四是个测试工程师","createtm":"1980-2-15 19:01:32"}

匹配搜索

精确搜索查询语句:{

"query" : {

"term" : {

"age" : 24

}

}

}

精确搜索返回结果:{"id":2,"name":"李四","age":24,"description":"李四是个测试工程师","createtm":"1980-2-15 19:01:32"}

时间区间搜索

区间搜索语句:{

"query" : {

"range" : {

"createtm" : {

"from" : "2016-8-21 06:11:32",

"to" : "2018-8-21 06:11:32",

"include_lower" : true,

"include_upper" : true

}

}

}

}

区间搜索返回结果:{"id":1,"name":"张三","age":20,"description":"张三是个Java开发工程师","createtm":"2018-4-25 11:07:42"}

新增完数据之后,我们可以上linux的 Kibana中进行相关的查询,查询结果如下:

注:Kibana 是属于ELK中一个开源软件。Kibana可以为 Logstash 和 ElasticSearch 提供的日志分析友好的 Web 界面,可以帮助汇总、分析和搜索重要数据日志。

上述代码中测试返回的结果符合我们的预期。其中关于JestClient只是用到了很少的一部分,更多的使用可以查看JestClient的官方文档。

Windows安装ElasticSearch

1,文件准备

下载地址:https://elastic.co/downloads

选择ElasticSearch相关版本, 然后选择后缀名为ZIP文件进行下载,下载之后进行解压。

2,启动Elasticsearch

进入bin目录下,运行 elasticsearch.bat

然后在浏览上输入: localhost:9200

成功显示一下界面表示成功!

3,安装ES插件

web管理界面head 安装

进入bin目录下,打开cmd,进入dos界面

输入:plugin install mobz/elasticsearch-head

进行下载

成功下载之后,在浏览器输入:http://localhost:9200/_plugin/head/

若显示一下界面,则安装成功!

4,注册服务

进入bin目录下,打开cmd,进入dos界面

依次输入:

service.bat install

service.bat start

成功之后,再输入

services.msc

跳转到Service服务界面,可以直接查看es的运行状态!

其它

ElasticSearch官网API地址:

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

JestClientGithub地址:

https://github.com/searchbox-io/Jest (本地下载)

项目我放到github上面去了。

https://github.com/xuwujing/springBoot (本地下载)

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:webstorm添加*.vue文件支持
下一篇:Angular 数据请求的实现方法
相关文章

 发表评论

暂时没有评论,来抢沙发吧~