时间:2021-05-02
前言
在上一篇学习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的配置如下:
? 1 2 3 4 5 6 7 8 9 <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的配置
? 1 2 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。)
代码编写
实体类
? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @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层
? 1 2 public interface userdao extends elasticsearchrepository<user, long>{ }dao层这里就比较简单了,只需继承elasticsearchrepository该类就行了。其中主要的方法就是 save、delete和search。其中save方法相当如insert和update,没有就新增,有就覆盖。delete方法主要就是删除数据以及索引库。至于search就是查询了,包括一些常用的查询,如分页、权重之类的。
service层
? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 @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即可。
代码测试
调用接口进行添加数据
新增数据:
? 1 2 3 4 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"}进行全文查询
请求
? 1 http://localhost:8086/api/user?searchcontent=工程师返回
? 1 2 3 [{"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"}]进行分页查询
请求
? 1 http://localhost:8086/api/user?pagenumber=0&pagesize=2&searchcontent=工程师返回
? 1 [{"id":2,"name":"李四","age":14,"description":"李四是个测试工程师"},{"id":1,"name":"张三","age":20,"description":"张三是个java开发工程师"}]进行权重查询
请求
? 1 http://localhost:8086/api/user2?searchcontent=李四返回
? 1 [{"id":2,"name":"李四","age":24,"description":"李四是个测试工程师","createtm":"1980-2-15 19:01:32"}]权重查询打印的语句:
? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 查询的语句:{{ "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即可。
示例:
? 1 2 @autowired elasticsearchtemplate elasticsearchtemplate;但是,上述方法中都有其局限性,也就是随着elasticsearch的版本变更,相关的java api也在做不断的调整,就是elasticsearch服务端版本进行更改之后,客户端的代码可能需要重新编写。
因此介绍一个相当好用的第三方工具jestclient,它对elasticsearch进行封装,填补了 elasticsearch httprest接口 客户端的空白,它适用于elasticsearch2.x以上的版本,无需因为elasticsearch服务端版本更改而对代码进行更改!
jestclient
首先在maven中添加如下依赖:
? 1 2 3 4 5 <dependency> <groupid>io.searchbox</groupid> <artifactid>jest</artifactid> <version>5.3.3</version> </dependency>然后编写相关的测试代码。
代码中的注释应该很完整,所以这里就不再对代码过多的讲述了。
? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 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。
测试结果
全文搜索
? 1 2 3 4 5 6 7 8 9 10 11 全文搜索查询语句:{ "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"}匹配搜索
? 1 2 3 4 5 6 7 8 9 精确搜索查询语句:{ "query" : { "term" : { "age" : 24 } } } 精确搜索返回结果:{"id":2,"name":"李四","age":24,"description":"李四是个测试工程师","createtm":"1980-2-15 19:01:32"}时间区间搜索
? 1 2 3 4 5 6 7 8 9 10 11 12 13 区间搜索语句:{ "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界面
依次输入:
? 1 2 service.bat install service.bat start成功之后,再输入
? 1 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
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对服务器之家的支持。
原文链接:http://www.cnblogs.com/xuwujing/p/8998168.html
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
Springboot2.1.X整合Elasticsearch最新版的一处问题新版本的Springboot2的spring-boot-starter-data-e
0.版本选择我这里选择了5.6.x,记得如果spring-boot-starter-parent是1.x可以选择2.x版本的elasticsearch,版本要对
前期工作1.导入mybatis整合依赖org.mybatis.spring.bootmybatis-spring-boot-starter2.1.42.连接数据
本文介绍了springboot的maven配置依赖详解,分享给大家,具体如下:我们通过引用spring-boot-starter-parent,添加spring
sprig-boot是一个微服务架构,加快了spring工程快速开发,以及简便了配置。接下来开始spring-boot与mybatis的整合。1、创建一个mav