前言

上一篇学习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的时候,它需要在实体类中设置indexNametype ,如果和传统型数据库比较的话,就相当于。需要注意的是indexNametype都必须是小写!!!

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实现多版本的兼容的更多相关文章

  1. ElasticSearch(2)---SpringBoot整合ElasticSearch

    SpringBoot整合ElasticSearch 一.基于spring-boot-starter-data-elasticsearch整合 开发环境:springboot版本:2.0.1,elast ...

  2. Springboot整合elasticsearch以及接口开发

    Springboot整合elasticsearch以及接口开发 搭建elasticsearch集群 搭建过程略(我这里用的是elasticsearch5.5.2版本) 写入测试数据 新建索引book( ...

  3. SpringBoot整合Elasticsearch详细步骤以及代码示例(附源码)

    准备工作 环境准备 JAVA版本 java version "1.8.0_121" Java(TM) SE Runtime Environment (build 1.8.0_121 ...

  4. Springboot整合ElasticSearch进行简单的测试及用Kibana进行查看

    一.前言 搜索引擎还是在电商项目.百度.还有技术博客中广泛应用,使用最多的还是ElasticSearch,Solr在大数据量下检索性能不如ElasticSearch.今天和大家一起搭建一下,小编是看完 ...

  5. 😊SpringBoot 整合 Elasticsearch (超详细).md

    SpringBoot 整合 Elasticsearch (超详细) 注意: 1.环境搭建 安装es Elasticsearch 6.4.3 下载链接 为了方便,环境使用Windows 配置 解压后配置 ...

  6. springboot整合elasticsearch入门例子

    springboot整合elasticsearch入门例子 https://blog.csdn.net/tianyaleixiaowu/article/details/72833940 Elastic ...

  7. Springboot整合Elasticsearch报错availableProcessors is already set to [4], rejecting [4]

    Springboot整合Elasticsearch报错 今天使用SpringBoot整合Elasticsearch时候,相关的配置完成后,启动项目就报错了. nested exception is j ...

  8. springboot整合elasticsearch(基于es7.2和官方high level client)

    前言 最近写的一个个人项目(传送门:全终端云书签)中需要用到全文检索功能,目前 mysql,es 都可以做全文检索,mysql 胜在配置方便很快就能搞定上线(参考这里),不考虑上手难度,es 在全文检 ...

  9. SpringBoot整合elasticsearch(三)

    Docker安装elasticsearch 启动注意2点,1是内存,2是线程数(此处进行简单安装,后面会详细补充es文档) [root@topcheer ~]# docker images REPOS ...

随机推荐

  1. JAVA之旅(三十)——打印流PrintWriter,合并流,切割文件并且合并,对象的序列化Serializable,管道流,RandomAccessFile,IO其他类,字符编码

    JAVA之旅(三十)--打印流PrintWriter,合并流,切割文件并且合并,对象的序列化Serializable,管道流,RandomAccessFile,IO其他类,字符编码 三十篇了,又是一个 ...

  2. Android进阶(二十八)上下文菜单ContextMenu使用案例

    上下文菜单ContextMenu使用案例 前言 回顾之前的应用程序,发现之前创建的选项菜单无法显示了.按照正常逻辑来说,左图中在"商品信息"一栏中应该存在选项菜单,用户可进行分享等 ...

  3. synchronized和volatile比较

    synchronized和volatile比较 volatile不需要加锁,比synchronized更轻量级,不会阻塞线程 从内存可见性角度讲,volatile读相当于加锁,volatile写相当于 ...

  4. 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 ...

  5. 检查一个二叉树是否平衡的算法分析与C++实现

    今天面试一个实习生,就想既然是未出校园,那就出一个比较基础的题吧,没想到答的并不如人意,对于树的操作完全不熟悉,因此此题算是未作答.原来我想看一下他分析问题的思路,优化代码的能力.接下来会把最近半年我 ...

  6. (NO.00002)iOS游戏精灵战争雏形(六)

    接下来我们给MainScene场景再添加一个精灵,作为敌人. 双击SpriteBuilder中的MainScene.ccb,从控件库拖入一个CCSprite到CCPhysicsNode中,设置精灵帧为 ...

  7. 【一天一道LeetCode】#63. Unique Paths II

    一天一道LeetCode (一)题目 Follow up for "Unique Paths": Now consider if some obstacles are added ...

  8. Coco2dx制作一个3D旋转的效果

    建了工程之后修改HelloWorldScene.cpp文件,修改部分为 // on "init" you need to initialize your instance bool ...

  9. python的join(string)函数

    join是字符串操作函数,操作的也是字符串. key="\t".join(('a','b','c')) result= key.split("\t") prin ...

  10. H5学习之旅-H5的样式(5)

    样式的引入方式 外部样式表 link rel = "stylesheet" type = "text/css" href = "mystyle.css ...