摘要: 原创出处 www.bysocket.com 「泥瓦匠bysocket 」欢迎转载,保留摘要,谢谢!
『 预见未来最好的方式就是亲手创造未来 – 《史蒂夫·乔布斯传》 』
运行环境:jdk 7 或 8,maven 3.0+
技术栈:springboot 1.5+,elasticsearch 2.3.2
本文提纲
一、es 的使用场景
二、运行 springboot-elasticsearch 工程
三、springboot-elasticsearch 工程代码详解
es 的使用场景大致分为两块
1. 全文检索。加上分词(ik 是其中一个)、拼音插件等可以成为强大的全文搜索引擎。
2. 日志统计分析。可以实时动态分析海量日志数据。
1
2
3
4
<code>spring boot version (x) spring data elasticsearch version (y) elasticsearch version (z)</code>
<code>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**</code>
<code>* - 只需要你修改下对应的 pom 文件版本号</code>
<code>** - 下一个 es 的版本会有重大的更新</code>
git clone 下载工程 springboot-elasticsearch ,项目地址见 github – https://github.com/jeffli1993/springboot-learning-example。
1. 后台起守护线程启动 elasticsearch
<code>cd</code> <code>elasticsearch-2.3.2/</code>
<code>.</code><code>/bin/elasticsearch</code> <code>-d</code>
下面开始运行工程步骤(quick start):
2. 项目结构介绍
5
6
<code>org.spring.springboot.controller - controller 层</code>
<code>org.spring.springboot.repository - es 数据操作层</code>
<code>org.spring.springboot.domain - 实体类</code>
<code>org.spring.springboot.service - es 业务逻辑层</code>
<code>application - 应用启动类</code>
<code>application.properties - 应用配置文件,应用启动会自动读取配置</code>
本地启动的 es ,就不需要改配置文件了。如果连测试 es 服务地址,需要修改相应配置
3.编译工程
在项目根目录 springboot-elasticsearch,运行 maven 指令:
<code>mvn clean</code><code>install</code>
4.运行工程
右键运行 application 应用启动类(位置:/springboot-learning-example/springboot-elasticsearch/src/main/java/org/spring/springboot/application.java)的 main 函数,这样就成功启动了 springboot-elasticsearch 案例。
用 postman 工具新增两个城市
新增城市信息
7
8
9
10
11
12
13
14
15
<code>post http:</code><code>//127</code><code>.0.0.1:8080</code><code>/api/city</code>
<code>{</code>
<code>"id"</code><code>:</code><code>"1"</code><code>,</code>
<code>"provinceid"</code><code>:</code><code>"1"</code><code>,</code>
<code>"cityname"</code><code>:</code><code>"温岭"</code><code>,</code>
<code>"description"</code><code>:</code><code>"温岭是个好城市"</code>
<code>}</code>
<code>"id"</code><code>:</code><code>"2"</code><code>,</code>
<code>"provinceid"</code><code>:</code><code>"2"</code><code>,</code>
<code>"cityname"</code><code>:</code><code>"温州"</code><code>,</code>
<code>"description"</code><code>:</code><code>"温州是个热城市"</code>
可以打开 es 可视化工具 head 插件:http://localhost:9200/_plugin/head/:
在「数据浏览」tab,可以查阅到 es 中数据是否被插入,插入后的数据格式如下:
<code>"_index"</code><code>:</code><code>"cityindex"</code><code>,</code>
<code>"_type"</code><code>:</code><code>"city"</code><code>,</code>
<code>"_id"</code><code>:</code><code>"1"</code><code>,</code>
<code>"_version"</code><code>: 1,</code>
<code>"_score"</code><code>: 1,</code>
<code>"_source"</code><code>: {</code>
<code>"id"</code><code>: 1,</code>
<code>"provinceid"</code><code>: 1,</code>
下面验证下权重分查询搜索接口的实现:
get http://localhost:8080/api/city/search?pagenumber=0&pagesize=10&searchcontent=温岭
数据是会出现
<code>[</code>
<code>},</code>
<code>"id"</code><code>: 2,</code>
<code>"provinceid"</code><code>: 2,</code>
<code>]</code>
从启动后台 console 可以看出,打印出来对应的 dsl 语句:
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<code>"function_score"</code> <code>: {</code>
<code>"functions"</code> <code>: [ {</code>
<code>"filter"</code> <code>: {</code>
<code>"bool"</code> <code>: {</code>
<code>"should"</code> <code>: {</code>
<code>"match"</code> <code>: {</code>
<code>"cityname"</code> <code>: {</code>
<code>"query"</code> <code>:</code><code>"温岭"</code><code>,</code>
<code>"type"</code> <code>:</code><code>"boolean"</code>
<code>"weight"</code> <code>: 1000.0</code>
<code>}, {</code>
<code>"description"</code> <code>: {</code>
<code>"weight"</code> <code>: 100.0</code>
<code>} ]</code>
为什么会出现 温州 城市呢?因为 function score query 权重分查询,无相关的数据默认分值为 1。如果想除去,设置一个 setminscore 分值即可。
1.pom.xml 依赖
34
35
36
37
38
39
<code><?</code><code>xml</code> <code>version</code><code>=</code><code>"1.0"</code> <code>encoding</code><code>=</code><code>"utf-8"</code><code>?></code>
<code><</code><code>project</code> <code>xmlns</code><code>=</code><code>"http://maven.apache.org/pom/4.0.0"</code> <code>xmlns:xsi</code><code>=</code><code>"http://www.w3.org/2001/xmlschema-instance"</code>
<code> </code><code>xsi:schemalocation</code><code>=</code><code>"http://maven.apache.org/pom/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</code><code>></code>
<code> </code><code><</code><code>modelversion</code><code>>4.0.0</</code><code>modelversion</code><code>></code>
<code> </code><code><</code><code>groupid</code><code>>springboot</</code><code>groupid</code><code>></code>
<code> </code><code><</code><code>artifactid</code><code>>springboot-elasticsearch</</code><code>artifactid</code><code>></code>
<code> </code><code><</code><code>version</code><code>>0.0.1-snapshot</</code><code>version</code><code>></code>
<code> </code><code><</code><code>name</code><code>>springboot-elasticsearch :: 整合 elasticsearch </</code><code>name</code><code>></code>
<code> </code><code><!-- spring boot 启动父依赖 --></code>
<code> </code><code><</code><code>parent</code><code>></code>
<code> </code><code><</code><code>groupid</code><code>>org.springframework.boot</</code><code>groupid</code><code>></code>
<code> </code><code><</code><code>artifactid</code><code>>spring-boot-starter-parent</</code><code>artifactid</code><code>></code>
<code> </code><code><</code><code>version</code><code>>1.5.1.release</</code><code>version</code><code>></code>
<code> </code><code></</code><code>parent</code><code>></code>
<code> </code><code><</code><code>dependencies</code><code>></code>
<code> </code><code><!-- spring boot elasticsearch 依赖 --></code>
<code> </code><code><</code><code>dependency</code><code>></code>
<code> </code><code><</code><code>groupid</code><code>>org.springframework.boot</</code><code>groupid</code><code>></code>
<code> </code><code><</code><code>artifactid</code><code>>spring-boot-starter-data-elasticsearch</</code><code>artifactid</code><code>></code>
<code> </code><code></</code><code>dependency</code><code>></code>
<code> </code><code><!-- spring boot web 依赖 --></code>
<code> </code><code><</code><code>artifactid</code><code>>spring-boot-starter-web</</code><code>artifactid</code><code>></code>
<code> </code><code><!-- junit --></code>
<code> </code><code><</code><code>groupid</code><code>>junit</</code><code>groupid</code><code>></code>
<code> </code><code><</code><code>artifactid</code><code>>junit</</code><code>artifactid</code><code>></code>
<code> </code><code><</code><code>version</code><code>>4.12</</code><code>version</code><code>></code>
<code> </code><code></</code><code>dependencies</code><code>></code>
<code></</code><code>project</code><code>></code>
这里依赖的 spring-boot-starter-data-elasticsearch 版本是 1.5.1.release,对应的 spring-data-elasticsearch 版本是 2.1.0.release。后面数据操作层都是通过该 spring-data-elasticsearch 提供的接口实现。
2. application.properties 配置 es 地址
<code># es</code>
<code>spring.data.elasticsearch.repositories.enabled =</code><code>true</code>
<code>spring.data.elasticsearch.cluster-nodes = 127.0.0.1:9300</code>
默认 9300 是 java 客户端的端口。9200 是支持 restful http 的接口。
更多配置:
<code>spring.data.elasticsearch.cluster-name elasticsearch 集群名。(默认值: elasticsearch)</code>
<code>spring.data.elasticsearch.cluster-nodes 集群节点地址列表,用逗号分隔。如果没有指定,就启动一个客户端节点。</code>
<code>spring.data.elasticsearch.propertie 用来配置客户端的额外属性。</code>
<code>spring.data.elasticsearch.repositories.enabled 开启 elasticsearch 仓库。(默认值:</code><code>true</code><code>。)</code>
3. es 数据操作层
<code>@repository</code>
<code>public</code> <code>interface</code> <code>cityrepository</code><code>extends</code> <code>elasticsearchrepository<city,long> {</code>
接口只要继承 elasticsearchrepository 类即可。默认会提供很多实现,比如 crud 和搜索相关的实现。
4. 实体类
<code>@document(indexname =</code><code>"cityindex"</code><code>,</code><code>type</code> <code>=</code><code>"city"</code><code>)</code>
<code>public class city implements serializable{</code>
<code> </code><code>private static final long serialversionuid = -1l;</code>
<code> </code><code>/**</code>
<code> </code><code>* 城市编号</code>
<code> </code><code>*/</code>
<code> </code><code>private long</code><code>id</code><code>;</code>
<code> </code><code>* 省份编号</code>
<code> </code><code>private long provinceid;</code>
<code> </code><code>* 城市名称</code>
<code> </code><code>private string cityname;</code>
<code> </code><code>* 描述</code>
<code> </code><code>private string description;</code>
注意
index 配置必须是全部小写,不然会暴异常。
org.elasticsearch.indices.invalidindexnameexception: invalid index name [cityindex], must be lowercase
5. es 业务逻辑层
40
41
42
43
44
45
46
<code>/**</code>
<code> </code><code>* 城市 es 业务逻辑实现类</code>
<code> </code><code>*</code>
<code> </code><code>* created by bysocket on 07</code><code>/02/2017</code><code>.</code>
<code> </code><code>*/</code>
<code>@service</code>
<code>public class cityesserviceimpl implements cityservice {</code>
<code> </code><code>private static final logger logger = loggerfactory.getlogger(cityesserviceimpl.class);</code>
<code> </code><code>@autowired</code>
<code> </code><code>cityrepository cityrepository;</code>
<code> </code><code>@override</code>
<code> </code><code>public long savecity(city city) {</code>
<code> </code><code>city cityresult = cityrepository.save(city);</code>
<code> </code><code>return</code> <code>cityresult.getid();</code>
<code> </code><code>}</code>
<code> </code><code>public list<city> searchcity(integer pagenumber,</code>
<code> </code><code>integer pagesize,</code>
<code> </code><code>string searchcontent) {</code>
<code> </code><code>//</code> <code>分页参数</code>
<code> </code><code>pageable pageable = new pagerequest(pagenumber, pagesize);</code>
<code> </code><code>//</code> <code>function score query</code>
<code> </code><code>functionscorequerybuilder functionscorequerybuilder = querybuilders.functionscorequery()</code>
<code> </code><code>.add(querybuilders.boolquery().should(querybuilders.matchquery(</code><code>"cityname"</code><code>, searchcontent)),</code>
<code> </code><code>scorefunctionbuilders.weightfactorfunction(1000))</code>
<code> </code><code>.add(querybuilders.boolquery().should(querybuilders.matchquery(</code><code>"description"</code><code>, searchcontent)),</code>
<code> </code><code>scorefunctionbuilders.weightfactorfunction(100));</code>
<code> </code><code>//</code> <code>创建搜索 dsl 查询</code>
<code> </code><code>searchquery searchquery = new nativesearchquerybuilder()</code>
<code> </code><code>.withpageable(pageable)</code>
<code> </code><code>.withquery(functionscorequerybuilder).build();</code>
<code> </code><code>logger.info(</code><code>"\n searchcity(): searchcontent ["</code> <code>+ searchcontent +</code><code>"] \n dsl = \n "</code> <code>+ searchquery.getquery().tostring());</code>
<code> </code><code>page<city> searchpageresults = cityrepository.search(searchquery);</code>
<code> </code><code>return</code> <code>searchpageresults.getcontent();</code>
保存逻辑很简单。
分页 function score query 搜索逻辑如下:
先创建分页参数,然后用 functionscorequerybuilder 定义 function score query,并设置对应字段的权重分值。城市名称 1000 分,description 100 分。
然后创建该搜索的 dsl 查询,并打印出来。
实际场景还会很复杂。这里只是点睛之笔,后续大家优化或者更改下 dsl 语句就可以完成自己想要的搜索规则。
欢迎扫一扫我的公众号关注 — 及时得到博客订阅哦!
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicGcq5CMzQzX2Q2NiV2NlFjM0Q2Yfh2ZfJ3bm9VZk92YyF3LcFDMvw1NxAjMvw1ckF2bsBXdvwFduVGdu92YtA3dvwVbvNmL0V2aj92c5JmL3d3dvw1LcpDc0RHaiojIsJye.jpg)