SpringBoot整合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的配置如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.5.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
<version>1.5.9.RELEASE</version>
</dependency>
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<User, Long>{
}
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<User> search(String searchContent) {
QueryStringQueryBuilder builder = new QueryStringQueryBuilder(searchContent);
System.out.println("查询的语句:"+builder);
Iterable<User> searchResult = userDao.search(builder);
Iterator<User> iterator = searchResult.iterator();
List<User> list=new ArrayList<User>();
while (iterator.hasNext()) {
list.add(iterator.next());
}
return list;
}
@Override
public List<User> 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<User> searchPageResults = userDao.search(searchQuery);
return searchPageResults.getContent();
}
@Override
public List<User> 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<User> searchResult = userDao.search(functionScoreQueryBuilder);
Iterator<User> iterator = searchResult.iterator();
List<User> list=new ArrayList<User>();
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"}]
进行分页查询
请求
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中添加如下依赖:
<dependency>
<groupId>io.searchbox</groupId>
<artifactId>jest</artifactId>
<version>5.3.3</version>
</dependency>
然后编写相关的测试代码。
代码中的注释应该很完整,所以这里就不再对代码过多的讲述了。
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;
public 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<Object> objs = new ArrayList<Object>();
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<Object> 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://www.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://www.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
------------- 分割线 2019-06-19---------------------
最新看到评论说SpringBoot官方整合的Elasticsearch版本太低了,查看官网发现,如果想在SpringBoot中想使用更高的Elasticsearch版本,可以使用 spring-data-elasticsearch 这个spring的架包,它们版本的对应关系如下:
| Spring Data Elasticsearch | Elasticsearch Version (z) |
|---|---|
| 3.2.x | 6.7.2 |
| 3.1.x | 6.2.2 |
| 3.0.x | 5.5.0 |
官网地址: https://github.com/spring-projects/spring-data-elasticsearch
对应的SpringBoot2.x整合ElasticSearch项目地址: https://github.com/xuwujing/springBoot-study/tree/master/springboot2-elasticsearch
如果觉得不错,希望顺便给个star。
到此,本文结束,谢谢阅读。
版权声明:
作者:虚无境
博客园出处:http://www.cnblogs.com/xuwujing
CSDN出处:http://blog.csdn.net/qazwsxpcm
个人博客出处:http://www.panchengming.com
原创不易,转载请标明出处,谢谢!
SpringBoot整合ElasticSearch实现多版本的兼容的更多相关文章
- ElasticSearch(2)---SpringBoot整合ElasticSearch
SpringBoot整合ElasticSearch 一.基于spring-boot-starter-data-elasticsearch整合 开发环境:springboot版本:2.0.1,elast ...
- Springboot整合elasticsearch以及接口开发
Springboot整合elasticsearch以及接口开发 搭建elasticsearch集群 搭建过程略(我这里用的是elasticsearch5.5.2版本) 写入测试数据 新建索引book( ...
- SpringBoot整合Elasticsearch详细步骤以及代码示例(附源码)
准备工作 环境准备 JAVA版本 java version "1.8.0_121" Java(TM) SE Runtime Environment (build 1.8.0_121 ...
- Springboot整合ElasticSearch进行简单的测试及用Kibana进行查看
一.前言 搜索引擎还是在电商项目.百度.还有技术博客中广泛应用,使用最多的还是ElasticSearch,Solr在大数据量下检索性能不如ElasticSearch.今天和大家一起搭建一下,小编是看完 ...
- 😊SpringBoot 整合 Elasticsearch (超详细).md
SpringBoot 整合 Elasticsearch (超详细) 注意: 1.环境搭建 安装es Elasticsearch 6.4.3 下载链接 为了方便,环境使用Windows 配置 解压后配置 ...
- springboot整合elasticsearch入门例子
springboot整合elasticsearch入门例子 https://blog.csdn.net/tianyaleixiaowu/article/details/72833940 Elastic ...
- Springboot整合Elasticsearch报错availableProcessors is already set to [4], rejecting [4]
Springboot整合Elasticsearch报错 今天使用SpringBoot整合Elasticsearch时候,相关的配置完成后,启动项目就报错了. nested exception is j ...
- springboot整合elasticsearch(基于es7.2和官方high level client)
前言 最近写的一个个人项目(传送门:全终端云书签)中需要用到全文检索功能,目前 mysql,es 都可以做全文检索,mysql 胜在配置方便很快就能搞定上线(参考这里),不考虑上手难度,es 在全文检 ...
- SpringBoot整合elasticsearch(三)
Docker安装elasticsearch 启动注意2点,1是内存,2是线程数(此处进行简单安装,后面会详细补充es文档) [root@topcheer ~]# docker images REPOS ...
随机推荐
- JAVA之旅(三十)——打印流PrintWriter,合并流,切割文件并且合并,对象的序列化Serializable,管道流,RandomAccessFile,IO其他类,字符编码
JAVA之旅(三十)--打印流PrintWriter,合并流,切割文件并且合并,对象的序列化Serializable,管道流,RandomAccessFile,IO其他类,字符编码 三十篇了,又是一个 ...
- Android进阶(二十八)上下文菜单ContextMenu使用案例
上下文菜单ContextMenu使用案例 前言 回顾之前的应用程序,发现之前创建的选项菜单无法显示了.按照正常逻辑来说,左图中在"商品信息"一栏中应该存在选项菜单,用户可进行分享等 ...
- synchronized和volatile比较
synchronized和volatile比较 volatile不需要加锁,比synchronized更轻量级,不会阻塞线程 从内存可见性角度讲,volatile读相当于加锁,volatile写相当于 ...
- DB Query Analyzer 6.04 is distributed, 78 articles concerned have been published
DB Query Analyzer 6.04 is distributed,78 articles concerned have been published DB Query Analyz ...
- 检查一个二叉树是否平衡的算法分析与C++实现
今天面试一个实习生,就想既然是未出校园,那就出一个比较基础的题吧,没想到答的并不如人意,对于树的操作完全不熟悉,因此此题算是未作答.原来我想看一下他分析问题的思路,优化代码的能力.接下来会把最近半年我 ...
- (NO.00002)iOS游戏精灵战争雏形(六)
接下来我们给MainScene场景再添加一个精灵,作为敌人. 双击SpriteBuilder中的MainScene.ccb,从控件库拖入一个CCSprite到CCPhysicsNode中,设置精灵帧为 ...
- 【一天一道LeetCode】#63. Unique Paths II
一天一道LeetCode (一)题目 Follow up for "Unique Paths": Now consider if some obstacles are added ...
- Coco2dx制作一个3D旋转的效果
建了工程之后修改HelloWorldScene.cpp文件,修改部分为 // on "init" you need to initialize your instance bool ...
- python的join(string)函数
join是字符串操作函数,操作的也是字符串. key="\t".join(('a','b','c')) result= key.split("\t") prin ...
- H5学习之旅-H5的样式(5)
样式的引入方式 外部样式表 link rel = "stylesheet" type = "text/css" href = "mystyle.css ...