spring-data-elasticsearch 實戰案例詳解(四)

本文提綱

1、搜索實戰場景需求
2、運行 spring-data-elasticsearch-query 工程
3、spring-data-elasticsearch-query 工程代碼詳解html

1、搜索實戰場景需求

搜索的場景會不少,經常使用的搜索場景,須要搜索的字段不少,但每一個字段匹配到後所佔的權重又不一樣。好比電商網站的搜索,搜到商品名稱和商品描述,天然商品名稱的權重遠遠大於商品描述。並且單詞匹配確定不如短語匹配。這樣就出現了新的需求,如何肯定這些短語,即天然分詞。那就利用分詞器,便可獲得所須要的短語,而後進行搜索。
下面介紹短語如何進行按權重分匹配搜索。java



 

2、運行 spring-data-elasticsearch-query 工程

1. 後臺起守護線程啓動 Elasticsearchnode

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

git clone 下載工程 springboot-elasticsearch ,項目地址見 GitHub – https://github.com/JeffLi1993/ … ample
下面開始運行工程步驟(Quick Start):
 git

2. 項目結構介紹github

org.spring.springboot.controller - Controller 層
org.spring.springboot.repository - ES 數據操做層
org.spring.springboot.domain - 實體類
org.spring.springboot.service - ES 業務邏輯層
Application - 應用啓動類
application.properties - 應用配置文件,應用啓動會自動讀取配置

本地啓動的 ES ,就不須要改配置文件了。若是連測試 ES 服務地址,須要修改相應配置web

3.編譯工程
在項目根目錄 spring-data-elasticsearch-query,運行 maven 指令:spring

mvn clean install

4.運行工程
右鍵運行 Application 應用啓動類(位置:org/spring/springboot/Application.java)的 main 函數,這樣就成功啓動了 spring-data-elasticsearch-query 案例。
用 Postman 工具新增兩個城市apache

 

a. 新增城市信息api

POST http://127.0.0.1:8080/api/city
{
    "id」:"1",
    "score":"5",
    "name":"上海",
    "description":"上海是個熱城市"
}
POST http://127.0.0.1:8080/api/city
{
    "id":"2",
    "score」:"4",
    "name」:」溫嶺",
    "description":」溫嶺是個沿海城市"
}

下面是實戰搜索語句的接口:
GET http://localhost:8080/api/city … nt%3D城市
獲取返回結果:
返回 JSON 以下:springboot

[
    {
        "id": 2,
        "name": "溫嶺",
        "description": "溫嶺是個沿海城市",
        "score": 4
    },
    {
        "id": 1,
        "name": "上海",
        "description": "上海是個好城市",
        "score": 3
    }
]

應用的控制檯中,日誌打印出查詢語句的 DSL :

DSL  = 
 {
  "function_score" : {
    "functions" : [ {
      "filter" : {
        "match" : {
          "name" : {
            "query" : "城市",
            "type" : "phrase"
          }
        }
      },
      "weight" : 1000.0
    }, {
      "filter" : {
        "match" : {
          "description" : {
            "query" : "城市",
            "type" : "phrase"
          }
        }
      },
      "weight" : 500.0
    } ],
    "score_mode" : "sum",
    "min_score" : 10.0
  }
}

3、spring-data-elasticsearch-query 工程代碼詳解

具體代碼見 GitHub – JeffLi1993/springboot-learning-example

1.pom.xml 依賴

<?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/ma ... gt%3B
    <modelVersion>4.0.0</modelVersion>
    <groupId>springboot</groupId>
    <artifactId>spring-data-elasticsearch-crud</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-data-elasticsearch-crud :: spring-data-elasticsearch - 基本案例 </name>
    <!-- Spring Boot 啓動父依賴 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.1.RELEASE</version>
    </parent>
    <dependencies>
        <!-- Spring Boot Elasticsearch 依賴 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>
        <!-- Spring Boot Web 依賴 -->
        <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>

這裏依賴的 spring-boot-starter-data-elasticsearch 版本是 1.5.1.RELEASE,對應的 spring-data-elasticsearch 版本是 2.1.0.RELEASE。對應官方文檔:http://docs.spring.io/spring-d … html/。後面數據操做層都是經過該 spring-data-elasticsearch 提供的接口實現。

2. application.properties 配置 ES 地址

# ES
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。)

 

3. ES 數據操做層

/**
 * ES 操做類
 * <p>
 * Created by bysocket on 17/05/2017.
 */
public interface CityRepository extends ElasticsearchRepository<City, Long> {
}

接口只要繼承 ElasticsearchRepository 接口類便可,具體使用的是該接口的方法:

Iterable<T> search(QueryBuilder query);
    Page<T> search(QueryBuilder query, Pageable pageable);
    Page<T> search(SearchQuery searchQuery);
    Page<T> searchSimilar(T entity, String[] fields, Pageable pageable);

