天天看点

Spring Boot 整合 Elasticsearch,实现 function score query 权重分查询

摘要: 原创出处 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 &lt;= 1.3.5 y &lt;= 1.3.4 z &lt;= 1.7.2* x &gt;= 1.4.x 2.0.0 &lt;=y &lt; 5.0.0** 2.0.0 &lt;= z &lt; 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&amp;pagesize=10&amp;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>&lt;?</code><code>xml</code> <code>version</code><code>=</code><code>"1.0"</code> <code>encoding</code><code>=</code><code>"utf-8"</code><code>?&gt;</code>

<code>&lt;</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>&gt;</code>

<code>    </code><code>&lt;</code><code>modelversion</code><code>&gt;4.0.0&lt;/</code><code>modelversion</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>groupid</code><code>&gt;springboot&lt;/</code><code>groupid</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>artifactid</code><code>&gt;springboot-elasticsearch&lt;/</code><code>artifactid</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>version</code><code>&gt;0.0.1-snapshot&lt;/</code><code>version</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>name</code><code>&gt;springboot-elasticsearch :: 整合 elasticsearch &lt;/</code><code>name</code><code>&gt;</code>

<code>    </code><code>&lt;!-- spring boot 启动父依赖 --&gt;</code>

<code>    </code><code>&lt;</code><code>parent</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>groupid</code><code>&gt;org.springframework.boot&lt;/</code><code>groupid</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>artifactid</code><code>&gt;spring-boot-starter-parent&lt;/</code><code>artifactid</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>version</code><code>&gt;1.5.1.release&lt;/</code><code>version</code><code>&gt;</code>

<code>    </code><code>&lt;/</code><code>parent</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>dependencies</code><code>&gt;</code>

<code>        </code><code>&lt;!-- spring boot elasticsearch 依赖 --&gt;</code>

<code>        </code><code>&lt;</code><code>dependency</code><code>&gt;</code>

<code>            </code><code>&lt;</code><code>groupid</code><code>&gt;org.springframework.boot&lt;/</code><code>groupid</code><code>&gt;</code>

<code>            </code><code>&lt;</code><code>artifactid</code><code>&gt;spring-boot-starter-data-elasticsearch&lt;/</code><code>artifactid</code><code>&gt;</code>

<code>        </code><code>&lt;/</code><code>dependency</code><code>&gt;</code>

<code>        </code><code>&lt;!-- spring boot web 依赖 --&gt;</code>

<code>            </code><code>&lt;</code><code>artifactid</code><code>&gt;spring-boot-starter-web&lt;/</code><code>artifactid</code><code>&gt;</code>

<code>        </code><code>&lt;!-- junit --&gt;</code>

<code>            </code><code>&lt;</code><code>groupid</code><code>&gt;junit&lt;/</code><code>groupid</code><code>&gt;</code>

<code>            </code><code>&lt;</code><code>artifactid</code><code>&gt;junit&lt;/</code><code>artifactid</code><code>&gt;</code>

<code>            </code><code>&lt;</code><code>version</code><code>&gt;4.12&lt;/</code><code>version</code><code>&gt;</code>

<code>    </code><code>&lt;/</code><code>dependencies</code><code>&gt;</code>

<code>&lt;/</code><code>project</code><code>&gt;</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&lt;city,long&gt; {</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&lt;city&gt; 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&lt;city&gt; 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 语句就可以完成自己想要的搜索规则。

欢迎扫一扫我的公众号关注 — 及时得到博客订阅哦!

Spring Boot 整合 Elasticsearch,实现 function score query 权重分查询