mirror of
https://github.com/spring-projects/spring-data-elasticsearch.git
synced 2025-06-26 22:02:26 +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;
|
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;
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -28,11 +33,13 @@ public class SearchHit<T> {
|
|||||||
|
|
||||||
private final String id;
|
private final String id;
|
||||||
private final float score;
|
private final float score;
|
||||||
|
private final List<Object> sortValues;
|
||||||
private final T content;
|
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.id = id;
|
||||||
this.score = score;
|
this.score = score;
|
||||||
|
this.sortValues = (sortValues != null) ? Arrays.asList(sortValues) : new ArrayList<>();
|
||||||
this.content = content;
|
this.content = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,12 +62,16 @@ public class SearchHit<T> {
|
|||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the sort values if the query had a sort criterion.
|
||||||
|
*/
|
||||||
|
public List<Object> getSortValues() {
|
||||||
|
return Collections.unmodifiableList(sortValues);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "SearchHit{" +
|
return "SearchHit{" + "id='" + id + '\'' + ", score=" + score + ", sortValues=" + sortValues + ", content="
|
||||||
"id='" + id + '\'' +
|
+ content + '}';
|
||||||
", score=" + score +
|
|
||||||
", content=" + content +
|
|
||||||
'}';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -601,9 +601,10 @@ public class MappingElasticsearchConverter
|
|||||||
public <T> SearchHit<T> read(Class<T> type, SearchDocument searchDocument) {
|
public <T> SearchHit<T> read(Class<T> type, SearchDocument searchDocument) {
|
||||||
String id = searchDocument.hasId() ? searchDocument.getId() : null;
|
String id = searchDocument.hasId() ? searchDocument.getId() : null;
|
||||||
float score = searchDocument.getScore();
|
float score = searchDocument.getScore();
|
||||||
|
Object[] sortValues = searchDocument.getSortValues();
|
||||||
T content = mapDocument(searchDocument, type);
|
T content = mapDocument(searchDocument, type);
|
||||||
|
|
||||||
return new SearchHit<T>(id, score, content);
|
return new SearchHit<T>(id, score, sortValues, content);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -142,7 +142,7 @@ public class DocumentAdapters {
|
|||||||
BytesReference sourceRef = source.getSourceRef();
|
BytesReference sourceRef = source.getSourceRef();
|
||||||
|
|
||||||
if (sourceRef == null || sourceRef.length() == 0) {
|
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()));
|
fromDocumentFields(source, source.getId(), source.getVersion()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +153,7 @@ public class DocumentAdapters {
|
|||||||
document.setVersion(source.getVersion());
|
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 {
|
static class SearchDocumentAdapter implements SearchDocument {
|
||||||
|
|
||||||
private final float score;
|
private final float score;
|
||||||
|
private final Object[] sortValues;
|
||||||
private final Map<String, List<Object>> fields = new HashMap<>();
|
private final Map<String, List<Object>> fields = new HashMap<>();
|
||||||
private final Document delegate;
|
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.score = score;
|
||||||
|
this.sortValues = sortValues;
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
fields.forEach((name, documentField) -> this.fields.put(name, documentField.getValues()));
|
fields.forEach((name, documentField) -> this.fields.put(name, documentField.getValues()));
|
||||||
}
|
}
|
||||||
@ -476,6 +478,15 @@ public class DocumentAdapters {
|
|||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see org.springframework.data.elasticsearch.core.document.SearchDocument#getSortValues()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object[] getSortValues() {
|
||||||
|
return sortValues;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
* @see org.springframework.data.elasticsearch.core.document.Document#hasId()
|
* @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.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extension to {@link Document} exposing a search response related data.
|
* Extension to {@link Document} exposing a search response related data.
|
||||||
*
|
*
|
||||||
@ -45,6 +47,7 @@ public interface SearchDocument extends Document {
|
|||||||
*
|
*
|
||||||
* @param name the field name
|
* @param name the field name
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
default <V> V getFieldValue(final String name) {
|
default <V> V getFieldValue(final String name) {
|
||||||
List<Object> values = getFields().get(name);
|
List<Object> values = getFields().get(name);
|
||||||
if (values == null || values.isEmpty()) {
|
if (values == null || values.isEmpty()) {
|
||||||
@ -52,4 +55,12 @@ public interface SearchDocument extends Document {
|
|||||||
}
|
}
|
||||||
return (V) values.get(0);
|
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.createIndex(SampleEntityUUIDKeyed.class);
|
||||||
indexOperations.putMapping(SampleEntityUUIDKeyed.class);
|
indexOperations.putMapping(SampleEntityUUIDKeyed.class);
|
||||||
|
|
||||||
|
indexOperations.createIndex(SearchHitsEntity.class);
|
||||||
|
indexOperations.putMapping(SearchHitsEntity.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
@ -146,6 +149,7 @@ public abstract class ElasticsearchTemplateTests {
|
|||||||
indexOperations.deleteIndex(INDEX_1_NAME);
|
indexOperations.deleteIndex(INDEX_1_NAME);
|
||||||
indexOperations.deleteIndex(INDEX_2_NAME);
|
indexOperations.deleteIndex(INDEX_2_NAME);
|
||||||
indexOperations.deleteIndex(INDEX_3_NAME);
|
indexOperations.deleteIndex(INDEX_3_NAME);
|
||||||
|
indexOperations.deleteIndex(SearchHitsEntity.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test // DATAES-106
|
@Test // DATAES-106
|
||||||
@ -2856,6 +2860,41 @@ public abstract class ElasticsearchTemplateTests {
|
|||||||
assertThat(map).containsKey("index.max_result_window");
|
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() {
|
protected RequestFactory getRequestFactory() {
|
||||||
return ((AbstractElasticsearchTemplate) operations).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 java.util.stream.IntStream;
|
||||||
|
|
||||||
import org.elasticsearch.ElasticsearchStatusException;
|
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.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -693,6 +695,27 @@ public class ReactiveElasticsearchTemplateTests {
|
|||||||
.verifyComplete();
|
.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
|
@Data
|
||||||
@Document(indexName = "marvel", type = "characters")
|
@Document(indexName = "marvel", type = "characters")
|
||||||
static class Person {
|
static class Person {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user