4. 實體類

/**
 * 城市實體類
 * <p>
 * Created by bysocket on 03/05/2017.
 */
@Document(indexName = "province", type = "city")
public class City implements Serializable {
    private static final long serialVersionUID = -1L;
    /**
     * 城市編號
     */
    private Long id;
    /**
     * 城市名稱
     */
    private String name;
    /**
     * 描述
     */
    private String description;
    /**
     * 城市評分
     */
    private Integer score;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public Integer getScore() {
        return score;
    }
    public void setScore(Integer score) {
        this.score = score;
    }
}

注意
a. City 屬性名不支持駝峯式。
b. indexName 配置必須是所有小寫,否則會出異常。
org.elasticsearch.indices.InvalidIndexNameException: Invalid index name [provinceIndex], must be lowercase

5. 城市 ES 業務邏輯實現類
代碼以下:

/**
 * 城市 ES 業務邏輯實現類
 * <p>
 * Created by bysocket on 20/06/2017.
 */
@Service
public class CityESServiceImpl implements CityService {
    private static final Logger LOGGER = LoggerFactory.getLogger(CityESServiceImpl.class);
    /* 分頁參數 */
    Integer PAGE_SIZE = 12;          // 每頁數量
    Integer DEFAULT_PAGE_NUMBER = 0; // 默認當前頁碼
    /* 搜索模式 */
    String SCORE_MODE_SUM = "sum"; // 權重分求和模式
    Float  MIN_SCORE = 10.0F;      // 因爲無相關性的分值默認爲 1 ,設置權重分最小值爲 10
    @Autowired
    CityRepository cityRepository; // ES 操做類
    public Long saveCity(City city) {
        City cityResult = cityRepository.save(city);
        return cityResult.getId();
    }
    @Override
    public List<City> searchCity(Integer pageNumber, Integer pageSize, String searchContent) {
        // 校驗分頁參數
        if (pageSize == null || pageSize <= 0) {
            pageSize = PAGE_SIZE;
        }
        if (pageNumber == null || pageNumber < DEFAULT_PAGE_NUMBER) {
            pageNumber = DEFAULT_PAGE_NUMBER;
        }
        LOGGER.info("\n searchCity: searchContent [" + searchContent + "] \n ");
        // 構建搜索查詢
        SearchQuery searchQuery = getCitySearchQuery(pageNumber,pageSize,searchContent);
        LOGGER.info("\n searchCity: searchContent [" + searchContent + "] \n DSL  = \n " + searchQuery.getQuery().toString());
        Page<City> cityPage = cityRepository.search(searchQuery);
        return cityPage.getContent();
    }
    /**
     * 根據搜索詞構造搜索查詢語句
     *
     * 代碼流程:
     *      - 權重分查詢
     *      - 短語匹配
     *      - 設置權重分最小值
     *      - 設置分頁參數
     *
     * @param pageNumber 當前頁碼
     * @param pageSize 每頁大小
     * @param searchContent 搜索內容
     * @return
     */
    private SearchQuery getCitySearchQuery(Integer pageNumber, Integer pageSize,String searchContent) {
        // 短語匹配到的搜索詞,求和模式累加權重分
        // 權重分查詢 https://www.elastic.co/guide/c ... .html
        //   - 短語匹配 https://www.elastic.co/guide/c ... .html
        //   - 字段對應權重分設置,能夠優化成 enum
        //   - 因爲無相關性的分值默認爲 1 ,設置權重分最小值爲 10
        FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery()
                .add(QueryBuilders.matchPhraseQuery("name", searchContent),
                ScoreFunctionBuilders.weightFactorFunction(1000))
                .add(QueryBuilders.matchPhraseQuery("description", searchContent),
                ScoreFunctionBuilders.weightFactorFunction(500))
                .scoreMode(SCORE_MODE_SUM).setMinScore(MIN_SCORE);
        // 分頁參數
        Pageable pageable = new PageRequest(pageNumber, pageSize);
        return new NativeSearchQueryBuilder()
                .withPageable(pageable)
                .withQuery(functionScoreQueryBuilder).build();
    }
}

能夠看到該過程實現了,短語精準匹配以及匹配到根據字段權重分求和,從而實現按權重搜索查詢。代碼流程以下:
– 權重分查詢
– 短語匹配
– 設置權重分最小值
– 設置分頁參數

注意:
– 字段對應權重分設置,能夠優化成 enum
– 因爲無相關性的分值默認爲 1 ,設置權重分最小值爲 10

權重分查詢文檔:https://www.elastic.co/guide/c … .html
短語匹配文檔: https://www.elastic.co/guide/c … .html

相關文章
相關標籤/搜索