Spring Boot 整合 Elasticsearch,實(shí)現(xiàn) function score query 權(quán)重分查詢(xún) | 泥瓦匠BYSocket

2023-05-11 10:18 更新

摘要: 原創(chuàng)出處 www.bysocket.com 「泥瓦匠BYSocket 」歡迎轉(zhuǎn)載,保留摘要,謝謝!

『 預(yù)見(jiàn)未來(lái)最好的方式就是親手創(chuàng)造未來(lái) – 《史蒂夫·喬布斯傳》 』

運(yùn)行環(huán)境:JDK 7 或 8,Maven 3.0+
技術(shù)棧:SpringBoot 1.5+,ElasticSearch 2.3.2

本文提綱
一、ES 的使用場(chǎng)景
二、運(yùn)行 springboot-elasticsearch 工程
三、springboot-elasticsearch 工程代碼詳解

一、ES 的使用場(chǎng)景

簡(jiǎn)單說(shuō),ElasticSearch(簡(jiǎn)稱(chēng) ES)是搜索引擎,是結(jié)構(gòu)化數(shù)據(jù)的分布式搜索引擎。在《Elasticsearch 和插件 elasticsearch-head 安裝詳解》  和 《Elasticsearch 默認(rèn)配置 IK 及 Java AnalyzeRequestBuilder 使用》 我詳細(xì)的介紹了如何安裝,初步使用了 IK 分詞器。這里,我主要講下 SpringBoot 工程中如何使用 ElasticSearch。

ES 的使用場(chǎng)景大致分為兩塊
1. 全文檢索。加上分詞(IK 是其中一個(gè))、拼音插件等可以成為強(qiáng)大的全文搜索引擎。
2. 日志統(tǒng)計(jì)分析??梢詫?shí)時(shí)動(dòng)態(tài)分析海量日志數(shù)據(jù)。

二、運(yùn)行 springboot-elasticsearch 工程

注意的是這里使用的是 ElasticSearch 2.3.2。是因?yàn)?a href="http://www.o2fo.com/targetlink?url=https://github.com/spring-projects/spring-data-elasticsearch/wiki/Spring-Data-Elasticsearch---Spring-Boot---version-matrix" target="_blank">版本對(duì)應(yīng)關(guān)系 :

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**
* - 只需要你修改下對(duì)應(yīng)的 pom 文件版本號(hào)
** - 下一個(gè) ES 的版本會(huì)有重大的更新

git clone 下載工程 springboot-elasticsearch ,項(xiàng)目地址見(jiàn) GitHub – https://github.com/JeffLi1993/springboot-learning-example。

1. 后臺(tái)起守護(hù)線程啟動(dòng) Elasticsearch

cd elasticsearch-2.3.2/
./bin/elasticsearch -d

下面開(kāi)始運(yùn)行工程步驟(Quick Start):

2. 項(xiàng)目結(jié)構(gòu)介紹

org.spring.springboot.controller - Controller 層
org.spring.springboot.repository - ES 數(shù)據(jù)操作層
org.spring.springboot.domain - 實(shí)體類(lèi)
org.spring.springboot.service - ES 業(yè)務(wù)邏輯層
Application - 應(yīng)用啟動(dòng)類(lèi)
application.properties - 應(yīng)用配置文件,應(yīng)用啟動(dòng)會(huì)自動(dòng)讀取配置

本地啟動(dòng)的 ES ,就不需要改配置文件了。如果連測(cè)試 ES 服務(wù)地址,需要修改相應(yīng)配置

3.編譯工程
在項(xiàng)目根目錄 springboot-elasticsearch,運(yùn)行 maven 指令:

mvn cleaninstall

4.運(yùn)行工程
右鍵運(yùn)行 Application 應(yīng)用啟動(dòng)類(lèi)(位置:/springboot-learning-example/springboot-elasticsearch/src/main/java/org/spring/springboot/Application.java)的 main 函數(shù),這樣就成功啟動(dòng)了 springboot-elasticsearch 案例。

用 Postman 工具新增兩個(gè)城市
新增城市信息

