[elc] add support for sorting results by SortOptions to NativeQuery.

Original Pull Request #2264
Closes #2263
This commit is contained in:
Sascha Woo 2022-08-10 21:34:17 +02:00 committed by GitHub
parent c4910bf399
commit 6cfb8eeea6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 88 additions and 5 deletions

View File

@ -15,6 +15,7 @@
*/ */
package org.springframework.data.elasticsearch.client.elc; 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.aggregations.Aggregation;
import co.elastic.clients.elasticsearch._types.query_dsl.Query; import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.core.search.FieldCollapse; import co.elastic.clients.elasticsearch.core.search.FieldCollapse;
@ -26,7 +27,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import org.springframework.data.elasticsearch.core.query.BaseQuery; 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.data.elasticsearch.core.query.ScriptedField;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
@ -35,6 +35,7 @@ import org.springframework.lang.Nullable;
* Elasticsearch Client library. * Elasticsearch Client library.
* *
* @author Peter-Josef Meisch * @author Peter-Josef Meisch
* @author Sascha Woo
* @since 4.4 * @since 4.4
*/ */
public class NativeQuery extends BaseQuery { public class NativeQuery extends BaseQuery {
@ -46,6 +47,7 @@ public class NativeQuery extends BaseQuery {
@Nullable private Suggester suggester; @Nullable private Suggester suggester;
@Nullable private FieldCollapse fieldCollapse; @Nullable private FieldCollapse fieldCollapse;
private List<ScriptedField> scriptedFields = Collections.emptyList(); private List<ScriptedField> scriptedFields = Collections.emptyList();
private List<SortOptions> sortOptions = Collections.emptyList();
public NativeQuery(NativeQueryBuilder builder) { public NativeQuery(NativeQueryBuilder builder) {
super(builder); super(builder);
@ -55,6 +57,7 @@ public class NativeQuery extends BaseQuery {
this.suggester = builder.getSuggester(); this.suggester = builder.getSuggester();
this.fieldCollapse = builder.getFieldCollapse(); this.fieldCollapse = builder.getFieldCollapse();
this.scriptedFields = builder.getScriptedFields(); this.scriptedFields = builder.getScriptedFields();
this.sortOptions = builder.getSortOptions();
} }
public NativeQuery(@Nullable Query query) { public NativeQuery(@Nullable Query query) {
@ -92,4 +95,8 @@ public class NativeQuery extends BaseQuery {
public List<ScriptedField> getScriptedFields() { public List<ScriptedField> getScriptedFields() {
return scriptedFields; return scriptedFields;
} }
public List<SortOptions> getSortOptions() {
return sortOptions;
}
} }

View File

@ -15,6 +15,7 @@
*/ */
package org.springframework.data.elasticsearch.client.elc; 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.aggregations.Aggregation;
import co.elastic.clients.elasticsearch._types.query_dsl.Query; import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.core.search.FieldCollapse; import co.elastic.clients.elasticsearch.core.search.FieldCollapse;
@ -22,19 +23,21 @@ import co.elastic.clients.elasticsearch.core.search.Suggester;
import co.elastic.clients.util.ObjectBuilder; import co.elastic.clients.util.ObjectBuilder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
import org.springframework.data.elasticsearch.core.query.BaseQueryBuilder; 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.data.elasticsearch.core.query.ScriptedField;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
* @author Peter-Josef Meisch * @author Peter-Josef Meisch
* @author Sascha Woo
* @since 4.4 * @since 4.4
*/ */
public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQueryBuilder> { public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQueryBuilder> {
@ -45,7 +48,10 @@ public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQuer
@Nullable private Suggester suggester; @Nullable private Suggester suggester;
@Nullable private FieldCollapse fieldCollapse; @Nullable private FieldCollapse fieldCollapse;
private final List<ScriptedField> scriptedFields = new ArrayList<>(); private final List<ScriptedField> scriptedFields = new ArrayList<>();
public NativeQueryBuilder() {} private List<SortOptions> sortOptions = new ArrayList<>();
public NativeQueryBuilder() {
}
@Nullable @Nullable
public Query getQuery() { public Query getQuery() {
@ -75,6 +81,10 @@ public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQuer
return scriptedFields; return scriptedFields;
} }
public List<SortOptions> getSortOptions() {
return sortOptions;
}
public NativeQueryBuilder withQuery(Query query) { public NativeQueryBuilder withQuery(Query query) {
Assert.notNull(query, "query must not be null"); Assert.notNull(query, "query must not be null");
@ -129,6 +139,34 @@ public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQuer
return this; return this;
} }
public NativeQueryBuilder withSort(List<SortOptions> 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<SortOptions.Builder, ObjectBuilder<SortOptions>> fn) {
Assert.notNull(fn, "fn must not be null");
withSort(fn.apply(new SortOptions.Builder()).build());
return this;
}
public NativeQuery build() { public NativeQuery build() {
return new NativeQuery(this); return new NativeQuery(this);
} }

View File

@ -101,6 +101,7 @@ import org.springframework.util.StringUtils;
* Class to create Elasticsearch request and request builders. * Class to create Elasticsearch request and request builders.
* *
* @author Peter-Josef Meisch * @author Peter-Josef Meisch
* @author Sascha Woo
* @since 4.4 * @since 4.4
*/ */
class RequestConverter { class RequestConverter {
@ -1405,7 +1406,7 @@ class RequestConverter {
builder // builder //
.suggest(query.getSuggester()) // .suggest(query.getSuggester()) //
.collapse(query.getFieldCollapse()) // .collapse(query.getFieldCollapse()) //
; .sort(query.getSortOptions());
if (!isEmpty(query.getAggregations())) { if (!isEmpty(query.getAggregations())) {
builder.aggregations(query.getAggregations()); builder.aggregations(query.getAggregations());
@ -1424,7 +1425,7 @@ class RequestConverter {
builder // builder //
.suggest(query.getSuggester()) // .suggest(query.getSuggester()) //
.collapse(query.getFieldCollapse()) // .collapse(query.getFieldCollapse()) //
; .sort(query.getSortOptions());
if (!isEmpty(query.getAggregations())) { if (!isEmpty(query.getAggregations())) {
builder.aggregations(query.getAggregations()); builder.aggregations(query.getAggregations());

View File

@ -15,25 +15,34 @@
*/ */
package org.springframework.data.elasticsearch.core; 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.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.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.FunctionBoostMode; import co.elastic.clients.elasticsearch._types.query_dsl.FunctionBoostMode;
import co.elastic.clients.elasticsearch._types.query_dsl.FunctionScoreMode; import co.elastic.clients.elasticsearch._types.query_dsl.FunctionScoreMode;
import co.elastic.clients.elasticsearch.core.search.FieldCollapse; import co.elastic.clients.elasticsearch.core.search.FieldCollapse;
import co.elastic.clients.json.JsonData; import co.elastic.clients.json.JsonData;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.ELCQueries; import org.springframework.data.elasticsearch.ELCQueries;
import org.springframework.data.elasticsearch.client.elc.NativeQuery; import org.springframework.data.elasticsearch.client.elc.NativeQuery;
import org.springframework.data.elasticsearch.client.elc.NativeQueryBuilder; 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.BaseQueryBuilder;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilterBuilder; 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.Query;
import org.springframework.data.elasticsearch.core.query.RescorerQuery; import org.springframework.data.elasticsearch.core.query.RescorerQuery;
import org.springframework.data.elasticsearch.core.query.ScriptData; import org.springframework.data.elasticsearch.core.query.ScriptData;
@ -45,6 +54,7 @@ import org.springframework.test.context.ContextConfiguration;
/** /**
* @author Farid Faoudi * @author Farid Faoudi
* @author Sascha Woo
* @since 4.4 * @since 4.4
*/ */
@ContextConfiguration(classes = { ElasticsearchELCIntegrationTests.Config.class }) @ContextConfiguration(classes = { ElasticsearchELCIntegrationTests.Config.class })
@ -60,6 +70,33 @@ public class ElasticsearchELCIntegrationTests extends ElasticsearchIntegrationTe
} }
} }
@Test // #2263
public void shouldSortResultsBySortOptions() {
List<IndexQuery> 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<SampleEntity> 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 @Override
public boolean newElasticsearchClient() { public boolean newElasticsearchClient() {
return true; return true;