当前位置: 移动技术网 > IT编程>开发语言>Java > Spring Boot整合ElasticSearch实现多版本兼容的方法详解

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

2019年07月19日  | 移动技术网IT编程  | 我要评论

前言

上一篇学习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://www.lhsxpumps.com/_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,文件准备

下载地址:

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

2,启动elasticsearch

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

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

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


3,安装es插件

web管理界面head 安装

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

输入:plugin install mobz/elasticsearch-head

进行下载

成功下载之后,在浏览器输入:http://www.lhsxpumps.com/_localhost:9200/_plugin/head/

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


4,注册服务

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

依次输入:

service.bat install
service.bat start

成功之后,再输入

services.msc

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

其它

elasticsearch官网api地址:

jestclientgithub地址:

https://github.com/searchbox-io/jest ()

项目我放到github上面去了。

https://github.com/xuwujing/springboot ()

总结

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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网