From 1a751bf1dd31709de261d0c7877a09d8ce74e036 Mon Sep 17 00:00:00 2001 From: Sascha Woo Date: Tue, 9 Aug 2022 10:50:40 +0200 Subject: [PATCH] [elc] add support for sorting results by SortOptions to NativeQuery --- .../elasticsearch/client/elc/NativeQuery.java | 9 +++- .../client/elc/NativeQueryBuilder.java | 42 ++++++++++++++++++- .../client/elc/RequestConverter.java | 5 ++- .../ElasticsearchELCIntegrationTests.java | 37 ++++++++++++++++ 4 files changed, 88 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/springframework/data/elasticsearch/client/elc/NativeQuery.java b/src/main/java/org/springframework/data/elasticsearch/client/elc/NativeQuery.java index c690d6d42..fb77f28fb 100644 --- a/src/main/java/org/springframework/data/elasticsearch/client/elc/NativeQuery.java +++ b/src/main/java/org/springframework/data/elasticsearch/client/elc/NativeQuery.java @@ -15,6 +15,7 @@ */ package org.springframework.data.elasticsearch.client.elc; +import co.elastic.clients.elasticsearch._types.SortOptions; import co.elastic.clients.elasticsearch._types.aggregations.Aggregation; import co.elastic.clients.elasticsearch._types.query_dsl.Query; import co.elastic.clients.elasticsearch.core.search.FieldCollapse; @@ -26,7 +27,6 @@ import java.util.Map; import org.springframework.data.elasticsearch.core.query.BaseQuery; -import org.springframework.data.elasticsearch.core.query.RescorerQuery; import org.springframework.data.elasticsearch.core.query.ScriptedField; import org.springframework.lang.Nullable; @@ -35,6 +35,7 @@ * Elasticsearch Client library. * * @author Peter-Josef Meisch + * @author Sascha Woo * @since 4.4 */ public class NativeQuery extends BaseQuery { @@ -46,6 +47,7 @@ public class NativeQuery extends BaseQuery { @Nullable private Suggester suggester; @Nullable private FieldCollapse fieldCollapse; private List scriptedFields = Collections.emptyList(); + private List sortOptions = Collections.emptyList(); public NativeQuery(NativeQueryBuilder builder) { super(builder); @@ -55,6 +57,7 @@ public NativeQuery(NativeQueryBuilder builder) { this.suggester = builder.getSuggester(); this.fieldCollapse = builder.getFieldCollapse(); this.scriptedFields = builder.getScriptedFields(); + this.sortOptions = builder.getSortOptions(); } public NativeQuery(@Nullable Query query) { @@ -92,4 +95,8 @@ public FieldCollapse getFieldCollapse() { public List getScriptedFields() { return scriptedFields; } + + public List getSortOptions() { + return sortOptions; + } } diff --git a/src/main/java/org/springframework/data/elasticsearch/client/elc/NativeQueryBuilder.java b/src/main/java/org/springframework/data/elasticsearch/client/elc/NativeQueryBuilder.java index fa0173568..0a046282d 100644 --- a/src/main/java/org/springframework/data/elasticsearch/client/elc/NativeQueryBuilder.java +++ b/src/main/java/org/springframework/data/elasticsearch/client/elc/NativeQueryBuilder.java @@ -15,6 +15,7 @@ */ package org.springframework.data.elasticsearch.client.elc; +import co.elastic.clients.elasticsearch._types.SortOptions; import co.elastic.clients.elasticsearch._types.aggregations.Aggregation; import co.elastic.clients.elasticsearch._types.query_dsl.Query; import co.elastic.clients.elasticsearch.core.search.FieldCollapse; @@ -22,19 +23,21 @@ import co.elastic.clients.util.ObjectBuilder; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.function.Function; import org.springframework.data.elasticsearch.core.query.BaseQueryBuilder; -import org.springframework.data.elasticsearch.core.query.RescorerQuery; import org.springframework.data.elasticsearch.core.query.ScriptedField; import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** * @author Peter-Josef Meisch + * @author Sascha Woo * @since 4.4 */ public class NativeQueryBuilder extends BaseQueryBuilder { @@ -45,7 +48,10 @@ public class NativeQueryBuilder extends BaseQueryBuilder scriptedFields = new ArrayList<>(); - public NativeQueryBuilder() {} + private List sortOptions = new ArrayList<>(); + + public NativeQueryBuilder() { + } @Nullable public Query getQuery() { @@ -75,6 +81,10 @@ public List getScriptedFields() { return scriptedFields; } + public List getSortOptions() { + return sortOptions; + } + public NativeQueryBuilder withQuery(Query query) { Assert.notNull(query, "query must not be null"); @@ -129,6 +139,34 @@ public NativeQueryBuilder withScriptedField(ScriptedField scriptedField) { return this; } + public NativeQueryBuilder withSort(List values) { + + Assert.notEmpty(values, "values must not be empty"); + sortOptions.clear(); + sortOptions.addAll(values); + + return this; + } + + public NativeQueryBuilder withSort(SortOptions value, SortOptions... values) { + + Assert.notNull(value, "value must not be null"); + sortOptions.add(value); + if (values.length > 0) { + sortOptions.addAll(Arrays.asList(values)); + } + + return this; + } + + public NativeQueryBuilder withSort(Function> fn) { + + Assert.notNull(fn, "fn must not be null"); + withSort(fn.apply(new SortOptions.Builder()).build()); + + return this; + } + public NativeQuery build() { return new NativeQuery(this); } diff --git a/src/main/java/org/springframework/data/elasticsearch/client/elc/RequestConverter.java b/src/main/java/org/springframework/data/elasticsearch/client/elc/RequestConverter.java index b40516bc5..7b179dde6 100644 --- a/src/main/java/org/springframework/data/elasticsearch/client/elc/RequestConverter.java +++ b/src/main/java/org/springframework/data/elasticsearch/client/elc/RequestConverter.java @@ -101,6 +101,7 @@ * Class to create Elasticsearch request and request builders. * * @author Peter-Josef Meisch + * @author Sascha Woo * @since 4.4 */ class RequestConverter { @@ -1405,7 +1406,7 @@ private void prepareNativeSearch(NativeQuery query, SearchRequest.Builder builde builder // .suggest(query.getSuggester()) // .collapse(query.getFieldCollapse()) // - ; + .sort(query.getSortOptions()); if (!isEmpty(query.getAggregations())) { builder.aggregations(query.getAggregations()); @@ -1424,7 +1425,7 @@ private void prepareNativeSearch(NativeQuery query, MultisearchBody.Builder buil builder // .suggest(query.getSuggester()) // .collapse(query.getFieldCollapse()) // - ; + .sort(query.getSortOptions()); if (!isEmpty(query.getAggregations())) { builder.aggregations(query.getAggregations()); diff --git a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchELCIntegrationTests.java b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchELCIntegrationTests.java index a57522ee3..50fe9b7ce 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchELCIntegrationTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/ElasticsearchELCIntegrationTests.java @@ -15,25 +15,34 @@ */ package org.springframework.data.elasticsearch.core; +import static org.assertj.core.api.Assertions.*; import static org.springframework.data.elasticsearch.client.elc.QueryBuilders.*; +import static org.springframework.data.elasticsearch.utils.IndexBuilder.*; +import co.elastic.clients.elasticsearch._types.SortOptionsBuilders; +import co.elastic.clients.elasticsearch._types.SortOrder; import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery; import co.elastic.clients.elasticsearch._types.query_dsl.FunctionBoostMode; import co.elastic.clients.elasticsearch._types.query_dsl.FunctionScoreMode; import co.elastic.clients.elasticsearch.core.search.FieldCollapse; import co.elastic.clients.json.JsonData; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.data.elasticsearch.ELCQueries; import org.springframework.data.elasticsearch.client.elc.NativeQuery; import org.springframework.data.elasticsearch.client.elc.NativeQueryBuilder; +import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; import org.springframework.data.elasticsearch.core.query.BaseQueryBuilder; import org.springframework.data.elasticsearch.core.query.FetchSourceFilterBuilder; +import org.springframework.data.elasticsearch.core.query.IndexQuery; import org.springframework.data.elasticsearch.core.query.Query; import org.springframework.data.elasticsearch.core.query.RescorerQuery; import org.springframework.data.elasticsearch.core.query.ScriptData; @@ -45,6 +54,7 @@ /** * @author Farid Faoudi + * @author Sascha Woo * @since 4.4 */ @ContextConfiguration(classes = { ElasticsearchELCIntegrationTests.Config.class }) @@ -60,6 +70,33 @@ IndexNameProvider indexNameProvider() { } } + @Test // #2263 + public void shouldSortResultsBySortOptions() { + + List indexQueries = new ArrayList<>(); + + indexQueries.add(buildIndex(SampleEntity.builder().id("1").message("ab xz").build())); + indexQueries.add(buildIndex(SampleEntity.builder().id("2").message("bc").build())); + indexQueries.add(buildIndex(SampleEntity.builder().id("3").message("ac xz hi").build())); + + operations.bulkIndex(indexQueries, IndexCoordinates.of(indexNameProvider.indexName())); + + NativeQuery query = NativeQuery.builder() + .withSort(b -> b.field(fb -> fb.field("message").order(SortOrder.Asc))).build(); + + SearchHits searchHits = operations.search(query, SampleEntity.class, + IndexCoordinates.of(indexNameProvider.indexName())); + + assertThat(searchHits.getSearchHits()) // + .satisfiesExactly(e -> { + assertThat(e.getId()).isEqualTo("1"); + }, e -> { + assertThat(e.getId()).isEqualTo("3"); + }, e -> { + assertThat(e.getId()).isEqualTo("2"); + }); + } + @Override public boolean newElasticsearchClient() { return true;