1、搜索實戰場景需求
2、運行 spring-data-elasticsearch-query 工程
3、spring-data-elasticsearch-query 工程代碼詳解html
搜索的場景會不少,經常使用的搜索場景,須要搜索的字段不少,但每一個字段匹配到後所佔的權重又不一樣。好比電商網站的搜索,搜到商品名稱和商品描述,天然商品名稱的權重遠遠大於商品描述。並且單詞匹配確定不如短語匹配。這樣就出現了新的需求,如何肯定這些短語,即天然分詞。那就利用分詞器,便可獲得所須要的短語,而後進行搜索。
下面介紹短語如何進行按權重分匹配搜索。java
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 } }
具體代碼見 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。