mirror of
https://github.com/spring-projects/spring-data-elasticsearch.git
synced 2025-06-29 07:12:26 +00:00
Enable use of search_after with field_collapse.
Original Pull Request #2937 Closes #2935
This commit is contained in:
parent
8d0ecf2aa3
commit
dd156b9e29
@ -395,7 +395,28 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
|||||||
Function<PitSearchAfter, Publisher<? extends ResponseBody<EntityAsMap>>> resourceClosure = psa -> {
|
Function<PitSearchAfter, Publisher<? extends ResponseBody<EntityAsMap>>> resourceClosure = psa -> {
|
||||||
|
|
||||||
baseQuery.setPointInTime(new Query.PointInTime(psa.getPit(), pitKeepAlive));
|
baseQuery.setPointInTime(new Query.PointInTime(psa.getPit(), pitKeepAlive));
|
||||||
baseQuery.addSort(Sort.by("_shard_doc"));
|
|
||||||
|
// only add _shard_doc if there is not a field_collapse and a sort with the same name
|
||||||
|
boolean addShardDoc = true;
|
||||||
|
|
||||||
|
if (query instanceof NativeQuery nativeQuery && nativeQuery.getFieldCollapse() != null) {
|
||||||
|
var field = nativeQuery.getFieldCollapse().field();
|
||||||
|
|
||||||
|
if (nativeQuery.getSortOptions().stream()
|
||||||
|
.anyMatch(sortOptions -> sortOptions.isField() && sortOptions.field().field().equals(field))) {
|
||||||
|
addShardDoc = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query.getSort() != null
|
||||||
|
&& query.getSort().stream().anyMatch(order -> order.getProperty().equals(field))) {
|
||||||
|
addShardDoc = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addShardDoc) {
|
||||||
|
baseQuery.addSort(Sort.by("_shard_doc"));
|
||||||
|
}
|
||||||
|
|
||||||
SearchRequest firstSearchRequest = requestConverter.searchRequest(baseQuery, routingResolver.getRouting(),
|
SearchRequest firstSearchRequest = requestConverter.searchRequest(baseQuery, routingResolver.getRouting(),
|
||||||
clazz, index, false, true);
|
clazz, index, false, true);
|
||||||
|
|
||||||
|
@ -1487,8 +1487,8 @@ class RequestConverter extends AbstractQueryProcessor {
|
|||||||
if (query instanceof NativeQuery nativeQuery) {
|
if (query instanceof NativeQuery nativeQuery) {
|
||||||
prepareNativeSearch(nativeQuery, builder);
|
prepareNativeSearch(nativeQuery, builder);
|
||||||
}
|
}
|
||||||
// query.getSort() must be checked after prepareNativeSearch as this already might hav a sort set that must have
|
// query.getSort() must be checked after prepareNativeSearch as this already might have a sort set
|
||||||
// higher priority
|
// that must have higher priority
|
||||||
if (query.getSort() != null) {
|
if (query.getSort() != null) {
|
||||||
List<SortOptions> sortOptions = getSortOptions(query.getSort(), persistentEntity);
|
List<SortOptions> sortOptions = getSortOptions(query.getSort(), persistentEntity);
|
||||||
|
|
||||||
@ -1510,7 +1510,15 @@ class RequestConverter extends AbstractQueryProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!isEmpty(query.getSearchAfter())) {
|
if (!isEmpty(query.getSearchAfter())) {
|
||||||
builder.searchAfter(query.getSearchAfter().stream().map(TypeUtils::toFieldValue).toList());
|
var fieldValues = query.getSearchAfter().stream().map(TypeUtils::toFieldValue).toList();
|
||||||
|
|
||||||
|
// when there is a field collapse on a native query, and we have a search_after, then the search_after
|
||||||
|
// must only have one entry
|
||||||
|
if (query instanceof NativeQuery nativeQuery && nativeQuery.getFieldCollapse() != null) {
|
||||||
|
builder.searchAfter(fieldValues.get(0));
|
||||||
|
} else {
|
||||||
|
builder.searchAfter(fieldValues);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
query.getRescorerQueries().forEach(rescorerQuery -> builder.rescore(getRescore(rescorerQuery)));
|
query.getRescorerQueries().forEach(rescorerQuery -> builder.rescore(getRescore(rescorerQuery)));
|
||||||
|
@ -15,14 +15,22 @@
|
|||||||
*/
|
*/
|
||||||
package org.springframework.data.elasticsearch.repository.support;
|
package org.springframework.data.elasticsearch.repository.support;
|
||||||
|
|
||||||
|
import co.elastic.clients.elasticsearch.core.search.FieldCollapse;
|
||||||
|
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.domain.Pageable;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
|
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
|
||||||
|
import org.springframework.data.elasticsearch.client.elc.Queries;
|
||||||
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchTemplateConfiguration;
|
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchTemplateConfiguration;
|
||||||
import org.springframework.data.elasticsearch.repositories.custommethod.QueryParameter;
|
import org.springframework.data.elasticsearch.repositories.custommethod.QueryParameter;
|
||||||
import org.springframework.data.elasticsearch.repository.config.EnableReactiveElasticsearchRepositories;
|
import org.springframework.data.elasticsearch.repository.config.EnableReactiveElasticsearchRepositories;
|
||||||
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
|
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
|
||||||
import org.springframework.test.context.ContextConfiguration;
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
|
import reactor.test.StepVerifier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Peter-Josef Meisch
|
* @author Peter-Josef Meisch
|
||||||
@ -51,4 +59,33 @@ public class SimpleReactiveElasticsearchRepositoryELCIntegrationTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* search_after is used by the reactive search operation, it normally always adds _shard_doc as a tiebreaker sort
|
||||||
|
* parameter. This must not be done when a collapse field is used as sort field, as in that case the collapse field
|
||||||
|
* must be the only sort field.
|
||||||
|
*/
|
||||||
|
@Test // #2935
|
||||||
|
@DisplayName("should use collapse_field for search_after in pit search")
|
||||||
|
void shouldUseCollapseFieldForSearchAfterI() {
|
||||||
|
var entity = new SampleEntity();
|
||||||
|
entity.setId("42");
|
||||||
|
entity.setMessage("m");
|
||||||
|
entity.setKeyword("kw");
|
||||||
|
repository.save(entity).block();
|
||||||
|
|
||||||
|
var query = NativeQuery.builder()
|
||||||
|
.withQuery(Queries.matchAllQueryAsQuery())
|
||||||
|
.withPageable(Pageable.unpaged())
|
||||||
|
.withFieldCollapse(FieldCollapse.of(fcb -> fcb
|
||||||
|
.field("keyword")))
|
||||||
|
.withSort(Sort.by("keyword"))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
operations.search(query, SampleEntity.class)
|
||||||
|
.as(StepVerifier::create)
|
||||||
|
.expectNextCount(1)
|
||||||
|
.verifyComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user