diff --git a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java index 556d77b4..1b164204 100755 --- a/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplate.java @@ -59,7 +59,9 @@ import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.aggregations.AbstractAggregationBuilder; import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder; +import org.elasticsearch.search.sort.FieldSortBuilder; import org.elasticsearch.search.sort.SortBuilder; +import org.elasticsearch.search.sort.SortBuilders; import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.search.suggest.SuggestBuilder; import org.slf4j.Logger; @@ -108,6 +110,7 @@ import static org.springframework.util.CollectionUtils.isEmpty; * @author Chris White * @author Mark Paluch * @author Sascha Woo + * @author Ted Liang */ public class ElasticsearchTemplate implements ElasticsearchOperations, ApplicationContextAware { @@ -1003,8 +1006,15 @@ public class ElasticsearchTemplate implements ElasticsearchOperations, Applicati if (query.getSort() != null) { for (Sort.Order order : query.getSort()) { - searchRequestBuilder.addSort(order.getProperty(), - order.getDirection() == Sort.Direction.DESC ? SortOrder.DESC : SortOrder.ASC); + FieldSortBuilder sort = SortBuilders.fieldSort(order.getProperty()) + .order(order.getDirection().isDescending() ? SortOrder.DESC : SortOrder.ASC); + if (order.getNullHandling() == Sort.NullHandling.NULLS_FIRST) { + sort.missing("_first"); + } + else if (order.getNullHandling() == Sort.NullHandling.NULLS_LAST) { + sort.missing("_last"); + } + searchRequestBuilder.addSort(sort); } } diff --git a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java index 569f82ff..33cba185 100755 --- a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchTemplateTests.java @@ -464,6 +464,106 @@ public class ElasticsearchTemplateTests { assertThat(sampleEntities.getContent().get(1).getMessage(), is(sampleEntity1.getMessage())); } + @Test // DATAES-312 + public void shouldSortResultsGivenNullFirstSortCriteria() { + // given + List indexQueries; + + // first document + String documentId = randomNumeric(5); + SampleEntity sampleEntity1 = SampleEntity.builder() + .id(documentId) + .message("abc") + .rate(15) + .version(System.currentTimeMillis()) + .build(); + + // second document + String documentId2 = randomNumeric(5); + SampleEntity sampleEntity2 = SampleEntity.builder() + .id(documentId2) + .message("xyz") + .rate(5) + .version(System.currentTimeMillis()) + .build(); + + // third document + String documentId3 = randomNumeric(5); + SampleEntity sampleEntity3 = SampleEntity.builder() + .id(documentId3) + .rate(10) + .version(System.currentTimeMillis()) + .build(); + + indexQueries = getIndexQueries(Arrays.asList(sampleEntity1, sampleEntity2, sampleEntity3)); + + elasticsearchTemplate.bulkIndex(indexQueries); + elasticsearchTemplate.refresh(SampleEntity.class); + + SearchQuery searchQuery = new NativeSearchQueryBuilder() + .withQuery(matchAllQuery()) + .withPageable(PageRequest.of(0, 10, Sort.by(Sort.Order.asc("message").nullsFirst()))) + .build(); + + // when + Page sampleEntities = elasticsearchTemplate.queryForPage(searchQuery, SampleEntity.class); + + // then + assertThat(sampleEntities.getTotalElements(), equalTo(3L)); + assertThat(sampleEntities.getContent().get(0).getRate(), is(sampleEntity3.getRate())); + assertThat(sampleEntities.getContent().get(1).getMessage(), is(sampleEntity1.getMessage())); + } + + @Test // DATAES-312 + public void shouldSortResultsGivenNullLastSortCriteria() { + // given + List indexQueries; + + // first document + String documentId = randomNumeric(5); + SampleEntity sampleEntity1 = SampleEntity.builder() + .id(documentId) + .message("abc") + .rate(15) + .version(System.currentTimeMillis()) + .build(); + + // second document + String documentId2 = randomNumeric(5); + SampleEntity sampleEntity2 = SampleEntity.builder() + .id(documentId2) + .message("xyz") + .rate(5) + .version(System.currentTimeMillis()) + .build(); + + // third document + String documentId3 = randomNumeric(5); + SampleEntity sampleEntity3 = SampleEntity.builder() + .id(documentId3) + .rate(10) + .version(System.currentTimeMillis()) + .build(); + + indexQueries = getIndexQueries(Arrays.asList(sampleEntity1, sampleEntity2, sampleEntity3)); + + elasticsearchTemplate.bulkIndex(indexQueries); + elasticsearchTemplate.refresh(SampleEntity.class); + + SearchQuery searchQuery = new NativeSearchQueryBuilder() + .withQuery(matchAllQuery()) + .withPageable(PageRequest.of(0, 10, Sort.by(Sort.Order.asc("message").nullsLast()))) + .build(); + + // when + Page sampleEntities = elasticsearchTemplate.queryForPage(searchQuery, SampleEntity.class); + + // then + assertThat(sampleEntities.getTotalElements(), equalTo(3L)); + assertThat(sampleEntities.getContent().get(0).getRate(), is(sampleEntity1.getRate())); + assertThat(sampleEntities.getContent().get(1).getMessage(), is(sampleEntity2.getMessage())); + } + @Test public void shouldExecuteStringQuery() { // given @@ -2070,4 +2170,4 @@ public class ElasticsearchTemplateTests { this.lastName = lastName; } } -} +} \ No newline at end of file