├── src ├── main │ ├── resources │ │ └── application.yml │ └── java │ │ └── io │ │ └── github │ │ └── mosiki │ │ ├── EsSearchApplication.java │ │ ├── app │ │ ├── repository │ │ │ └── BookRepository.java │ │ ├── query │ │ │ └── BookQuery.java │ │ ├── document │ │ │ └── Book.java │ │ └── mapper │ │ │ └── ExtResultMapper.java │ │ └── common │ │ └── utils │ │ ├── DateUtils.java │ │ └── Result.java └── test │ └── java │ └── io │ └── github │ └── mosiki │ └── search │ ├── EsSearchApplicationTests.java │ ├── BookRepositoryTest.java │ └── HighlightBookRepositoryTest.java ├── .gitignore ├── README.MD └── pom.xml /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | data: 3 | elasticsearch: 4 | cluster-nodes: 192.168.199.192:9300 5 | repositories: 6 | enabled: true 7 | properties: 8 | transport: 9 | tcp: 10 | connect_timeout: 120s 11 | # 集群名称 12 | cluster-name: es-search -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | /nbproject/private/ 21 | /build/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ -------------------------------------------------------------------------------- /src/main/java/io/github/mosiki/EsSearchApplication.java: -------------------------------------------------------------------------------- 1 | package io.github.mosiki; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class EsSearchApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(EsSearchApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/io/github/mosiki/app/repository/BookRepository.java: -------------------------------------------------------------------------------- 1 | package io.github.mosiki.app.repository; 2 | 3 | import io.github.mosiki.app.document.Book; 4 | import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; 5 | 6 | 7 | 8 | /** 9 | * 类名称:BookRepository 10 | * 类描述: 11 | * 创建人:WeJan 12 | * 创建时间:2018-09-02 10:27 13 | */ 14 | public interface BookRepository extends ElasticsearchRepository { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/io/github/mosiki/search/EsSearchApplicationTests.java: -------------------------------------------------------------------------------- 1 | package io.github.mosiki.search; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class EsSearchApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/github/mosiki/common/utils/DateUtils.java: -------------------------------------------------------------------------------- 1 | package io.github.mosiki.common.utils; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.Calendar; 5 | import java.util.Date; 6 | 7 | /** 8 | * 类名称:DateUtils 9 | * 类描述: 10 | * 创建人:WeJan 11 | * 创建时间:2018年09月04日 14:39 12 | */ 13 | public class DateUtils { 14 | 15 | public static String format(Date date, String style) { 16 | SimpleDateFormat df = new SimpleDateFormat(style); 17 | return df.format(date); 18 | } 19 | 20 | public static String getDate(Date d) { 21 | return format(d, "yyyy-MM-dd HH:mm:ss"); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/io/github/mosiki/app/query/BookQuery.java: -------------------------------------------------------------------------------- 1 | package io.github.mosiki.app.query; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.Data; 5 | import org.springframework.data.annotation.Id; 6 | 7 | import java.util.Date; 8 | 9 | /** 10 | * 类名称:BookQuery 11 | * 类描述:封装Book 查询参数 12 | * 创建人:WeJan 13 | * 创建时间:2018年09月04日 13:30 14 | */ 15 | @Data 16 | public class BookQuery { 17 | 18 | private String queryString; 19 | 20 | private Integer page = 1; 21 | 22 | private Integer size = 20; 23 | 24 | private Integer wordsBegin; 25 | 26 | private Integer wordsEnd; 27 | 28 | private Integer sort; 29 | 30 | private Boolean vip; 31 | 32 | private Integer site; 33 | 34 | private Integer collection; 35 | 36 | private Integer click; 37 | 38 | private Integer popularity; 39 | 40 | private Integer goods; 41 | 42 | private Integer status; 43 | 44 | private Date updatetime; 45 | 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/io/github/mosiki/common/utils/Result.java: -------------------------------------------------------------------------------- 1 | package io.github.mosiki.common.utils; 2 | 3 | import java.util.HashMap; 4 | 5 | public class Result extends HashMap { 6 | private static final long serialVersionUID = -4964594876005675011L; 7 | 8 | public Result() { 9 | put("code", 200); 10 | put("msg", "success"); 11 | } 12 | 13 | public static Result error() { 14 | return error(500, "未知异常"); 15 | } 16 | 17 | public static Result error(String msg) { 18 | return error(500, msg); 19 | } 20 | 21 | public static Result error(int code, String msg) { 22 | Result result = new Result(); 23 | result.put("code", code); 24 | result.put("msg", msg); 25 | return result; 26 | } 27 | 28 | public static Result success(String msg) { 29 | Result result = new Result(); 30 | result.put("msg", msg); 31 | return result; 32 | } 33 | 34 | public static Result success() { 35 | return new Result(); 36 | } 37 | 38 | /** 39 | * 使用示例:Result put = Result.success().put(new Date()); 40 | * @param value 41 | * @return 42 | */ 43 | public Result put(Object value) { 44 | return put("data", value); 45 | } 46 | 47 | public Result put(String key, Object value) { 48 | super.put(key, value); 49 | return this; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/io/github/mosiki/app/document/Book.java: -------------------------------------------------------------------------------- 1 | package io.github.mosiki.app.document; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import lombok.Data; 5 | import org.springframework.data.annotation.Id; 6 | import org.springframework.data.elasticsearch.annotations.Document; 7 | 8 | import java.io.Serializable; 9 | import java.util.Date; 10 | 11 | /** 12 | * 类名称:Book 13 | * 类描述: 14 | * 创建人:WeJan 15 | * 创建时间:2018-09-02 09:51 16 | * 17 | * indexName : 索引名称 18 | * type: 索引类型 19 | * createIndex: 最好关闭,否则启动项目会自动建立索引,索引最好手动建 20 | */ 21 | @Data 22 | @Document(indexName = "novel", type = "book", createIndex = false) 23 | public class Book implements Serializable { 24 | 25 | private static final long serialVersionUID = 8504604495927552402L; 26 | 27 | /** 28 | * 需要添加 @Id 标识主键 29 | */ 30 | @Id 31 | private Integer id; 32 | 33 | private Integer words; 34 | 35 | private String intro; 36 | 37 | private String name; 38 | 39 | private Integer sort; 40 | 41 | private Boolean vip; 42 | 43 | private Integer site; 44 | 45 | private String author; 46 | 47 | private Integer collection; 48 | 49 | private Integer click; 50 | 51 | private Integer popularity; 52 | 53 | private Integer goods; 54 | 55 | private Integer status; 56 | 57 | /** 58 | * 需要自定义时间格式化格式,否则会使用默认时间格式化 59 | */ 60 | @JsonFormat (shape = JsonFormat.Shape.STRING, pattern ="yyyy-MM-dd HH:mm:ss") 61 | private Date updatetime; 62 | 63 | } 64 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | ## SpringDataElasticSearch入门示例 2 | 3 | 1. 演示 SpringDataElasticSearch 的基本查询语法的使用 4 | 详见:`io.github.mosiki.search.BookRepositoryTest` 5 | 1. 演示 SpringDataElasticSearch 的高亮查询 以及权重查询 6 | 详见:`io.github.mosiki.search.HighlightBookRepositoryTest` 7 | 8 | 具体文章,详见我的博客 [http://nullpointer.pw](http://nullpointer.pw) 9 | ## 系列文章 10 | - 一、[和我一起打造个简单搜索之ElasticSearch集群搭建](http://nullpointer.pw/%E5%92%8C%E6%88%91%E4%B8%80%E8%B5%B7%E6%89%93%E9%80%A0%E4%B8%AA%E7%AE%80%E5%8D%95%E6%90%9C%E7%B4%A2%E4%B9%8BElasticSearch%E9%9B%86%E7%BE%A4%E6%90%AD%E5%BB%BA.html) 11 | - 二、[和我一起打造个简单搜索之ElasticSearch入门](http://nullpointer.pw/%E5%92%8C%E6%88%91%E4%B8%80%E8%B5%B7%E6%89%93%E9%80%A0%E4%B8%AA%E7%AE%80%E5%8D%95%E6%90%9C%E7%B4%A2%E4%B9%8BElasticSearch%E5%85%A5%E9%97%A8.html) 12 | - 三、[和我一起打造个简单搜索之IK分词以及拼音分词](http://nullpointer.pw/%E5%92%8C%E6%88%91%E4%B8%80%E8%B5%B7%E6%89%93%E9%80%A0%E4%B8%AA%E7%AE%80%E5%8D%95%E6%90%9C%E7%B4%A2%E4%B9%8BIK%E5%88%86%E8%AF%8D%E4%BB%A5%E5%8F%8A%E6%8B%BC%E9%9F%B3%E5%88%86%E8%AF%8D.html) 13 | - 四、[和我一起打造个简单搜索之Logstash实时同步建立索引](http://nullpointer.pw/%E5%92%8C%E6%88%91%E4%B8%80%E8%B5%B7%E6%89%93%E9%80%A0%E4%B8%AA%E7%AE%80%E5%8D%95%E6%90%9C%E7%B4%A2%E4%B9%8BLogstash%E5%AE%9E%E6%97%B6%E5%90%8C%E6%AD%A5%E5%BB%BA%E7%AB%8B%E7%B4%A2%E5%BC%95.html) 14 | - 五、[和我一起打造个简单搜索之SpringDataElasticSearch入门](http://nullpointer.pw/%E5%92%8C%E6%88%91%E4%B8%80%E8%B5%B7%E6%89%93%E9%80%A0%E4%B8%AA%E7%AE%80%E5%8D%95%E6%90%9C%E7%B4%A2%E4%B9%8BSpringDataElasticSearch%E5%85%A5%E9%97%A8.html) 15 | - 六、[和我一起打造个简单搜索之SpringDataElasticSearch关键词高亮](http://nullpointer.pw/%E5%92%8C%E6%88%91%E4%B8%80%E8%B5%B7%E6%89%93%E9%80%A0%E4%B8%AA%E7%AE%80%E5%8D%95%E6%90%9C%E7%B4%A2%E4%B9%8BSpringDataElasticSearch%E5%85%B3%E9%94%AE%E8%AF%8D%E9%AB%98%E4%BA%AE.html) 16 | - 七、[ES搜索结果调优](http://nullpointer.pw/ES%E6%90%9C%E7%B4%A2%E7%BB%93%E6%9E%9C%E8%B0%83%E4%BC%98.html) -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | io.github.mosiki 7 | es-search 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | es-search 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.0.4.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-data-elasticsearch 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-web 35 | 36 | 37 | org.projectlombok 38 | lombok 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-test 43 | test 44 | 45 | 46 | commons-beanutils 47 | commons-beanutils 48 | 1.9.3 49 | 50 | 51 | 52 | 53 | 54 | 55 | org.springframework.boot 56 | spring-boot-maven-plugin 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/test/java/io/github/mosiki/search/BookRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package io.github.mosiki.search; 2 | 3 | 4 | import io.github.mosiki.app.document.Book; 5 | import io.github.mosiki.app.mapper.ExtResultMapper; 6 | import io.github.mosiki.app.query.BookQuery; 7 | import io.github.mosiki.app.repository.BookRepository; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.elasticsearch.index.query.*; 10 | import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder; 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.boot.test.context.SpringBootTest; 15 | import org.springframework.data.domain.Page; 16 | import org.springframework.data.domain.PageRequest; 17 | import org.springframework.data.domain.Sort; 18 | import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; 19 | import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage; 20 | import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; 21 | import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; 22 | import org.springframework.stereotype.Component; 23 | import org.springframework.test.context.junit4.SpringRunner; 24 | 25 | import javax.annotation.Resource; 26 | 27 | /** 28 | * 类名称:BookRepositoryTest 29 | * 类描述:演示SpringDataElasticSearch基本查询 30 | * 创建人:WeJan 31 | * 创建时间:2018-09-02 10:38 32 | */ 33 | @Slf4j 34 | @Component 35 | public class BookRepositoryTest extends EsSearchApplicationTests{ 36 | 37 | @Autowired 38 | private BookRepository bookRepository; 39 | 40 | @Test 41 | public void findBook() { 42 | 43 | BookQuery query = new BookQuery(); 44 | query.setQueryString("魔"); 45 | query.setSite(2);// 1 是男生 2 是女生 46 | query.setSort(29); // 29 是玄幻 47 | query.setVip(true);// 查询 vip 作品 48 | query.setWordsBegin(0); // 查询字数在 0-25w 之间的作品 49 | query.setWordsEnd(500000); 50 | query.setPage(1);// 分页页码 51 | query.setSize(10);// 每页显示数 52 | 53 | // 复合查询 54 | BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); 55 | 56 | // 以下为查询条件, 使用 must query 进行查询组合 57 | MultiMatchQueryBuilder matchQuery = QueryBuilders.multiMatchQuery(query.getQueryString(), "name", "intro", "author"); 58 | boolQuery.must(matchQuery); 59 | 60 | // 以下为过滤筛选条件,使用 filter 比使用 must query 性能要好 61 | TermQueryBuilder siteQuery = QueryBuilders.termQuery("site", query.getSite()); 62 | boolQuery.filter(siteQuery); 63 | TermQueryBuilder sortQuery = QueryBuilders.termQuery("sort", query.getSort()); 64 | boolQuery.filter(sortQuery); 65 | TermQueryBuilder vipQuery = QueryBuilders.termQuery("vip", query.getVip()); 66 | boolQuery.filter(vipQuery); 67 | RangeQueryBuilder wordsQuery = QueryBuilders.rangeQuery("words").gt(query.getWordsBegin()).lt(query.getWordsEnd()); 68 | boolQuery.filter(wordsQuery); 69 | 70 | Sort sort = Sort.by(Sort.Direction.DESC, "click"); 71 | // 分页 同时根据 点击数 click 进行降序排列 72 | PageRequest pageRequest = PageRequest.of(query.getPage() - 1, query.getSize(), sort); 73 | 74 | // log.info("{}", boolQuery); // 打印出查询 json 75 | bookRepository.search(boolQuery, pageRequest) 76 | .forEach(e -> log.info("作品信息:{}", e)); 77 | } 78 | } -------------------------------------------------------------------------------- /src/test/java/io/github/mosiki/search/HighlightBookRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package io.github.mosiki.search; 2 | 3 | 4 | import io.github.mosiki.app.document.Book; 5 | import io.github.mosiki.app.mapper.ExtResultMapper; 6 | import io.github.mosiki.app.query.BookQuery; 7 | import io.github.mosiki.app.repository.BookRepository; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.elasticsearch.index.query.*; 10 | import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder; 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.boot.test.context.SpringBootTest; 15 | import org.springframework.data.domain.Page; 16 | import org.springframework.data.domain.PageRequest; 17 | import org.springframework.data.domain.Sort; 18 | import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; 19 | import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; 20 | import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; 21 | import org.springframework.stereotype.Component; 22 | import org.springframework.test.context.junit4.SpringRunner; 23 | 24 | import javax.annotation.Resource; 25 | 26 | /** 27 | * 类名称:BookRepositoryTest 28 | * 类描述:演示SpringDataElasticSearch高亮查询 29 | * 创建人:WeJan 30 | * 创建时间:2018-09-02 10:38 31 | */ 32 | @Slf4j 33 | @Component 34 | public class HighlightBookRepositoryTest extends EsSearchApplicationTests { 35 | 36 | @Autowired 37 | private ElasticsearchTemplate elasticsearchTemplate; 38 | @Resource 39 | private ExtResultMapper extResultMapper; 40 | 41 | @Test 42 | public void testHighlightQuery() { 43 | BookQuery query = new BookQuery(); 44 | query.setQueryString("穿越"); 45 | 46 | // 复合查询 47 | BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); 48 | 49 | PageRequest pageRequest = PageRequest.of(query.getPage() - 1, query.getSize()); 50 | NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder() 51 | .withQuery(boolQuery) 52 | .withHighlightFields( 53 | new HighlightBuilder.Field("name").preTags("").postTags(""), 54 | new HighlightBuilder.Field("author").preTags("").postTags("")) 55 | .withPageable(pageRequest); 56 | 57 | // 以下为查询条件, 使用 must query 进行查询组合 58 | // MultiMatchQueryBuilder matchQuery = QueryBuilders.multiMatchQuery(query.getQueryString(), "name", "intro", "author"); 59 | // boolQuery.must(matchQuery); 60 | String queryString = query.getQueryString(); 61 | // 最佳字段 + 降低除了name之外字段的权重系数 62 | MatchQueryBuilder nameQuery = QueryBuilders.matchQuery("name", queryString); 63 | MatchQueryBuilder authorQuery = QueryBuilders.matchQuery("author", queryString).boost(0.8f); 64 | DisMaxQueryBuilder disMaxQueryBuilder = QueryBuilders.disMaxQuery().add(nameQuery).add(authorQuery); 65 | queryBuilder.withQuery(disMaxQueryBuilder); 66 | 67 | NativeSearchQuery searchQuery = queryBuilder.build(); 68 | Page books = elasticsearchTemplate.queryForPage(searchQuery, Book.class, extResultMapper); 69 | 70 | books.forEach(e -> log.info("{}", e)); 71 | // 穿越小道人 72 | } 73 | } -------------------------------------------------------------------------------- /src/main/java/io/github/mosiki/app/mapper/ExtResultMapper.java: -------------------------------------------------------------------------------- 1 | package io.github.mosiki.app.mapper; 2 | 3 | import com.fasterxml.jackson.core.JsonEncoding; 4 | import com.fasterxml.jackson.core.JsonFactory; 5 | import com.fasterxml.jackson.core.JsonGenerator; 6 | import org.apache.commons.beanutils.PropertyUtils; 7 | import org.elasticsearch.action.get.GetResponse; 8 | import org.elasticsearch.action.get.MultiGetItemResponse; 9 | import org.elasticsearch.action.get.MultiGetResponse; 10 | import org.elasticsearch.action.search.SearchResponse; 11 | import org.elasticsearch.common.text.Text; 12 | import org.elasticsearch.search.SearchHit; 13 | import org.elasticsearch.search.SearchHitField; 14 | import org.elasticsearch.search.fetch.subphase.highlight.HighlightField; 15 | import org.springframework.data.domain.Pageable; 16 | import org.springframework.data.elasticsearch.ElasticsearchException; 17 | import org.springframework.data.elasticsearch.annotations.Document; 18 | import org.springframework.data.elasticsearch.annotations.ScriptedField; 19 | import org.springframework.data.elasticsearch.core.AbstractResultMapper; 20 | import org.springframework.data.elasticsearch.core.DefaultEntityMapper; 21 | import org.springframework.data.elasticsearch.core.EntityMapper; 22 | import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage; 23 | import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl; 24 | import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity; 25 | import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty; 26 | import org.springframework.data.mapping.context.MappingContext; 27 | import org.springframework.stereotype.Component; 28 | import org.springframework.util.Assert; 29 | import org.springframework.util.StringUtils; 30 | 31 | import java.io.ByteArrayOutputStream; 32 | import java.io.IOException; 33 | import java.lang.reflect.InvocationTargetException; 34 | import java.nio.charset.Charset; 35 | import java.util.*; 36 | 37 | /** 38 | * 类名称:ExtResultMapper 39 | * 类描述:自定义结果映射类 40 | * 创建人:WeJan 41 | * 创建时间:2018-09-13 20:47 42 | */ 43 | @Component 44 | public class ExtResultMapper extends AbstractResultMapper { 45 | 46 | private MappingContext, ElasticsearchPersistentProperty> mappingContext; 47 | 48 | public ExtResultMapper() { 49 | super(new DefaultEntityMapper()); 50 | } 51 | 52 | public ExtResultMapper(MappingContext, ElasticsearchPersistentProperty> mappingContext) { 53 | super(new DefaultEntityMapper()); 54 | this.mappingContext = mappingContext; 55 | } 56 | 57 | public ExtResultMapper(EntityMapper entityMapper) { 58 | super(entityMapper); 59 | } 60 | 61 | public ExtResultMapper( 62 | MappingContext, ElasticsearchPersistentProperty> mappingContext, 63 | EntityMapper entityMapper) { 64 | super(entityMapper); 65 | this.mappingContext = mappingContext; 66 | } 67 | 68 | @Override 69 | public AggregatedPage mapResults(SearchResponse response, Class clazz, Pageable pageable) { 70 | long totalHits = response.getHits().totalHits(); 71 | List results = new ArrayList<>(); 72 | for (SearchHit hit : response.getHits()) { 73 | if (hit != null) { 74 | T result = null; 75 | if (StringUtils.hasText(hit.sourceAsString())) { 76 | result = mapEntity(hit.sourceAsString(), clazz); 77 | } else { 78 | result = mapEntity(hit.getFields().values(), clazz); 79 | } 80 | setPersistentEntityId(result, hit.getId(), clazz); 81 | setPersistentEntityVersion(result, hit.getVersion(), clazz); 82 | populateScriptFields(result, hit); 83 | populateHighLightedFields(result, hit.getHighlightFields()); 84 | results.add(result); 85 | } 86 | } 87 | 88 | return new AggregatedPageImpl(results, pageable, totalHits, response.getAggregations(), response.getScrollId()); 89 | } 90 | 91 | private void populateHighLightedFields(T result, Map highlightFields) { 92 | for (HighlightField field : highlightFields.values()) { 93 | try { 94 | PropertyUtils.setProperty(result, field.getName(), concat(field.fragments())); 95 | } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { 96 | throw new ElasticsearchException("failed to set highlighted value for field: " + field.getName() 97 | + " with value: " + Arrays.toString(field.getFragments()), e); 98 | } 99 | } 100 | } 101 | 102 | private String concat(Text[] texts) { 103 | StringBuffer sb = new StringBuffer(); 104 | for (Text text : texts) { 105 | sb.append(text.toString()); 106 | } 107 | return sb.toString(); 108 | } 109 | 110 | private void populateScriptFields(T result, SearchHit hit) { 111 | if (hit.getFields() != null && !hit.getFields().isEmpty() && result != null) { 112 | for (java.lang.reflect.Field field : result.getClass().getDeclaredFields()) { 113 | ScriptedField scriptedField = field.getAnnotation(ScriptedField.class); 114 | if (scriptedField != null) { 115 | String name = scriptedField.name().isEmpty() ? field.getName() : scriptedField.name(); 116 | SearchHitField searchHitField = hit.getFields().get(name); 117 | if (searchHitField != null) { 118 | field.setAccessible(true); 119 | try { 120 | field.set(result, searchHitField.getValue()); 121 | } catch (IllegalArgumentException e) { 122 | throw new ElasticsearchException("failed to set scripted field: " + name + " with value: " 123 | + searchHitField.getValue(), e); 124 | } catch (IllegalAccessException e) { 125 | throw new ElasticsearchException("failed to access scripted field: " + name, e); 126 | } 127 | } 128 | } 129 | } 130 | } 131 | } 132 | 133 | private T mapEntity(Collection values, Class clazz) { 134 | return mapEntity(buildJSONFromFields(values), clazz); 135 | } 136 | 137 | private String buildJSONFromFields(Collection values) { 138 | JsonFactory nodeFactory = new JsonFactory(); 139 | try { 140 | ByteArrayOutputStream stream = new ByteArrayOutputStream(); 141 | JsonGenerator generator = nodeFactory.createGenerator(stream, JsonEncoding.UTF8); 142 | generator.writeStartObject(); 143 | for (SearchHitField value : values) { 144 | if (value.getValues().size() > 1) { 145 | generator.writeArrayFieldStart(value.getName()); 146 | for (Object val : value.getValues()) { 147 | generator.writeObject(val); 148 | } 149 | generator.writeEndArray(); 150 | } else { 151 | generator.writeObjectField(value.getName(), value.getValue()); 152 | } 153 | } 154 | generator.writeEndObject(); 155 | generator.flush(); 156 | return new String(stream.toByteArray(), Charset.forName("UTF-8")); 157 | } catch (IOException e) { 158 | return null; 159 | } 160 | } 161 | 162 | @Override 163 | public T mapResult(GetResponse response, Class clazz) { 164 | T result = mapEntity(response.getSourceAsString(), clazz); 165 | if (result != null) { 166 | setPersistentEntityId(result, response.getId(), clazz); 167 | setPersistentEntityVersion(result, response.getVersion(), clazz); 168 | } 169 | return result; 170 | } 171 | 172 | @Override 173 | public LinkedList mapResults(MultiGetResponse responses, Class clazz) { 174 | LinkedList list = new LinkedList<>(); 175 | for (MultiGetItemResponse response : responses.getResponses()) { 176 | if (!response.isFailed() && response.getResponse().isExists()) { 177 | T result = mapEntity(response.getResponse().getSourceAsString(), clazz); 178 | setPersistentEntityId(result, response.getResponse().getId(), clazz); 179 | setPersistentEntityVersion(result, response.getResponse().getVersion(), clazz); 180 | list.add(result); 181 | } 182 | } 183 | return list; 184 | } 185 | 186 | private void setPersistentEntityId(T result, String id, Class clazz) { 187 | 188 | if (mappingContext != null && clazz.isAnnotationPresent(Document.class)) { 189 | 190 | ElasticsearchPersistentEntity persistentEntity = mappingContext.getRequiredPersistentEntity(clazz); 191 | ElasticsearchPersistentProperty idProperty = persistentEntity.getIdProperty(); 192 | 193 | // Only deal with String because ES generated Ids are strings ! 194 | if (idProperty != null && idProperty.getType().isAssignableFrom(String.class)) { 195 | persistentEntity.getPropertyAccessor(result).setProperty(idProperty, id); 196 | } 197 | 198 | } 199 | } 200 | 201 | private void setPersistentEntityVersion(T result, long version, Class clazz) { 202 | if (mappingContext != null && clazz.isAnnotationPresent(Document.class)) { 203 | 204 | ElasticsearchPersistentEntity persistentEntity = mappingContext.getPersistentEntity(clazz); 205 | ElasticsearchPersistentProperty versionProperty = persistentEntity.getVersionProperty(); 206 | 207 | // Only deal with Long because ES versions are longs ! 208 | if (versionProperty != null && versionProperty.getType().isAssignableFrom(Long.class)) { 209 | // check that a version was actually returned in the response, -1 would indicate that 210 | // a search didn't request the version ids in the response, which would be an issue 211 | Assert.isTrue(version != -1, "Version in response is -1"); 212 | persistentEntity.getPropertyAccessor(result).setProperty(versionProperty, version); 213 | } 214 | } 215 | } 216 | } 217 | --------------------------------------------------------------------------------