mirror of
https://github.com/spring-projects/spring-data-elasticsearch.git
synced 2025-06-12 07:02:10 +00:00
Fix search_after field values (#2679)
Closes #2678 (cherry picked from commit 9adc4d2b368fc600e58ac5d15c02cdc78c0dd743)
This commit is contained in:
parent
063020f8b3
commit
4614c62bb5
@ -19,7 +19,6 @@ import static org.springframework.data.elasticsearch.client.elc.TypeUtils.*;
|
||||
import static org.springframework.util.CollectionUtils.*;
|
||||
|
||||
import co.elastic.clients.elasticsearch._types.Conflicts;
|
||||
import co.elastic.clients.elasticsearch._types.FieldValue;
|
||||
import co.elastic.clients.elasticsearch._types.InlineScript;
|
||||
import co.elastic.clients.elasticsearch._types.OpType;
|
||||
import co.elastic.clients.elasticsearch._types.SortOptions;
|
||||
@ -63,7 +62,6 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
@ -1221,8 +1219,7 @@ class RequestConverter {
|
||||
}
|
||||
|
||||
if (!isEmpty(query.getSearchAfter())) {
|
||||
bb.searchAfter(query.getSearchAfter().stream().map(it -> FieldValue.of(it.toString()))
|
||||
.collect(Collectors.toList()));
|
||||
bb.searchAfter(query.getSearchAfter().stream().map(TypeUtils::toFieldValue).toList());
|
||||
}
|
||||
|
||||
query.getRescorerQueries().forEach(rescorerQuery -> bb.rescore(getRescore(rescorerQuery)));
|
||||
@ -1376,8 +1373,7 @@ class RequestConverter {
|
||||
}
|
||||
|
||||
if (!isEmpty(query.getSearchAfter())) {
|
||||
builder.searchAfter(
|
||||
query.getSearchAfter().stream().map(it -> FieldValue.of(it.toString())).collect(Collectors.toList()));
|
||||
builder.searchAfter(query.getSearchAfter().stream().map(TypeUtils::toFieldValue).toList());
|
||||
}
|
||||
|
||||
query.getRescorerQueries().forEach(rescorerQuery -> builder.rescore(getRescore(rescorerQuery)));
|
||||
|
@ -18,8 +18,15 @@ package org.springframework.data.elasticsearch.client.elc;
|
||||
import co.elastic.clients.elasticsearch._types.*;
|
||||
import co.elastic.clients.elasticsearch._types.mapping.FieldType;
|
||||
import co.elastic.clients.elasticsearch._types.mapping.TypeMapping;
|
||||
import co.elastic.clients.elasticsearch.core.search.*;
|
||||
import co.elastic.clients.elasticsearch.core.search.BoundaryScanner;
|
||||
import co.elastic.clients.elasticsearch.core.search.HighlighterEncoder;
|
||||
import co.elastic.clients.elasticsearch.core.search.HighlighterFragmenter;
|
||||
import co.elastic.clients.elasticsearch.core.search.HighlighterOrder;
|
||||
import co.elastic.clients.elasticsearch.core.search.HighlighterTagsSchema;
|
||||
import co.elastic.clients.elasticsearch.core.search.HighlighterType;
|
||||
import co.elastic.clients.elasticsearch.core.search.ScoreMode;
|
||||
import co.elastic.clients.elasticsearch.indices.IndexSettings;
|
||||
import co.elastic.clients.json.JsonData;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.time.Duration;
|
||||
@ -31,8 +38,13 @@ import java.util.stream.Collectors;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.elasticsearch.core.RefreshPolicy;
|
||||
import org.springframework.data.elasticsearch.core.document.Document;
|
||||
import org.springframework.data.elasticsearch.core.query.*;
|
||||
import org.springframework.data.elasticsearch.core.query.GeoDistanceOrder;
|
||||
import org.springframework.data.elasticsearch.core.query.IndexQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.IndicesOptions;
|
||||
import org.springframework.data.elasticsearch.core.query.Order;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.core.query.RescorerQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.UpdateResponse;
|
||||
import org.springframework.data.elasticsearch.core.reindex.ReindexRequest;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@ -156,6 +168,40 @@ final class TypeUtils {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static FieldValue toFieldValue(@Nullable Object fieldValue) {
|
||||
|
||||
if (fieldValue == null) {
|
||||
return FieldValue.NULL;
|
||||
}
|
||||
|
||||
if (fieldValue instanceof Boolean b) {
|
||||
return b ? FieldValue.TRUE : FieldValue.FALSE;
|
||||
}
|
||||
|
||||
if (fieldValue instanceof String s) {
|
||||
return FieldValue.of(s);
|
||||
}
|
||||
|
||||
if (fieldValue instanceof Long l) {
|
||||
return FieldValue.of(l);
|
||||
}
|
||||
|
||||
if (fieldValue instanceof Integer i) {
|
||||
return FieldValue.of((long) i);
|
||||
}
|
||||
|
||||
if (fieldValue instanceof Double d) {
|
||||
return FieldValue.of(d);
|
||||
}
|
||||
|
||||
if (fieldValue instanceof Float f) {
|
||||
return FieldValue.of((double) f);
|
||||
}
|
||||
|
||||
return FieldValue.of(JsonData.of(fieldValue));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static GeoDistanceType geoDistanceType(GeoDistanceOrder.DistanceType distanceType) {
|
||||
|
||||
|
@ -62,7 +62,7 @@ public abstract class SearchAfterIntegrationTests {
|
||||
@Test
|
||||
@Order(java.lang.Integer.MAX_VALUE)
|
||||
void cleanup() {
|
||||
operations.indexOps(IndexCoordinates.of(indexNameProvider.getPrefix() + "*")).delete();
|
||||
operations.indexOps(IndexCoordinates.of(indexNameProvider.getPrefix() + '*')).delete();
|
||||
}
|
||||
|
||||
@Test // #1143
|
||||
@ -85,11 +85,11 @@ public abstract class SearchAfterIntegrationTests {
|
||||
query.setSearchAfter(searchAfter);
|
||||
SearchHits<Entity> searchHits = operations.search(query, Entity.class);
|
||||
|
||||
if (searchHits.getSearchHits().size() == 0) {
|
||||
if (searchHits.getSearchHits().isEmpty()) {
|
||||
break;
|
||||
}
|
||||
foundEntities.addAll(searchHits.stream().map(SearchHit::getContent).collect(Collectors.toList()));
|
||||
searchAfter = searchHits.getSearchHit((int) (searchHits.getSearchHits().size() - 1)).getSortValues();
|
||||
foundEntities.addAll(searchHits.stream().map(SearchHit::getContent).toList());
|
||||
searchAfter = searchHits.getSearchHit(searchHits.getSearchHits().size() - 1).getSortValues();
|
||||
|
||||
if (++loop > 10) {
|
||||
fail("loop not terminating");
|
||||
@ -99,16 +99,69 @@ public abstract class SearchAfterIntegrationTests {
|
||||
assertThat(foundEntities).containsExactlyElementsOf(entities);
|
||||
}
|
||||
|
||||
@Test // #2678
|
||||
@DisplayName("should be able to handle different search after type values including null")
|
||||
void shouldBeAbleToHandleDifferentSearchAfterTypeValuesIncludingNull() {
|
||||
|
||||
List<Entity> entities = IntStream.rangeClosed(1, 10)
|
||||
.mapToObj(i -> {
|
||||
var message = (i % 2 == 0) ? null : "message " + i;
|
||||
var value = (i % 3 == 0) ? null : (long) i;
|
||||
return new Entity((long) i, message, value);
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
operations.save(entities);
|
||||
|
||||
Query query = Query.findAll();
|
||||
query.setPageable(PageRequest.of(0, 3));
|
||||
query.addSort(Sort.by(Sort.Direction.ASC, "id"));
|
||||
query.addSort(Sort.by(Sort.Direction.ASC, "keyword"));
|
||||
query.addSort(Sort.by(Sort.Direction.ASC, "value"));
|
||||
|
||||
List<Object> searchAfter = null;
|
||||
List<Entity> foundEntities = new ArrayList<>();
|
||||
|
||||
int loop = 0;
|
||||
do {
|
||||
query.setSearchAfter(searchAfter);
|
||||
SearchHits<Entity> searchHits = operations.search(query, Entity.class);
|
||||
|
||||
if (searchHits.getSearchHits().isEmpty()) {
|
||||
break;
|
||||
}
|
||||
foundEntities.addAll(searchHits.stream().map(SearchHit::getContent).toList());
|
||||
searchAfter = searchHits.getSearchHit(searchHits.getSearchHits().size() - 1).getSortValues();
|
||||
|
||||
if (++loop > 10) {
|
||||
fail("loop not terminating");
|
||||
}
|
||||
} while (true);
|
||||
|
||||
assertThat(foundEntities).containsExactlyElementsOf(entities);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
private static class Entity {
|
||||
@Nullable
|
||||
@Id private Long id;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Text) private String message;
|
||||
@Field(type = FieldType.Keyword) private String keyword;
|
||||
|
||||
public Entity(@Nullable Long id, @Nullable String message) {
|
||||
@Nullable
|
||||
@Field(type = FieldType.Long) private Long value;
|
||||
|
||||
public Entity() {}
|
||||
|
||||
public Entity(@Nullable Long id, @Nullable String keyword) {
|
||||
this.id = id;
|
||||
this.message = message;
|
||||
this.keyword = keyword;
|
||||
}
|
||||
|
||||
public Entity(@Nullable Long id, @Nullable String keyword, @Nullable Long value) {
|
||||
this.id = id;
|
||||
this.keyword = keyword;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@ -121,30 +174,44 @@ public abstract class SearchAfterIntegrationTests {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getMessage() {
|
||||
return message;
|
||||
public String getKeyword() {
|
||||
return keyword;
|
||||
}
|
||||
|
||||
public void setMessage(@Nullable String message) {
|
||||
this.message = message;
|
||||
public void setKeyword(@Nullable String keyword) {
|
||||
this.keyword = keyword;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Long getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(@Nullable Long value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (!(o instanceof Entity entity))
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
|
||||
Entity entity = (Entity) o;
|
||||
|
||||
if (!Objects.equals(id, entity.id))
|
||||
return false;
|
||||
return Objects.equals(message, entity.message);
|
||||
if (!Objects.equals(keyword, entity.keyword))
|
||||
return false;
|
||||
return Objects.equals(value, entity.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = id != null ? id.hashCode() : 0;
|
||||
result = 31 * result + (message != null ? message.hashCode() : 0);
|
||||
result = 31 * result + (keyword != null ? keyword.hashCode() : 0);
|
||||
result = 31 * result + (value != null ? value.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user