From 7efa2191b8d5315207d434629167301a408d6e70 Mon Sep 17 00:00:00 2001 From: JeffLi1993 Date: Tue, 20 Jun 2017 19:03:41 +0800 Subject: [PATCH] =?UTF-8?q?Spring=20Data=20Elasticsearch=20-=20=E5=AE=9E?= =?UTF-8?q?=E6=88=98=E6=A1=88=E4=BE=8B=E8=AF=A6=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +- pom.xml | 1 + spring-data-elasticsearch-query/pom.xml | 39 +++++++ .../org/spring/springboot/Application.java | 20 ++++ .../controller/CityRestController.java | 47 ++++++++ .../org/spring/springboot/domain/City.java | 68 ++++++++++++ .../springboot/repository/CityRepository.java | 66 +++++++++++ .../springboot/service/CityService.java | 31 ++++++ .../service/impl/CityESServiceImpl.java | 105 ++++++++++++++++++ .../src/main/resources/application.properties | 3 + 10 files changed, 383 insertions(+), 1 deletion(-) create mode 100755 spring-data-elasticsearch-query/pom.xml create mode 100644 spring-data-elasticsearch-query/src/main/java/org/spring/springboot/Application.java create mode 100644 spring-data-elasticsearch-query/src/main/java/org/spring/springboot/controller/CityRestController.java create mode 100644 spring-data-elasticsearch-query/src/main/java/org/spring/springboot/domain/City.java create mode 100644 spring-data-elasticsearch-query/src/main/java/org/spring/springboot/repository/CityRepository.java create mode 100644 spring-data-elasticsearch-query/src/main/java/org/spring/springboot/service/CityService.java create mode 100644 spring-data-elasticsearch-query/src/main/java/org/spring/springboot/service/impl/CityESServiceImpl.java create mode 100644 spring-data-elasticsearch-query/src/main/resources/application.properties diff --git a/README.md b/README.md index 394031e..5038ff4 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,9 @@ Dubbo 服务提供者工程和 Dubbo 服务消费者工程
#### 『 Spring Data ES 篇 』 - spring-data-elasticsearch-crud
-Spring Data Elasticsearch - 基本案例 + [《Spring Data Elasticsearch - 基本案例》](http://spring4all.com/article/70 "Spring Data Elasticsearch - 基本案例")
+- spring-data-elasticsearch-query
+spring-data-elasticsearch - 实战案例详解 ## 二、项目 Quick Start 快速开发指南 #### a. 基本环境配置 diff --git a/pom.xml b/pom.xml index 9c9becf..cce40de 100644 --- a/pom.xml +++ b/pom.xml @@ -50,6 +50,7 @@ spring-data-elasticsearch-crud + spring-data-elasticsearch-query diff --git a/spring-data-elasticsearch-query/pom.xml b/spring-data-elasticsearch-query/pom.xml new file mode 100755 index 0000000..60235d9 --- /dev/null +++ b/spring-data-elasticsearch-query/pom.xml @@ -0,0 +1,39 @@ + + + 4.0.0 + + springboot + spring-data-elasticsearch-query + 0.0.1-SNAPSHOT + spring-data-elasticsearch-query :: spring-data-elasticsearch - 实战案例详解 + + + + org.springframework.boot + spring-boot-starter-parent + 1.5.1.RELEASE + + + + + + + org.springframework.boot + spring-boot-starter-data-elasticsearch + + + + + org.springframework.boot + spring-boot-starter-web + + + + + junit + junit + 4.12 + + + diff --git a/spring-data-elasticsearch-query/src/main/java/org/spring/springboot/Application.java b/spring-data-elasticsearch-query/src/main/java/org/spring/springboot/Application.java new file mode 100644 index 0000000..3fe97d3 --- /dev/null +++ b/spring-data-elasticsearch-query/src/main/java/org/spring/springboot/Application.java @@ -0,0 +1,20 @@ +package org.spring.springboot; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * Spring Boot 应用启动类 + * + * Created by bysocket on 20/06/2017. + */ +// Spring Boot 应用的标识 +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + // 程序启动入口 + // 启动嵌入式的 Tomcat 并初始化 Spring 环境及其各 Spring 组件 + SpringApplication.run(Application.class,args); + } +} diff --git a/spring-data-elasticsearch-query/src/main/java/org/spring/springboot/controller/CityRestController.java b/spring-data-elasticsearch-query/src/main/java/org/spring/springboot/controller/CityRestController.java new file mode 100644 index 0000000..2df1a96 --- /dev/null +++ b/spring-data-elasticsearch-query/src/main/java/org/spring/springboot/controller/CityRestController.java @@ -0,0 +1,47 @@ +package org.spring.springboot.controller; + +import org.spring.springboot.domain.City; +import org.spring.springboot.service.CityService; +import org.springframework.beans.factory.annotation.Autowired; + +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 城市 Controller 实现 Restful HTTP 服务 + *

+ * Created by bysocket on 20/06/2017. + */ +@RestController +public class CityRestController { + + @Autowired + private CityService cityService; + + /** + * 插入 ES 新城市 + * + * @param city + * @return + */ + @RequestMapping(value = "/api/city", method = RequestMethod.POST) + public Long createCity(@RequestBody City city) { + return cityService.saveCity(city); + } + + /** + * 搜索返回分页结果 + * + * @param pageNumber 当前页码 + * @param pageSize 每页大小 + * @param searchContent 搜索内容 + * @return + */ + @RequestMapping(value = "/api/city/search", method = RequestMethod.GET) + public List searchCity(@RequestParam(value = "pageNumber") Integer pageNumber, + @RequestParam(value = "pageSize", required = false) Integer pageSize, + @RequestParam(value = "searchContent") String searchContent) { + return cityService.searchCity(pageNumber, pageSize,searchContent); + } +} diff --git a/spring-data-elasticsearch-query/src/main/java/org/spring/springboot/domain/City.java b/spring-data-elasticsearch-query/src/main/java/org/spring/springboot/domain/City.java new file mode 100644 index 0000000..5905f24 --- /dev/null +++ b/spring-data-elasticsearch-query/src/main/java/org/spring/springboot/domain/City.java @@ -0,0 +1,68 @@ +package org.spring.springboot.domain; + +import org.springframework.data.elasticsearch.annotations.Document; + +import java.io.Serializable; + +/** + * 城市实体类 + *

+ * Created by bysocket on 20/06/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; + } +} diff --git a/spring-data-elasticsearch-query/src/main/java/org/spring/springboot/repository/CityRepository.java b/spring-data-elasticsearch-query/src/main/java/org/spring/springboot/repository/CityRepository.java new file mode 100644 index 0000000..c51c729 --- /dev/null +++ b/spring-data-elasticsearch-query/src/main/java/org/spring/springboot/repository/CityRepository.java @@ -0,0 +1,66 @@ +package org.spring.springboot.repository; + +import org.spring.springboot.domain.City; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.elasticsearch.annotations.Query; +import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; + +import java.util.List; + +/** + * ES 操作类 + *

+ * Created by bysocket on 20/06/2017. + */ +public interface CityRepository extends ElasticsearchRepository { + /** + * AND 语句查询 + * + * @param description + * @param score + * @return + */ + List findByDescriptionAndScore(String description, Integer score); + + /** + * OR 语句查询 + * + * @param description + * @param score + * @return + */ + List findByDescriptionOrScore(String description, Integer score); + + /** + * 查询城市描述 + * + * 等同于下面代码 + * @Query("{\"bool\" : {\"must\" : {\"term\" : {\"description\" : \"?0\"}}}}") + * Page findByDescription(String description, Pageable pageable); + * + * @param description + * @param page + * @return + */ + Page findByDescription(String description, Pageable page); + + /** + * NOT 语句查询 + * + * @param description + * @param page + * @return + */ + Page findByDescriptionNot(String description, Pageable page); + + /** + * LIKE 语句查询 + * + * @param description + * @param page + * @return + */ + Page findByDescriptionLike(String description, Pageable page); + +} diff --git a/spring-data-elasticsearch-query/src/main/java/org/spring/springboot/service/CityService.java b/spring-data-elasticsearch-query/src/main/java/org/spring/springboot/service/CityService.java new file mode 100644 index 0000000..c6cd894 --- /dev/null +++ b/spring-data-elasticsearch-query/src/main/java/org/spring/springboot/service/CityService.java @@ -0,0 +1,31 @@ + +package org.spring.springboot.service; + +import org.spring.springboot.domain.City; + +import java.util.List; + +/** + * 城市 ES 业务接口类 + * + */ +public interface CityService { + + /** + * 新增 ES 城市信息 + * + * @param city + * @return + */ + Long saveCity(City city); + + /** + * 搜索词搜索,分页返回城市信息 + * + * @param pageNumber 当前页码 + * @param pageSize 每页大小 + * @param searchContent 搜索内容 + * @return + */ + List searchCity(Integer pageNumber, Integer pageSize, String searchContent); +} \ No newline at end of file diff --git a/spring-data-elasticsearch-query/src/main/java/org/spring/springboot/service/impl/CityESServiceImpl.java b/spring-data-elasticsearch-query/src/main/java/org/spring/springboot/service/impl/CityESServiceImpl.java new file mode 100644 index 0000000..4c5b22b --- /dev/null +++ b/spring-data-elasticsearch-query/src/main/java/org/spring/springboot/service/impl/CityESServiceImpl.java @@ -0,0 +1,105 @@ +package org.spring.springboot.service.impl; + +import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder; +import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.spring.springboot.domain.City; +import org.spring.springboot.repository.CityRepository; +import org.spring.springboot.service.CityService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; +import org.springframework.data.elasticsearch.core.query.SearchQuery; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 城市 ES 业务逻辑实现类 + *

+ * 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 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 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/cn/elasticsearch/guide/current/function-score-query.html + // - 短语匹配 https://www.elastic.co/guide/cn/elasticsearch/guide/current/phrase-matching.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(); + } + + +} diff --git a/spring-data-elasticsearch-query/src/main/resources/application.properties b/spring-data-elasticsearch-query/src/main/resources/application.properties new file mode 100644 index 0000000..4f1ed6d --- /dev/null +++ b/spring-data-elasticsearch-query/src/main/resources/application.properties @@ -0,0 +1,3 @@ +# ES +spring.data.elasticsearch.repositories.enabled = true +spring.data.elasticsearch.cluster-nodes = 127.0.0.1:9300 \ No newline at end of file