POST http://127.0.0.1:8080/api/city
{
"id":"1",
"provinceid":"1",
"cityname":"溫嶺",
"description":"溫嶺是個(gè)好城市"
}
POST http://127.0.0.1:8080/api/city
{
"id":"2",
"provinceid":"2",
"cityname":"溫州",
"description":"溫州是個(gè)熱城市"
}

可以打開(kāi) ES 可視化工具 head 插件:http://localhost:9200/_plugin/head/:
(如果不知道怎么安裝,請(qǐng)查閱 《Elasticsearch 和插件 elasticsearch-head 安裝詳解》 。)
在「數(shù)據(jù)瀏覽」tab,可以查閱到 ES 中數(shù)據(jù)是否被插入,插入后的數(shù)據(jù)格式如下:

{
"_index":"cityindex",
"_type":"city",
"_id":"1",
"_version": 1,
"_score": 1,
"_source": {
"id": 1,
"provinceid": 1,
"cityname":"溫嶺",
"description":"溫嶺是個(gè)好城市"
}
}

下面驗(yàn)證下權(quán)重分查詢(xún)搜索接口的實(shí)現(xiàn):
GET http://localhost:8080/api/city/search?pageNumber=0&pageSize=10&searchContent=溫嶺
數(shù)據(jù)是會(huì)出現(xiàn)

[
{
"id": 1,
"provinceid": 1,
"cityname":"溫嶺",
"description":"溫嶺是個(gè)好城市"
},
{
"id": 2,
"provinceid": 2,
"cityname":"溫州",
"description":"溫州是個(gè)熱城市"
}
]

從啟動(dòng)后臺(tái) Console 可以看出,打印出來(lái)對(duì)應(yīng)的 DSL 語(yǔ)句:

{
"function_score" : {
"functions" : [ {
"filter" : {
"bool" : {
"should" : {
"match" : {
"cityname" : {
"query" :"溫嶺",
"type" :"boolean"
}
}
}
}
},
"weight" : 1000.0
}, {
"filter" : {
"bool" : {
"should" : {
"match" : {
"description" : {
"query" :"溫嶺",
"type" :"boolean"
}
}
}
}
},
"weight" : 100.0
} ]
}
}

為什么會(huì)出現(xiàn) 溫州 城市呢?因?yàn)?function score query 權(quán)重分查詢(xún),無(wú)相關(guān)的數(shù)據(jù)默認(rèn)分值為 1。如果想除去,設(shè)置一個(gè) setMinScore 分值即可。

三、springboot-elasticsearch 工程代碼詳解

具體代碼見(jiàn) GitHubhttps://github.com/JeffLi1993/springboot-learning-example
1.pom.xml 依賴(lài)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>springboot</groupId>
    <artifactId>springboot-elasticsearch</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-elasticsearch :: 整合 Elasticsearch </name>
    <!-- Spring Boot 啟動(dòng)父依賴(lài) -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.1.RELEASE</version>
    </parent>
    <dependencies>
        <!-- Spring Boot Elasticsearch 依賴(lài) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>
        <!-- Spring Boot Web 依賴(lài) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
</project>

這里依賴(lài)的 spring-boot-starter-data-elasticsearch 版本是 1.5.1.RELEASE,對(duì)應(yīng)的 spring-data-elasticsearch 版本是 2.1.0.RELEASE。后面數(shù)據(jù)操作層都是通過(guò)該 spring-data-elasticsearch 提供的接口實(shí)現(xiàn)。

操作對(duì)應(yīng)官方文檔:http://docs.spring.io/spring-data/elasticsearch/docs/2.1.0.RELEASE/reference/html/。

2. application.properties 配置 ES 地址

# ES
spring.data.elasticsearch.repositories.enabled =true
spring.data.elasticsearch.cluster-nodes = 127.0.0.1:9300

默認(rèn) 9300 是 Java 客戶(hù)端的端口。9200 是支持 Restful HTTP 的接口。
更多配置:

