mirror of
https://github.com/spring-projects/spring-data-elasticsearch.git
synced 2025-06-25 13:32:10 +00:00
DATAES-714 - Sort results should be returned in the SearchHits.
Original PR: #361
This commit is contained in:
parent
e55bae725e
commit
d19e699b32
@ -15,6 +15,11 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
@ -28,11 +33,13 @@ public class SearchHit<T> {
|
||||
|
||||
private final String id;
|
||||
private final float score;
|
||||
private final List<Object> sortValues;
|
||||
private final T content;
|
||||
|
||||
public SearchHit(@Nullable String id, float score, T content) {
|
||||
public SearchHit(@Nullable String id, float score, Object[] sortValues, T content) {
|
||||
this.id = id;
|
||||
this.score = score;
|
||||
this.sortValues = (sortValues != null) ? Arrays.asList(sortValues) : new ArrayList<>();
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
@ -55,12 +62,16 @@ public class SearchHit<T> {
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the sort values if the query had a sort criterion.
|
||||
*/
|
||||
public List<Object> getSortValues() {
|
||||
return Collections.unmodifiableList(sortValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SearchHit{" +
|
||||
"id='" + id + '\'' +
|
||||
", score=" + score +
|
||||
", content=" + content +
|
||||
'}';
|
||||
return "SearchHit{" + "id='" + id + '\'' + ", score=" + score + ", sortValues=" + sortValues + ", content="
|
||||
+ content + '}';
|
||||
}
|
||||
}
|
||||
|
@ -601,9 +601,10 @@ public class MappingElasticsearchConverter
|
||||
public <T> SearchHit<T> read(Class<T> type, SearchDocument searchDocument) {
|
||||
String id = searchDocument.hasId() ? searchDocument.getId() : null;
|
||||
float score = searchDocument.getScore();
|
||||
Object[] sortValues = searchDocument.getSortValues();
|
||||
T content = mapDocument(searchDocument, type);
|
||||
|
||||
return new SearchHit<T>(id, score, content);
|
||||
return new SearchHit<T>(id, score, sortValues, content);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -142,7 +142,7 @@ public class DocumentAdapters {
|
||||
BytesReference sourceRef = source.getSourceRef();
|
||||
|
||||
if (sourceRef == null || sourceRef.length() == 0) {
|
||||
return new SearchDocumentAdapter(source.getScore(), source.getFields(),
|
||||
return new SearchDocumentAdapter(source.getScore(), source.getSortValues(), source.getFields(),
|
||||
fromDocumentFields(source, source.getId(), source.getVersion()));
|
||||
}
|
||||
|
||||
@ -153,7 +153,7 @@ public class DocumentAdapters {
|
||||
document.setVersion(source.getVersion());
|
||||
}
|
||||
|
||||
return new SearchDocumentAdapter(source.getScore(), source.getFields(), document);
|
||||
return new SearchDocumentAdapter(source.getScore(), source.getSortValues(), source.getFields(), document);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -438,11 +438,13 @@ public class DocumentAdapters {
|
||||
static class SearchDocumentAdapter implements SearchDocument {
|
||||
|
||||
private final float score;
|
||||
private final Object[] sortValues;
|
||||
private final Map<String, List<Object>> fields = new HashMap<>();
|
||||
private final Document delegate;
|
||||
|
||||
SearchDocumentAdapter(float score, Map<String, DocumentField> fields, Document delegate) {
|
||||
SearchDocumentAdapter(float score, Object[] sortValues, Map<String, DocumentField> fields, Document delegate) {
|
||||
this.score = score;
|
||||
this.sortValues = sortValues;
|
||||
this.delegate = delegate;
|
||||
fields.forEach((name, documentField) -> this.fields.put(name, documentField.getValues()));
|
||||
}
|
||||
@ -476,6 +478,15 @@ public class DocumentAdapters {
|
||||
return fields;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.document.SearchDocument#getSortValues()
|
||||
*/
|
||||
@Override
|
||||
public Object[] getSortValues() {
|
||||
return sortValues;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.document.Document#hasId()
|
||||
|
@ -18,6 +18,8 @@ package org.springframework.data.elasticsearch.core.document;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Extension to {@link Document} exposing a search response related data.
|
||||
*
|
||||
@ -45,6 +47,7 @@ public interface SearchDocument extends Document {
|
||||
*
|
||||
* @param name the field name
|
||||
*/
|
||||
@Nullable
|
||||
default <V> V getFieldValue(final String name) {
|
||||
List<Object> values = getFields().get(name);
|
||||
if (values == null || values.isEmpty()) {
|
||||
@ -52,4 +55,12 @@ public interface SearchDocument extends Document {
|
||||
}
|
||||
return (V) values.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the sort values for the search hit
|
||||
*/
|
||||
@Nullable
|
||||
default Object[] getSortValues() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -128,6 +128,9 @@ public abstract class ElasticsearchTemplateTests {
|
||||
|
||||
indexOperations.createIndex(SampleEntityUUIDKeyed.class);
|
||||
indexOperations.putMapping(SampleEntityUUIDKeyed.class);
|
||||
|
||||
indexOperations.createIndex(SearchHitsEntity.class);
|
||||
indexOperations.putMapping(SearchHitsEntity.class);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
@ -146,6 +149,7 @@ public abstract class ElasticsearchTemplateTests {
|
||||
indexOperations.deleteIndex(INDEX_1_NAME);
|
||||
indexOperations.deleteIndex(INDEX_2_NAME);
|
||||
indexOperations.deleteIndex(INDEX_3_NAME);
|
||||
indexOperations.deleteIndex(SearchHitsEntity.class);
|
||||
}
|
||||
|
||||
@Test // DATAES-106
|
||||
@ -2856,6 +2860,41 @@ public abstract class ElasticsearchTemplateTests {
|
||||
assertThat(map).containsKey("index.max_result_window");
|
||||
}
|
||||
|
||||
@Test // DATAES-714
|
||||
void shouldReturnSortFieldsInSearchHits() {
|
||||
IndexCoordinates index = IndexCoordinates.of("test-index-searchhits-entity-template");
|
||||
SearchHitsEntity entity = SearchHitsEntity.builder().id("1").number(1000L).keyword("thousands").build();
|
||||
IndexQuery indexQuery = new IndexQueryBuilder().withId(entity.getId()).withObject(entity).build();
|
||||
operations.index(indexQuery, index);
|
||||
indexOperations.refresh(index);
|
||||
|
||||
NativeSearchQuery query = new NativeSearchQueryBuilder() //
|
||||
.withQuery(matchAllQuery()) //
|
||||
.withSort(new FieldSortBuilder("keyword").order(SortOrder.ASC))
|
||||
.withSort(new FieldSortBuilder("number").order(SortOrder.DESC)).build();
|
||||
|
||||
SearchHits<SearchHitsEntity> searchHits = operations.search(query, SearchHitsEntity.class, index);
|
||||
|
||||
assertThat(searchHits).isNotNull();
|
||||
assertThat(searchHits.getSearchHits()).hasSize(1);
|
||||
|
||||
SearchHit<SearchHitsEntity> searchHit = searchHits.getSearchHit(0);
|
||||
List<Object> sortValues = searchHit.getSortValues();
|
||||
assertThat(sortValues).hasSize(2);
|
||||
assertThat(sortValues.get(0)).isInstanceOf(String.class).isEqualTo("thousands");
|
||||
// transport client returns Long, rest client Integer
|
||||
java.lang.Object o = sortValues.get(1);
|
||||
if (o instanceof Integer) {
|
||||
Integer i = (Integer) o;
|
||||
assertThat(o).isInstanceOf(Integer.class).isEqualTo(1000);
|
||||
} else if (o instanceof Long) {
|
||||
Long l = (Long) o;
|
||||
assertThat(o).isInstanceOf(Long.class).isEqualTo(1000L);
|
||||
} else {
|
||||
fail("unexpected object type " + o);
|
||||
}
|
||||
}
|
||||
|
||||
protected RequestFactory getRequestFactory() {
|
||||
return ((AbstractElasticsearchTemplate) operations).getRequestFactory();
|
||||
}
|
||||
@ -3007,4 +3046,14 @@ public abstract class ElasticsearchTemplateTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Document(indexName = "test-index-searchhits-entity-template")
|
||||
static class SearchHitsEntity {
|
||||
@Id private String id;
|
||||
@Field(type = FieldType.Long) Long number;
|
||||
@Field(type = FieldType.Keyword) String keyword;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -40,6 +40,8 @@ import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.elasticsearch.ElasticsearchStatusException;
|
||||
import org.elasticsearch.search.sort.FieldSortBuilder;
|
||||
import org.elasticsearch.search.sort.SortOrder;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -693,6 +695,27 @@ public class ReactiveElasticsearchTemplateTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnSortFields() {
|
||||
SampleEntity entity = randomEntity("test message");
|
||||
entity.rate = 42;
|
||||
index(entity);
|
||||
|
||||
NativeSearchQuery query = new NativeSearchQueryBuilder() //
|
||||
.withQuery(matchAllQuery()) //
|
||||
.withSort(new FieldSortBuilder("rate").order(SortOrder.DESC)) //
|
||||
.build();
|
||||
|
||||
template.search(query, SampleEntity.class) //
|
||||
.as(StepVerifier::create) //
|
||||
.consumeNextWith(it -> {
|
||||
List<Object> sortValues = it.getSortValues();
|
||||
assertThat(sortValues).hasSize(1);
|
||||
assertThat(sortValues.get(0)).isEqualTo(42);
|
||||
}) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Data
|
||||
@Document(indexName = "marvel", type = "characters")
|
||||
static class Person {
|
||||
|
Loading…
x
Reference in New Issue
Block a user