Skip to content

Commit 4614c62

Browse files
committed
Fix search_after field values (#2679)
Closes #2678 (cherry picked from commit 9adc4d2)
1 parent 063020f commit 4614c62

File tree

3 files changed

+131
-22
lines changed

3 files changed

+131
-22
lines changed

src/main/java/org/springframework/data/elasticsearch/client/elc/RequestConverter.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import static org.springframework.util.CollectionUtils.*;
2020

2121
import co.elastic.clients.elasticsearch._types.Conflicts;
22-
import co.elastic.clients.elasticsearch._types.FieldValue;
2322
import co.elastic.clients.elasticsearch._types.InlineScript;
2423
import co.elastic.clients.elasticsearch._types.OpType;
2524
import co.elastic.clients.elasticsearch._types.SortOptions;
@@ -63,7 +62,6 @@
6362
import java.util.Arrays;
6463
import java.util.Collections;
6564
import java.util.HashMap;
66-
import java.util.LinkedHashMap;
6765
import java.util.List;
6866
import java.util.Map;
6967
import java.util.function.Function;
@@ -1221,8 +1219,7 @@ public MsearchRequest searchMsearchRequest(
12211219
}
12221220

12231221
if (!isEmpty(query.getSearchAfter())) {
1224-
bb.searchAfter(query.getSearchAfter().stream().map(it -> FieldValue.of(it.toString()))
1225-
.collect(Collectors.toList()));
1222+
bb.searchAfter(query.getSearchAfter().stream().map(TypeUtils::toFieldValue).toList());
12261223
}
12271224

12281225
query.getRescorerQueries().forEach(rescorerQuery -> bb.rescore(getRescore(rescorerQuery)));
@@ -1376,8 +1373,7 @@ private <T> void prepareSearchRequest(Query query, @Nullable String routing, @Nu
13761373
}
13771374

13781375
if (!isEmpty(query.getSearchAfter())) {
1379-
builder.searchAfter(
1380-
query.getSearchAfter().stream().map(it -> FieldValue.of(it.toString())).collect(Collectors.toList()));
1376+
builder.searchAfter(query.getSearchAfter().stream().map(TypeUtils::toFieldValue).toList());
13811377
}
13821378

13831379
query.getRescorerQueries().forEach(rescorerQuery -> builder.rescore(getRescore(rescorerQuery)));

src/main/java/org/springframework/data/elasticsearch/client/elc/TypeUtils.java

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,15 @@
1818
import co.elastic.clients.elasticsearch._types.*;
1919
import co.elastic.clients.elasticsearch._types.mapping.FieldType;
2020
import co.elastic.clients.elasticsearch._types.mapping.TypeMapping;
21-
import co.elastic.clients.elasticsearch.core.search.*;
21+
import co.elastic.clients.elasticsearch.core.search.BoundaryScanner;
22+
import co.elastic.clients.elasticsearch.core.search.HighlighterEncoder;
23+
import co.elastic.clients.elasticsearch.core.search.HighlighterFragmenter;
24+
import co.elastic.clients.elasticsearch.core.search.HighlighterOrder;
25+
import co.elastic.clients.elasticsearch.core.search.HighlighterTagsSchema;
26+
import co.elastic.clients.elasticsearch.core.search.HighlighterType;
27+
import co.elastic.clients.elasticsearch.core.search.ScoreMode;
2228
import co.elastic.clients.elasticsearch.indices.IndexSettings;
29+
import co.elastic.clients.json.JsonData;
2330

2431
import java.io.StringReader;
2532
import java.time.Duration;
@@ -31,8 +38,13 @@
3138
import org.springframework.data.domain.Sort;
3239
import org.springframework.data.elasticsearch.core.RefreshPolicy;
3340
import org.springframework.data.elasticsearch.core.document.Document;
34-
import org.springframework.data.elasticsearch.core.query.*;
41+
import org.springframework.data.elasticsearch.core.query.GeoDistanceOrder;
42+
import org.springframework.data.elasticsearch.core.query.IndexQuery;
3543
import org.springframework.data.elasticsearch.core.query.IndicesOptions;
44+
import org.springframework.data.elasticsearch.core.query.Order;
45+
import org.springframework.data.elasticsearch.core.query.Query;
46+
import org.springframework.data.elasticsearch.core.query.RescorerQuery;
47+
import org.springframework.data.elasticsearch.core.query.UpdateResponse;
3648
import org.springframework.data.elasticsearch.core.reindex.ReindexRequest;
3749
import org.springframework.lang.Nullable;
3850

@@ -156,6 +168,40 @@ static Object toObject(@Nullable FieldValue fieldValue) {
156168
}
157169
}
158170