spring.data.elasticsearch.cluster-name Elasticsearch 集群名。(默認(rèn)值: elasticsearch)
spring.data.elasticsearch.cluster-nodes 集群節(jié)點(diǎn)地址列表,用逗號(hào)分隔。如果沒(méi)有指定,就啟動(dòng)一個(gè)客戶(hù)端節(jié)點(diǎn)。
spring.data.elasticsearch.propertie 用來(lái)配置客戶(hù)端的額外屬性。
spring.data.elasticsearch.repositories.enabled 開(kāi)啟 Elasticsearch 倉(cāng)庫(kù)。(默認(rèn)值:true。)

3. ES 數(shù)據(jù)操作層

@Repository
public interface CityRepositoryextends ElasticsearchRepository<City,Long> {
}

接口只要繼承 ElasticsearchRepository 類(lèi)即可。默認(rèn)會(huì)提供很多實(shí)現(xiàn),比如 CRUD 和搜索相關(guān)的實(shí)現(xiàn)。

4. 實(shí)體類(lèi)

@Document(indexName ="cityindex",type ="city")
public class City implements Serializable{
    private static final long serialVersionUID = -1L;
    /**
     * 城市編號(hào)
     */
    private Longid;
    /**
     * 省份編號(hào)
     */
    private Long provinceid;
    /**
     * 城市名稱(chēng)
     */
    private String cityname;
    /**
     * 描述
     */
    private String description;
}

注意
index 配置必須是全部小寫(xiě),不然會(huì)報(bào)異常。
org.elasticsearch.indices.InvalidIndexNameException: Invalid index name [cityIndex], must be lowercase

5. ES 業(yè)務(wù)邏輯層

/**
 * 城市 ES 業(yè)務(wù)邏輯實(shí)現(xiàn)類(lèi)
 *
 * Created by bysocket on 07/02/2017.
 */
@Service
public class CityESServiceImpl implements CityService {
    private static final Logger LOGGER = LoggerFactory.getLogger(CityESServiceImpl.class);
    @Autowired
    CityRepository cityRepository;
    @Override
    public Long saveCity(City city) {
        City cityResult = cityRepository.save(city);
        return cityResult.getId();
    }
    @Override
    public List<City> searchCity(Integer pageNumber,
                                 Integer pageSize,
                                 String searchContent) {
        // 分頁(yè)參數(shù)
        Pageable pageable = new PageRequest(pageNumber, pageSize);
        // Function Score Query
        FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery()
                .add(QueryBuilders.boolQuery().should(QueryBuilders.matchQuery("cityname", searchContent)),
                    ScoreFunctionBuilders.weightFactorFunction(1000))
                .add(QueryBuilders.boolQuery().should(QueryBuilders.matchQuery("description", searchContent)),
                        ScoreFunctionBuilders.weightFactorFunction(100));
        // 創(chuàng)建搜索 DSL 查詢(xún)
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withPageable(pageable)
                .withQuery(functionScoreQueryBuilder).build();
        LOGGER.info("\n searchCity(): searchContent [" + searchContent +"] \n DSL  = \n " + searchQuery.getQuery().toString());
        Page<City> searchPageResults = cityRepository.search(searchQuery);
        return searchPageResults.getContent();
    }
}

保存邏輯很簡(jiǎn)單。

分頁(yè) function score query 搜索邏輯如下:

先創(chuàng)建分頁(yè)參數(shù),然后用 FunctionScoreQueryBuilder 定義 Function Score Query,并設(shè)置對(duì)應(yīng)字段的權(quán)重分值。城市名稱(chēng) 1000 分,description 100 分。
然后創(chuàng)建該搜索的 DSL 查詢(xún),并打印出來(lái)。

四、小結(jié)

實(shí)際場(chǎng)景還會(huì)很復(fù)雜。這里只是點(diǎn)睛之筆,后續(xù)大家優(yōu)化或者更改下 DSL 語(yǔ)句就可以完成自己想要的搜索規(guī)則。

推薦:《Spring Boot 整合 Dubbo/ZooKeeper 詳解 SOA 案例
上一篇:《Spring Boot 整合 Mybatis Annotation 注解案例

歡迎掃一掃我的公眾號(hào)關(guān)注 — 及時(shí)得到博客訂閱哦!
http://www.bysocket.com/
https://github.com/JeffLi1993




以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)