171+
@Nullable
172+
static FieldValue toFieldValue(@Nullable Object fieldValue) {
173+
174+
if (fieldValue == null) {
175+
return FieldValue.NULL;
176+
}
177+
178+
if (fieldValue instanceof Boolean b) {
179+
return b ? FieldValue.TRUE : FieldValue.FALSE;
180+
}
181+
182+
if (fieldValue instanceof String s) {
183+
return FieldValue.of(s);
184+
}
185+
186+
if (fieldValue instanceof Long l) {
187+
return FieldValue.of(l);
188+
}
189+
190+
if (fieldValue instanceof Integer i) {
191+
return FieldValue.of((long) i);
192+
}
193+
194+
if (fieldValue instanceof Double d) {
195+
return FieldValue.of(d);
196+
}
197+
198+
if (fieldValue instanceof Float f) {
199+
return FieldValue.of((double) f);
200+
}
201+
202+
return FieldValue.of(JsonData.of(fieldValue));
203+
}
204+
159205
@Nullable
160206
static GeoDistanceType geoDistanceType(GeoDistanceOrder.DistanceType distanceType) {
161207

src/test/java/org/springframework/data/elasticsearch/core/paginating/SearchAfterIntegrationTests.java

Lines changed: 81 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public void before() {
6262
@Test
6363
@Order(java.lang.Integer.MAX_VALUE)
6464
void cleanup() {
65-
operations.indexOps(IndexCoordinates.of(indexNameProvider.getPrefix() + "*")).delete();
65+
operations.indexOps(IndexCoordinates.of(indexNameProvider.getPrefix() + '*')).delete();
6666
}
6767

6868
@Test // #1143
@@ -85,11 +85,11 @@ void shouldReadPagesWithSearchAfter() {
8585
query.setSearchAfter(searchAfter);
8686
SearchHits<Entity> searchHits = operations.search(query, Entity.class);
8787

88-
if (searchHits.getSearchHits().size() == 0) {
88+
if (searchHits.getSearchHits().isEmpty()) {
8989
break;
9090
}
91-
foundEntities.addAll(searchHits.stream().map(SearchHit::getContent).collect(Collectors.toList()));
92-
searchAfter = searchHits.getSearchHit((int) (searchHits.getSearchHits().size() - 1)).getSortValues();
91+
foundEntities.addAll(searchHits.stream().map(SearchHit::getContent).toList());
92+
searchAfter = searchHits.getSearchHit(searchHits.getSearchHits().size() - 1).getSortValues();
9393

9494
if (++loop > 10) {
9595
fail("loop not terminating");
@@ -99,16 +99,69 @@ void shouldReadPagesWithSearchAfter() {
9999
assertThat(foundEntities).containsExactlyElementsOf(entities);
100100
}
101101

102+
@Test // #2678
103+
@DisplayName("should be able to handle different search after type values including null")
104+
void shouldBeAbleToHandleDifferentSearchAfterTypeValuesIncludingNull() {
105+
106+
List<Entity> entities = IntStream.rangeClosed(1, 10)
107+
.mapToObj(i -> {
108+
var message = (i % 2 == 0) ? null : "message " + i;
109+
var value = (i % 3 == 0) ? null : (long) i;
110+
return new Entity((long) i, message, value);
111+
})
112+
.collect(Collectors.toList());
113+
operations.save(entities);
114+
115+
Query query = Query.findAll();
116+
query.setPageable(PageRequest.of(0, 3));
117+
query.addSort(Sort.by(Sort.Direction.ASC, "id"));
118+
query.addSort(Sort.by(Sort.Direction.ASC, "keyword"));
119+
query.addSort(Sort.by(Sort.Direction.ASC, "value"));
120+
121+
List<Object> searchAfter = null;
122+
List<Entity> foundEntities = new ArrayList<>();
123+
124+
int loop = 0;
125+
do {
126+
query.setSearchAfter(searchAfter);
127+
SearchHits<Entity> searchHits = operations.search(query, Entity.class);
128+
129+
if (searchHits.getSearchHits().isEmpty()) {
130+
break;
131+
}
132+
foundEntities.addAll(searchHits.stream().map(SearchHit::getContent).toList());
133+
searchAfter = searchHits.getSearchHit(searchHits.getSearchHits().size() - 1).getSortValues();
134+
135+
if (++loop > 10) {
136+
fail("loop not terminating");
137+
}
138+
} while (true);
139+
140+
assertThat(foundEntities).containsExactlyElementsOf(entities);
141+
}
142+
143+
@SuppressWarnings("unused")
102144
@Document(indexName = "#{@indexNameProvider.indexName()}")
103145
private static class Entity {
104146
@Nullable
105147
@Id private Long id;
106148
@Nullable
107-
@Field(type = FieldType.Text) private String message;
149+
@Field(type = FieldType.Keyword) private String keyword;
150+
151+
@Nullable
152+
@Field(type = FieldType.Long) private Long value;
153+
154+
public Entity() {}
108155

109-
public Entity(@Nullable Long id, @Nullable String message) {
156+
public Entity(@Nullable Long id, @Nullable String keyword) {
110157
this.id = id;
111-
this.message = message;
158+
this.keyword = keyword;
159+
}
160+
161+
public Entity(@Nullable Long id, @Nullable String keyword, @Nullable Long value) {
162+
this.id = id;
163+
this.keyword = keyword;
164+
this.value = value;
112165
}
113166

114167
@Nullable
@@ -121,30 +174,44 @@ public void setId(@Nullable Long id) {
121174
}
122175

123176
@Nullable
124-
public String getMessage() {
125-
return message;
177+
public String getKeyword() {
178+
return keyword;
179+
}
180+
181+
public void setKeyword(@Nullable String keyword) {
182+
this.keyword = keyword;
183+
}
184+
185+
@Nullable
186+
public Long getValue() {
187+
return value;
126188
}
127189

128-
public void setMessage(@Nullable String message) {
129-
this.message = message;
190+
public void setValue(@Nullable Long value) {
191+
this.value = value;
130192
}
131193

132194
@Override
133195
public boolean equals(Object o) {
134196
if (this == o)
135197
return true;
136-
if (!(o instanceof Entity entity))
198+
if (o == null || getClass() != o.getClass())
137199
return false;
138200

201+
Entity entity = (Entity) o;
202+
139203
if (!Objects.equals(id, entity.id))
140204
return false;
141-
return Objects.equals(message, entity.message);
205+
if (!Objects.equals(keyword, entity.keyword))
206+
return false;
207+
return Objects.equals(value, entity.value);
142208
}
143209

144210
@Override
145211
public int hashCode() {
146212
int result = id != null ? id.hashCode() : 0;
147-
result = 31 * result + (message != null ? message.hashCode() : 0);
213+
result = 31 * result + (keyword != null ? keyword.hashCode() : 0);
214+
result = 31 * result + (value != null ? value.hashCode() : 0);
148215
return result;
149216
}
150217
}

0 commit comments

Comments
 (0)