Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 89c20c3b21 | |||
| 6fdd786b03 | |||
| c739e023b6 | |||
| c7a8c0bf60 | |||
| 57b4af14c2 | |||
| 5175f34418 | |||
| f5f73dc67a | |||
| 6700580755 | |||
| 7ff25485de | |||
| 394e56d3a9 | |||
| 474e433926 | |||
| 7e64d85efc | |||
| ea51ce062e | |||
| 4767a8215e | |||
| 6c4fc59a11 | |||
| 0e8401d3b1 | |||
| 2c20671379 | |||
| 65b0709b73 | |||
| 47110a212a | |||
| ed59fc85ca | |||
| 46fdc8a84b | |||
| 394fb7a831 | |||
| c23da6ed5e | |||
| cc56e27633 | |||
| ce64d2d6d1 | |||
| e02599d177 | |||
| 8558c44714 | |||
| 88e2e9da2a | |||
| c15c1d08d7 | |||
| c653e6a4ec |
@@ -33,3 +33,4 @@ node
|
||||
package-lock.json
|
||||
|
||||
.mvn/.develocity
|
||||
/src/test/resources/testcontainers-local.properties
|
||||
|
||||
@@ -8,3 +8,7 @@
|
||||
--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
|
||||
--add-opens jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
|
||||
--add-opens jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED
|
||||
--add-opens=java.base/java.util=ALL-UNNAMED
|
||||
--add-opens=java.base/java.lang.reflect=ALL-UNNAMED
|
||||
--add-opens=java.base/java.text=ALL-UNNAMED
|
||||
--add-opens=java.desktop/java.awt.font=ALL-UNNAMED
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
#Thu Nov 07 09:47:28 CET 2024
|
||||
#Thu Jul 17 14:04:44 CEST 2025
|
||||
wrapperUrl=https\://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
|
||||
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
|
||||
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Java versions
|
||||
java.main.tag=17.0.13_11-jdk-focal
|
||||
java.next.tag=23.0.1_11-jdk-noble
|
||||
java.main.tag=17.0.15_6-jdk-focal
|
||||
java.next.tag=24.0.1_9-jdk-noble
|
||||
|
||||
# Docker container images - standard
|
||||
docker.java.main.image=library/eclipse-temurin:${java.main.tag}
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-elasticsearch</artifactId>
|
||||
<version>5.4.3</version>
|
||||
<version>5.4.8</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.build</groupId>
|
||||
<artifactId>spring-data-parent</artifactId>
|
||||
<version>3.4.3</version>
|
||||
<version>3.4.8</version>
|
||||
</parent>
|
||||
|
||||
<name>Spring Data Elasticsearch</name>
|
||||
@@ -18,7 +18,7 @@
|
||||
<url>https://github.com/spring-projects/spring-data-elasticsearch</url>
|
||||
|
||||
<properties>
|
||||
<springdata.commons>3.4.3</springdata.commons>
|
||||
<springdata.commons>3.4.8</springdata.commons>
|
||||
|
||||
<!-- version of the ElasticsearchClient -->
|
||||
<elasticsearch-java>8.15.5</elasticsearch-java>
|
||||
|
||||
+4
-23
@@ -117,9 +117,6 @@ class RequestConverter extends AbstractQueryProcessor {
|
||||
|
||||
private static final Log LOGGER = LogFactory.getLog(RequestConverter.class);
|
||||
|
||||
// the default max result window size of Elasticsearch
|
||||
public static final Integer INDEX_MAX_RESULT_WINDOW = 10_000;
|
||||
|
||||
protected final JsonpMapper jsonpMapper;
|
||||
protected final ElasticsearchConverter elasticsearchConverter;
|
||||
|
||||
@@ -1287,11 +1284,8 @@ class RequestConverter extends AbstractQueryProcessor {
|
||||
.timeout(timeStringMs(query.getTimeout())) //
|
||||
;
|
||||
|
||||
if (query.getPageable().isPaged()) {
|
||||
bb //
|
||||
.from((int) query.getPageable().getOffset()) //
|
||||
.size(query.getPageable().getPageSize());
|
||||
}
|
||||
bb.from((int) (query.getPageable().isPaged() ? query.getPageable().getOffset() : 0))
|
||||
.size(query.getRequestSize());
|
||||
|
||||
if (!isEmpty(query.getFields())) {
|
||||
bb.fields(fb -> {
|
||||
@@ -1304,10 +1298,6 @@ class RequestConverter extends AbstractQueryProcessor {
|
||||
bb.storedFields(query.getStoredFields());
|
||||
}
|
||||
|
||||
if (query.isLimiting()) {
|
||||
bb.size(query.getMaxResults());
|
||||
}
|
||||
|
||||
if (query.getMinScore() > 0) {
|
||||
bb.minScore((double) query.getMinScore());
|
||||
}
|
||||
@@ -1460,13 +1450,8 @@ class RequestConverter extends AbstractQueryProcessor {
|
||||
builder.seqNoPrimaryTerm(true);
|
||||
}
|
||||
|
||||
if (query.getPageable().isPaged()) {
|
||||
builder //
|
||||
.from((int) query.getPageable().getOffset()) //
|
||||
.size(query.getPageable().getPageSize());
|
||||
} else {
|
||||
builder.from(0).size(INDEX_MAX_RESULT_WINDOW);
|
||||
}
|
||||
builder.from((int) (query.getPageable().isPaged() ? query.getPageable().getOffset() : 0))
|
||||
.size(query.getRequestSize());
|
||||
|
||||
if (!isEmpty(query.getFields())) {
|
||||
var fieldAndFormats = query.getFields().stream().map(field -> FieldAndFormat.of(b -> b.field(field))).toList();
|
||||
@@ -1481,10 +1466,6 @@ class RequestConverter extends AbstractQueryProcessor {
|
||||
addIndicesOptions(builder, query.getIndicesOptions());
|
||||
}
|
||||
|
||||
if (query.isLimiting()) {
|
||||
builder.size(query.getMaxResults());
|
||||
}
|
||||
|
||||
if (query.getMinScore() > 0) {
|
||||
builder.minScore((double) query.getMinScore());
|
||||
}
|
||||
|
||||
@@ -500,6 +500,10 @@ class ResponseConverter {
|
||||
builder.withDeleted(response.deleted());
|
||||
}
|
||||
|
||||
if(response.updated() != null) {
|
||||
builder.withUpdated(response.updated());
|
||||
}
|
||||
|
||||
if (response.batches() != null) {
|
||||
builder.withBatches(Math.toIntExact(response.batches()));
|
||||
}
|
||||
|
||||
+6
@@ -233,6 +233,7 @@ abstract public class AbstractReactiveElasticsearchTemplate
|
||||
.subscribe(new Subscriber<>() {
|
||||
@Nullable private Subscription subscription = null;
|
||||
private final AtomicBoolean upstreamComplete = new AtomicBoolean(false);
|
||||
private final AtomicBoolean onNextHasBeenCalled = new AtomicBoolean(false);
|
||||
|
||||
@Override
|
||||
public void onSubscribe(Subscription subscription) {
|
||||
@@ -242,6 +243,7 @@ abstract public class AbstractReactiveElasticsearchTemplate
|
||||
|
||||
@Override
|
||||
public void onNext(List<T> entityList) {
|
||||
onNextHasBeenCalled.set(true);
|
||||
saveAll(entityList, index)
|
||||
.map(sink::tryEmitNext)
|
||||
.doOnComplete(() -> {
|
||||
@@ -267,6 +269,10 @@ abstract public class AbstractReactiveElasticsearchTemplate
|
||||
@Override
|
||||
public void onComplete() {
|
||||
upstreamComplete.set(true);
|
||||
if (!onNextHasBeenCalled.get()) {
|
||||
// this happens when an empty flux is saved
|
||||
sink.tryEmitComplete();
|
||||
}
|
||||
}
|
||||
});
|
||||
return sink.asFlux();
|
||||
|
||||
+120
-87
@@ -38,6 +38,7 @@ import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.EnvironmentCapable;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.data.convert.CustomConversions;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.elasticsearch.annotations.FieldType;
|
||||
import org.springframework.data.elasticsearch.annotations.ScriptedField;
|
||||
import org.springframework.data.elasticsearch.core.document.Document;
|
||||
@@ -50,6 +51,7 @@ import org.springframework.data.elasticsearch.core.query.Criteria;
|
||||
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
|
||||
import org.springframework.data.elasticsearch.core.query.Field;
|
||||
import org.springframework.data.elasticsearch.core.query.Order;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm;
|
||||
import org.springframework.data.elasticsearch.core.query.SourceFilter;
|
||||
@@ -1231,7 +1233,7 @@ public class MappingElasticsearchConverter
|
||||
return;
|
||||
}
|
||||
|
||||
updatePropertiesInFieldsAndSourceFilter(query, domainClass);
|
||||
updatePropertiesInFieldsSortAndSourceFilter(query, domainClass);
|
||||
|
||||
if (query instanceof CriteriaQuery criteriaQuery) {
|
||||
updatePropertiesInCriteriaQuery(criteriaQuery, domainClass);
|
||||
@@ -1242,7 +1244,14 @@ public class MappingElasticsearchConverter
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePropertiesInFieldsAndSourceFilter(Query query, Class<?> domainClass) {
|
||||
/**
|
||||
* replaces the names of fields in the query, the sort or soucre filters with the field names used in Elasticsearch
|
||||
* when they are defined on the ElasticsearchProperties
|
||||
*
|
||||
* @param query the query to process
|
||||
* @param domainClass the domain class (persistent entity)
|
||||
*/
|
||||
private void updatePropertiesInFieldsSortAndSourceFilter(Query query, Class<?> domainClass) {
|
||||
|
||||
ElasticsearchPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(domainClass);
|
||||
|
||||
@@ -1250,12 +1259,12 @@ public class MappingElasticsearchConverter
|
||||
List<String> fields = query.getFields();
|
||||
|
||||
if (!fields.isEmpty()) {
|
||||
query.setFields(updateFieldNames(fields, persistentEntity));
|
||||
query.setFields(propertyToFieldNames(fields, persistentEntity));
|
||||
}
|
||||
|
||||
List<String> storedFields = query.getStoredFields();
|
||||
if (!CollectionUtils.isEmpty(storedFields)) {
|
||||
query.setStoredFields(updateFieldNames(storedFields, persistentEntity));
|
||||
query.setStoredFields(propertyToFieldNames(storedFields, persistentEntity));
|
||||
}
|
||||
|
||||
SourceFilter sourceFilter = query.getSourceFilter();
|
||||
@@ -1266,37 +1275,60 @@ public class MappingElasticsearchConverter
|
||||
String[] excludes = null;
|
||||
|
||||
if (sourceFilter.getIncludes() != null) {
|
||||
includes = updateFieldNames(Arrays.asList(sourceFilter.getIncludes()), persistentEntity)
|
||||
includes = propertyToFieldNames(Arrays.asList(sourceFilter.getIncludes()), persistentEntity)
|
||||
.toArray(new String[] {});
|
||||
}
|
||||
|
||||
if (sourceFilter.getExcludes() != null) {
|
||||
excludes = updateFieldNames(Arrays.asList(sourceFilter.getExcludes()), persistentEntity)
|
||||
excludes = propertyToFieldNames(Arrays.asList(sourceFilter.getExcludes()), persistentEntity)
|
||||
.toArray(new String[] {});
|
||||
}
|
||||
|
||||
query.addSourceFilter(new FetchSourceFilter(includes, excludes));
|
||||
}
|
||||
|
||||
if (query.getSort() != null) {
|
||||
var sort = query.getSort();
|
||||
// stream the orders and map them to a new order with the changed names,
|
||||
// then replace the existing sort with a new sort containing the new orders.
|
||||
var newOrders = sort.stream().map(order -> {
|
||||
var fieldNames = updateFieldNames(order.getProperty(), persistentEntity);
|
||||
|
||||
if (order instanceof Order springDataElasticsearchOrder) {
|
||||
return springDataElasticsearchOrder.withProperty(fieldNames);
|
||||
} else {
|
||||
return new Sort.Order(order.getDirection(),
|
||||
fieldNames,
|
||||
order.isIgnoreCase(),
|
||||
order.getNullHandling());
|
||||
}
|
||||
}).toList();
|
||||
|
||||
if (query instanceof BaseQuery baseQuery) {
|
||||
baseQuery.setSort(Sort.by(newOrders));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* relaces the fieldName with the property name of a property of the persistentEntity with the corresponding
|
||||
* fieldname. If no such property exists, the original fieldName is kept.
|
||||
* replaces property name of a property of the persistentEntity with the corresponding fieldname. If no such property
|
||||
* exists, the original fieldName is kept.
|
||||
*
|
||||
* @param fieldNames list of fieldnames
|
||||
* @param propertyNames list of fieldnames
|
||||
* @param persistentEntity the persistent entity to check
|
||||
* @return an updated list of field names
|
||||
*/
|
||||
private List<String> updateFieldNames(List<String> fieldNames, ElasticsearchPersistentEntity<?> persistentEntity) {
|
||||
return fieldNames.stream().map(fieldName -> updateFieldName(persistentEntity, fieldName))
|
||||
private List<String> propertyToFieldNames(List<String> propertyNames,
|
||||
ElasticsearchPersistentEntity<?> persistentEntity) {
|
||||
return propertyNames.stream().map(propertyName -> propertyToFieldName(persistentEntity, propertyName))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String updateFieldName(ElasticsearchPersistentEntity<?> persistentEntity, String fieldName) {
|
||||
ElasticsearchPersistentProperty persistentProperty = persistentEntity.getPersistentProperty(fieldName);
|
||||
return persistentProperty != null ? persistentProperty.getFieldName() : fieldName;
|
||||
private String propertyToFieldName(ElasticsearchPersistentEntity<?> persistentEntity, String propertyName) {
|
||||
ElasticsearchPersistentProperty persistentProperty = persistentEntity.getPersistentProperty(propertyName);
|
||||
return persistentProperty != null ? persistentProperty.getFieldName() : propertyName;
|
||||
}
|
||||
|
||||
private void updatePropertiesInCriteriaQuery(CriteriaQuery criteriaQuery, Class<?> domainClass) {
|
||||
@@ -1326,15 +1358,85 @@ public class MappingElasticsearchConverter
|
||||
return;
|
||||
}
|
||||
|
||||
String[] fieldNames = field.getName().split("\\.");
|
||||
var propertyNamesUpdate = updatePropertyNames(persistentEntity, field.getName());
|
||||
|
||||
var fieldNames = propertyNamesUpdate.names();
|
||||
field.setName(String.join(".", fieldNames));
|
||||
|
||||
if (propertyNamesUpdate.propertyCount() > 1 && propertyNamesUpdate.nestedProperty()) {
|
||||
List<String> propertyNames = Arrays.asList(fieldNames);
|
||||
field.setPath(String.join(".", propertyNames.subList(0, propertyNamesUpdate.propertyCount - 1)));
|
||||
}
|
||||
|
||||
if (propertyNamesUpdate.persistentProperty != null) {
|
||||
|
||||
if (propertyNamesUpdate.persistentProperty.hasPropertyValueConverter()) {
|
||||
PropertyValueConverter propertyValueConverter = Objects
|
||||
.requireNonNull(propertyNamesUpdate.persistentProperty.getPropertyValueConverter());
|
||||
criteria.getQueryCriteriaEntries().forEach(criteriaEntry -> {
|
||||
|
||||
if (criteriaEntry.getKey().hasValue()) {
|
||||
Object value = criteriaEntry.getValue();
|
||||
|
||||
if (value.getClass().isArray()) {
|
||||
Object[] objects = (Object[]) value;
|
||||
|
||||
for (int i = 0; i < objects.length; i++) {
|
||||
objects[i] = propertyValueConverter.write(objects[i]);
|
||||
}
|
||||
} else {
|
||||
criteriaEntry.setValue(propertyValueConverter.write(value));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
org.springframework.data.elasticsearch.annotations.Field fieldAnnotation = propertyNamesUpdate.persistentProperty
|
||||
.findAnnotation(org.springframework.data.elasticsearch.annotations.Field.class);
|
||||
|
||||
if (fieldAnnotation != null) {
|
||||
field.setFieldType(fieldAnnotation.type());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static record PropertyNamesUpdate(
|
||||
String[] names,
|
||||
Boolean nestedProperty,
|
||||
Integer propertyCount,
|
||||
ElasticsearchPersistentProperty persistentProperty) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String updateFieldNames(String propertyPath, ElasticsearchPersistentEntity<?> persistentEntity) {
|
||||
|
||||
Assert.notNull(propertyPath, "propertyPath must not be null");
|
||||
Assert.notNull(persistentEntity, "persistentEntity must not be null");
|
||||
|
||||
var propertyNamesUpdate = updatePropertyNames(persistentEntity, propertyPath);
|
||||
return String.join(".", propertyNamesUpdate.names());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a propertyPath and replace the path values with the field names from a persistentEntity. path entries not
|
||||
* found in the entity are kept as they are.
|
||||
*
|
||||
* @return the eventually modified names, a flag if a nested entity was encountered the number of processed
|
||||
* propertiesand the last processed PersistentProperty.
|
||||
*/
|
||||
PropertyNamesUpdate updatePropertyNames(ElasticsearchPersistentEntity<?> persistentEntity, String propertyPath) {
|
||||
|
||||
String[] propertyNames = propertyPath.split("\\.");
|
||||
String[] fieldNames = Arrays.copyOf(propertyNames, propertyNames.length);
|
||||
|
||||
ElasticsearchPersistentEntity<?> currentEntity = persistentEntity;
|
||||
ElasticsearchPersistentProperty persistentProperty = null;
|
||||
|
||||
int propertyCount = 0;
|
||||
boolean isNested = false;
|
||||
|
||||
for (int i = 0; i < fieldNames.length; i++) {
|
||||
persistentProperty = currentEntity.getPersistentProperty(fieldNames[i]);
|
||||
for (int i = 0; i < propertyNames.length; i++) {
|
||||
persistentProperty = currentEntity.getPersistentProperty(propertyNames[i]);
|
||||
|
||||
if (persistentProperty != null) {
|
||||
propertyCount++;
|
||||
@@ -1361,77 +1463,8 @@ public class MappingElasticsearchConverter
|
||||
}
|
||||
}
|
||||
|
||||
field.setName(String.join(".", fieldNames));
|
||||
|
||||
if (propertyCount > 1 && isNested) {
|
||||
List<String> propertyNames = Arrays.asList(fieldNames);
|
||||
field.setPath(String.join(".", propertyNames.subList(0, propertyCount - 1)));
|
||||
}
|
||||
|
||||
if (persistentProperty != null) {
|
||||
|
||||
if (persistentProperty.hasPropertyValueConverter()) {
|
||||
PropertyValueConverter propertyValueConverter = Objects
|
||||
.requireNonNull(persistentProperty.getPropertyValueConverter());
|
||||
criteria.getQueryCriteriaEntries().forEach(criteriaEntry -> {
|
||||
|
||||
if (criteriaEntry.getKey().hasValue()) {
|
||||
Object value = criteriaEntry.getValue();
|
||||
|
||||
if (value.getClass().isArray()) {
|
||||
Object[] objects = (Object[]) value;
|
||||
|
||||
for (int i = 0; i < objects.length; i++) {
|
||||
objects[i] = propertyValueConverter.write(objects[i]);
|
||||
}
|
||||
} else {
|
||||
criteriaEntry.setValue(propertyValueConverter.write(value));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
org.springframework.data.elasticsearch.annotations.Field fieldAnnotation = persistentProperty
|
||||
.findAnnotation(org.springframework.data.elasticsearch.annotations.Field.class);
|
||||
|
||||
if (fieldAnnotation != null) {
|
||||
field.setFieldType(fieldAnnotation.type());
|
||||
}
|
||||
}
|
||||
return new PropertyNamesUpdate(fieldNames, isNested, propertyCount, persistentProperty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String updateFieldNames(String propertyPath, ElasticsearchPersistentEntity<?> persistentEntity) {
|
||||
|
||||
Assert.notNull(propertyPath, "propertyPath must not be null");
|
||||
Assert.notNull(persistentEntity, "persistentEntity must not be null");
|
||||
|
||||
var properties = propertyPath.split("\\.", 2);
|
||||
|
||||
if (properties.length > 0) {
|
||||
var propertyName = properties[0];
|
||||
var fieldName = updateFieldName(persistentEntity, propertyName);
|
||||
|
||||
if (properties.length > 1) {
|
||||
var persistentProperty = persistentEntity.getPersistentProperty(propertyName);
|
||||
|
||||
if (persistentProperty != null) {
|
||||
ElasticsearchPersistentEntity<?> nestedPersistentEntity = mappingContext
|
||||
.getPersistentEntity(persistentProperty);
|
||||
if (nestedPersistentEntity != null) {
|
||||
return fieldName + '.' + updateFieldNames(properties[1], nestedPersistentEntity);
|
||||
} else {
|
||||
return fieldName;
|
||||
}
|
||||
}
|
||||
}
|
||||
return fieldName;
|
||||
} else {
|
||||
return propertyPath;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
@SuppressWarnings("ClassCanBeRecord")
|
||||
|
||||
@@ -27,6 +27,7 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -47,10 +48,15 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class BaseQuery implements Query {
|
||||
|
||||
public static final int INDEX_MAX_RESULT_WINDOW = 10_000;
|
||||
|
||||
private static final int DEFAULT_REACTIVE_BATCH_SIZE = 500;
|
||||
// the instance to mark the query pageable initial status, needed to distinguish between the initial
|
||||
// value and a user-set unpaged value; values don't matter, the RequestConverter compares to the isntance.
|
||||
private static final Pageable UNSET_PAGE = PageRequest.of(0, 1);
|
||||
|
||||
@Nullable protected Sort sort;
|
||||
protected Pageable pageable = DEFAULT_PAGE;
|
||||
protected Pageable pageable = UNSET_PAGE;
|
||||
protected List<String> fields = new ArrayList<>();
|
||||
@Nullable protected List<String> storedFields;
|
||||
@Nullable protected SourceFilter sourceFilter;
|
||||
@@ -78,7 +84,7 @@ public class BaseQuery implements Query {
|
||||
private boolean queryIsUpdatedByConverter = false;
|
||||
@Nullable private Integer reactiveBatchSize = null;
|
||||
@Nullable private Boolean allowNoIndices = null;
|
||||
private EnumSet<IndicesOptions.WildcardStates> expandWildcards;
|
||||
private EnumSet<IndicesOptions.WildcardStates> expandWildcards = EnumSet.noneOf(IndicesOptions.WildcardStates.class);
|
||||
private List<DocValueField> docValueFields = new ArrayList<>();
|
||||
private List<ScriptedField> scriptedFields = new ArrayList<>();
|
||||
|
||||
@@ -87,7 +93,7 @@ public class BaseQuery implements Query {
|
||||
public <Q extends BaseQuery, B extends BaseQueryBuilder<Q, B>> BaseQuery(BaseQueryBuilder<Q, B> builder) {
|
||||
this.sort = builder.getSort();
|
||||
// do a setPageable after setting the sort, because the pageable may contain an additional sort
|
||||
this.setPageable(builder.getPageable() != null ? builder.getPageable() : DEFAULT_PAGE);
|
||||
this.setPageable(builder.getPageable() != null ? builder.getPageable() : UNSET_PAGE);
|
||||
this.fields = builder.getFields();
|
||||
this.storedFields = builder.getStoredFields();
|
||||
this.sourceFilter = builder.getSourceFilter();
|
||||
@@ -203,7 +209,7 @@ public class BaseQuery implements Query {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public final <T extends Query> T addSort(@Nullable Sort sort) {
|
||||
if (sort == null) {
|
||||
if (sort == null || sort.isUnsorted()) {
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
@@ -561,4 +567,52 @@ public class BaseQuery implements Query {
|
||||
public List<ScriptedField> getScriptedFields() {
|
||||
return scriptedFields;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getRequestSize() {
|
||||
|
||||
var pageable = getPageable();
|
||||
Integer requestSize = null;
|
||||
|
||||
if (pageable.isPaged() && pageable != UNSET_PAGE) {
|
||||
// pagesize defined by the user
|
||||
if (!isLimiting()) {
|
||||
// no maxResults
|
||||
requestSize = pageable.getPageSize();
|
||||
} else {
|
||||
// if we have both a page size and a max results, we take the min, this is necessary for
|
||||
// searchForStream to work correctly (#3098) as there the page size defines what is
|
||||
// returned in a single request, and the max result determines the total number of
|
||||
// documents returned.
|
||||
requestSize = Math.min(pageable.getPageSize(), getMaxResults());
|
||||
}
|
||||
} else if (pageable == UNSET_PAGE) {
|
||||
// no user defined pageable
|
||||
if (isLimiting()) {
|
||||
// maxResults
|
||||
requestSize = getMaxResults();
|
||||
} else {
|
||||
requestSize = DEFAULT_PAGE_SIZE;
|
||||
}
|
||||
} else {
|
||||
// explicitly set unpaged
|
||||
if (!isLimiting()) {
|
||||
// no maxResults
|
||||
requestSize = INDEX_MAX_RESULT_WINDOW;
|
||||
} else {
|
||||
// if we have both a implicit page size and a max results, we take the min, this is necessary for
|
||||
// searchForStream to work correctly (#3098) as there the page size defines what is
|
||||
// returned in a single request, and the max result determines the total number of
|
||||
// documents returned.
|
||||
requestSize = Math.min(INDEX_MAX_RESULT_WINDOW, getMaxResults());
|
||||
}
|
||||
}
|
||||
|
||||
if (requestSize == null) {
|
||||
// this should not happen
|
||||
requestSize = DEFAULT_PAGE_SIZE;
|
||||
}
|
||||
|
||||
return requestSize;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.elasticsearch.core.geo.GeoBox;
|
||||
@@ -859,6 +860,7 @@ public class Criteria {
|
||||
|
||||
// endregion
|
||||
|
||||
// region equals/hashcode
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
@@ -874,6 +876,8 @@ public class Criteria {
|
||||
return false;
|
||||
if (!Objects.equals(field, criteria.field))
|
||||
return false;
|
||||
if (!criteriaChain.filter(this).equals(criteria.criteriaChain.filter(criteria)))
|
||||
return false;
|
||||
if (!queryCriteriaEntries.equals(criteria.queryCriteriaEntries))
|
||||
return false;
|
||||
if (!filterCriteriaEntries.equals(criteria.filterCriteriaEntries))
|
||||
@@ -886,11 +890,16 @@ public class Criteria {
|
||||
int result = field != null ? field.hashCode() : 0;
|
||||
result = 31 * result + (boost != +0.0f ? Float.floatToIntBits(boost) : 0);
|
||||
result = 31 * result + (negating ? 1 : 0);
|
||||
// the criteriaChain contains "this" object, so we need to filter it out
|
||||
// to avoid a stackoverflow here, because the hashcode implementation
|
||||
// uses the element's hashcodes
|
||||
result = 31 * result + criteriaChain.filter(this).hashCode();
|
||||
result = 31 * result + queryCriteriaEntries.hashCode();
|
||||
result = 31 * result + filterCriteriaEntries.hashCode();
|
||||
result = 31 * result + subCriteria.hashCode();
|
||||
return result;
|
||||
}
|
||||
// endregion
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
@@ -938,7 +947,17 @@ public class Criteria {
|
||||
*
|
||||
* @since 4.1
|
||||
*/
|
||||
public static class CriteriaChain extends LinkedList<Criteria> {}
|
||||
public static class CriteriaChain extends LinkedList<Criteria> {
|
||||
/**
|
||||
* return a copy of this list with the given element filtered out.
|
||||
*
|
||||
* @param criteria the element to filter
|
||||
* @return the filtered list
|
||||
*/
|
||||
List<Criteria> filter(Criteria criteria) {
|
||||
return this.stream().filter(c -> c != criteria).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Operator to join the entries of the criteria chain
|
||||
|
||||
@@ -484,6 +484,13 @@ public interface Query {
|
||||
*/
|
||||
List<ScriptedField> getScriptedFields();
|
||||
|
||||
/**
|
||||
* @return the number of documents that should be requested from Elasticsearch in this query. Depends wether a
|
||||
* Pageable and/or maxResult size is set on the query.
|
||||
* @since 5.4.8 5.5.2
|
||||
*/
|
||||
public Integer getRequestSize();
|
||||
|
||||
/**
|
||||
* @since 4.3
|
||||
*/
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Spring Data Elasticsearch 5.4.3 (2024.1.3)
|
||||
Spring Data Elasticsearch 5.4.8 (2024.1.8)
|
||||
Copyright (c) [2013-2022] Pivotal Software, Inc.
|
||||
|
||||
This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
||||
@@ -23,6 +23,11 @@ conditions of the subcomponent's license, as noted in the LICENSE file.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+104
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.client.elc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.skyscreamer.jsonassert.JSONAssert.*;
|
||||
import static org.springframework.data.elasticsearch.client.elc.JsonUtils.*;
|
||||
|
||||
@@ -22,6 +23,7 @@ import co.elastic.clients.json.JsonpMapper;
|
||||
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
@@ -445,6 +447,108 @@ public class CriteriaQueryMappingUnitTests {
|
||||
softly.assertAll();
|
||||
}
|
||||
|
||||
// the following test failed because of a wrong implementation in Criteria
|
||||
// equals and hscode methods.
|
||||
@Test // #3083
|
||||
@DisplayName("should map correct subcriteria")
|
||||
void shouldMapCorrectSubcriteria() throws JSONException {
|
||||
Criteria criteria = new Criteria("first").is("hello");
|
||||
|
||||
List<Criteria> criterias = new ArrayList<>();
|
||||
criterias.add(new Criteria().or("second").exists());
|
||||
|
||||
List<Criteria> subCriterias = new ArrayList<>();
|
||||
subCriterias.add(new Criteria("third").exists()
|
||||
.and(new Criteria("fourth").is("ciao")));
|
||||
subCriterias.add(new Criteria("third").exists()
|
||||
.and(new Criteria("fourth").is("hi")));
|
||||
|
||||
Criteria result = Criteria.or();
|
||||
|
||||
for (Criteria c : criterias) {
|
||||
result = result.or(c);
|
||||
}
|
||||
|
||||
for (Criteria c : subCriterias) {
|
||||
result = result.subCriteria(c);
|
||||
}
|
||||
criteria = criteria.subCriteria(result);
|
||||
CriteriaQuery criteriaQuery = new CriteriaQuery(criteria);
|
||||
|
||||
String expected = """
|
||||
{
|
||||
"bool": {
|
||||
"must": [
|
||||
{
|
||||
"query_string": {
|
||||
"default_operator": "and",
|
||||
"fields": [
|
||||
"first"
|
||||
],
|
||||
"query": "hello"
|
||||
}
|
||||
},
|
||||
{
|
||||
"bool": {
|
||||
"should": [
|
||||
{
|
||||
"exists": {
|
||||
"field": "second"
|
||||
}
|
||||
},
|
||||
{
|
||||
"bool": {
|
||||
"must": [
|
||||
{
|
||||
"exists": {
|
||||
"field": "third"
|
||||
}
|
||||
},
|
||||
{
|
||||
"query_string": {
|
||||
"default_operator": "and",
|
||||
"fields": [
|
||||
"fourth"
|
||||
],
|
||||
"query": "ciao"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"bool": {
|
||||
"must": [
|
||||
{
|
||||
"exists": {
|
||||
"field": "third"
|
||||
}
|
||||
},
|
||||
{
|
||||
"query_string": {
|
||||
"default_operator": "and",
|
||||
"fields": [
|
||||
"fourth"
|
||||
],
|
||||
"query": "hi"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
mappingElasticsearchConverter.updateQuery(criteriaQuery, Person.class);
|
||||
var queryString = queryToJson(CriteriaQueryProcessor.createQuery(criteriaQuery.getCriteria()), mapper);
|
||||
|
||||
assertEquals(expected, queryString, false);
|
||||
}
|
||||
// endregion
|
||||
// region helper functions
|
||||
|
||||
|
||||
+3
-3
@@ -105,8 +105,6 @@ import org.springframework.lang.Nullable;
|
||||
@SpringIntegrationTest
|
||||
public abstract class ElasticsearchIntegrationTests {
|
||||
|
||||
static final Integer INDEX_MAX_RESULT_WINDOW = 10_000;
|
||||
|
||||
private static final String MULTI_INDEX_PREFIX = "test-index";
|
||||
private static final String MULTI_INDEX_ALL = MULTI_INDEX_PREFIX + "*";
|
||||
private static final String MULTI_INDEX_1_NAME = MULTI_INDEX_PREFIX + "-1";
|
||||
@@ -1636,7 +1634,9 @@ public abstract class ElasticsearchIntegrationTests {
|
||||
.withParams(Collections.singletonMap("newMessage", messageAfterUpdate)).withAbortOnVersionConflict(true)
|
||||
.build();
|
||||
|
||||
operations.updateByQuery(updateQuery, IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
var byQueryResponse = operations.updateByQuery(updateQuery, IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
|
||||
assertThat(byQueryResponse.getUpdated()).isEqualTo(1);
|
||||
|
||||
SampleEntity indexedEntity = operations.get(documentId, SampleEntity.class,
|
||||
IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.query;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.springframework.data.elasticsearch.core.query.BaseQuery.*;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
class BaseQueryTests {
|
||||
|
||||
private static final String MATCH_ALL_QUERY = "{\"match_all\":{}}";
|
||||
|
||||
@Test // #3127
|
||||
@DisplayName("query with no Pageable and no maxResults requests 10 docs from 0")
|
||||
void queryWithNoPageableAndNoMaxResultsRequests10DocsFrom0() {
|
||||
|
||||
var query = StringQuery.builder(MATCH_ALL_QUERY)
|
||||
.build();
|
||||
|
||||
var requestSize = query.getRequestSize();
|
||||
|
||||
assertThat(requestSize).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test // #3127
|
||||
@DisplayName("query with a Pageable and no MaxResults request with values from Pageable")
|
||||
void queryWithAPageableAndNoMaxResultsRequestWithValuesFromPageable() {
|
||||
var query = StringQuery.builder(MATCH_ALL_QUERY)
|
||||
.withPageable(Pageable.ofSize(42))
|
||||
.build();
|
||||
|
||||
var requestSize = query.getRequestSize();
|
||||
|
||||
assertThat(requestSize).isEqualTo(42);
|
||||
}
|
||||
|
||||
@Test // #3127
|
||||
@DisplayName("query with no Pageable and maxResults requests maxResults")
|
||||
void queryWithNoPageableAndMaxResultsRequestsMaxResults() {
|
||||
|
||||
var query = StringQuery.builder(MATCH_ALL_QUERY)
|
||||
.withMaxResults(12_345)
|
||||
.build();
|
||||
|
||||
var requestSize = query.getRequestSize();
|
||||
|
||||
assertThat(requestSize).isEqualTo(12_345);
|
||||
}
|
||||
|
||||
@Test // #3127
|
||||
@DisplayName("query with Pageable and maxResults requests with values from Pageable if Pageable is less than maxResults")
|
||||
void queryWithPageableAndMaxResultsRequestsWithValuesFromPageableIfPageableIsLessThanMaxResults() {
|
||||
|
||||
var query = StringQuery.builder(MATCH_ALL_QUERY)
|
||||
.withPageable(Pageable.ofSize(42))
|
||||
.withMaxResults(123)
|
||||
.build();
|
||||
|
||||
var requestSize = query.getRequestSize();
|
||||
|
||||
assertThat(requestSize).isEqualTo(42);
|
||||
}
|
||||
|
||||
@Test // #3127
|
||||
@DisplayName("query with Pageable and maxResults requests with values from maxResults if Pageable is more than maxResults")
|
||||
void queryWithPageableAndMaxResultsRequestsWithValuesFromMaxResultsIfPageableIsMoreThanMaxResults() {
|
||||
|
||||
var query = StringQuery.builder(MATCH_ALL_QUERY)
|
||||
.withPageable(Pageable.ofSize(420))
|
||||
.withMaxResults(123)
|
||||
.build();
|
||||
|
||||
var requestSize = query.getRequestSize();
|
||||
|
||||
assertThat(requestSize).isEqualTo(123);
|
||||
}
|
||||
|
||||
@Test // #3127
|
||||
@DisplayName("query with explicit unpaged request and no maxResults requests max request window size")
|
||||
void queryWithExplicitUnpagedRequestAndNoMaxResultsRequestsMaxRequestWindowSize() {
|
||||
|
||||
var query = StringQuery.builder(MATCH_ALL_QUERY)
|
||||
.withPageable(Pageable.unpaged())
|
||||
.build();
|
||||
|
||||
var requestSize = query.getRequestSize();
|
||||
|
||||
assertThat(requestSize).isEqualTo(INDEX_MAX_RESULT_WINDOW);
|
||||
}
|
||||
}
|
||||
+112
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.query;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.skyscreamer.jsonassert.JSONAssert.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
@@ -23,9 +24,11 @@ import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.elasticsearch.annotations.Field;
|
||||
import org.springframework.data.elasticsearch.annotations.FieldType;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||
@@ -639,6 +642,84 @@ public abstract class ElasticsearchPartQueryIntegrationTests {
|
||||
assertEquals(expected, query, false);
|
||||
}
|
||||
|
||||
@Test // #3072
|
||||
@DisplayName("should build sort object with correct field names")
|
||||
void shouldBuildSortObjectWithCorrectFieldNames() throws NoSuchMethodException, JSONException {
|
||||
|
||||
String methodName = "findByNameOrderBySortAuthor_SortName";
|
||||
Class<?>[] parameterClasses = new Class[] { String.class };
|
||||
Object[] parameters = new Object[] { BOOK_TITLE };
|
||||
|
||||
String query = getQueryString(methodName, parameterClasses, parameters);
|
||||
|
||||
String expected = """
|
||||
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
"must": [
|
||||
{
|
||||
"query_string": {
|
||||
"query": "Title",
|
||||
"fields": [
|
||||
"name"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"sort": [
|
||||
{
|
||||
"sort_author.sort_name": {
|
||||
"order": "asc"
|
||||
}
|
||||
}
|
||||
]
|
||||
}""";
|
||||
|
||||
assertEquals(expected, query, false);
|
||||
}
|
||||
|
||||
@Test // #3081
|
||||
@DisplayName("should build sort object with unknown field names")
|
||||
void shouldBuildSortObjectWithUnknownFieldNames() throws NoSuchMethodException, JSONException {
|
||||
|
||||
String methodName = "findByName";
|
||||
Class<?>[] parameterClasses = new Class[] { String.class, Sort.class };
|
||||
Object[] parameters = new Object[] { BOOK_TITLE, Sort.by("sortAuthor.sortName.raw") };
|
||||
|
||||
String query = getQueryString(methodName, parameterClasses, parameters);
|
||||
|
||||
String expected = """
|
||||
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
"must": [
|
||||
{
|
||||
"query_string": {
|
||||
"query": "Title",
|
||||
"fields": [
|
||||
"name"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"sort": [
|
||||
{
|
||||
"sort_author.sort_name.raw": {
|
||||
"order": "asc"
|
||||
}
|
||||
}
|
||||
]
|
||||
}""";
|
||||
|
||||
assertEquals(expected, query, false);
|
||||
}
|
||||
|
||||
private String getQueryString(String methodName, Class<?>[] parameterClasses, Object[] parameters)
|
||||
throws NoSuchMethodException {
|
||||
|
||||
@@ -726,6 +807,9 @@ public abstract class ElasticsearchPartQueryIntegrationTests {
|
||||
|
||||
List<Book> findByAvailableTrueOrderByNameDesc();
|
||||
|
||||
List<Book> findByNameOrderBySortAuthor_SortName(String name);
|
||||
|
||||
List<Book> findByName(String name, Sort sort);
|
||||
}
|
||||
|
||||
public static class Book {
|
||||
@@ -735,6 +819,10 @@ public abstract class ElasticsearchPartQueryIntegrationTests {
|
||||
@Nullable private Integer price;
|
||||
@Field(type = FieldType.Boolean) private boolean available;
|
||||
|
||||
// this is needed for the #3072 test
|
||||
@Nullable
|
||||
@Field(name = "sort_author", type = FieldType.Object) private Author sortAuthor;
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
return id;
|
||||
@@ -766,8 +854,32 @@ public abstract class ElasticsearchPartQueryIntegrationTests {
|
||||
return available;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Author getSortAuthor() {
|
||||
return sortAuthor;
|
||||
}
|
||||
|
||||
public void setSortAuthor(@Nullable Author sortAuthor) {
|
||||
this.sortAuthor = sortAuthor;
|
||||
}
|
||||
|
||||
public void setAvailable(Boolean available) {
|
||||
this.available = available;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static class Author {
|
||||
@Nullable
|
||||
@Field(name = "sort_name", type = FieldType.Keyword) private String sortName;
|
||||
|
||||
@Nullable
|
||||
public String getSortName() {
|
||||
return sortName;
|
||||
}
|
||||
|
||||
public void setSortName(@Nullable String sortName) {
|
||||
this.sortName = sortName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -129,6 +129,9 @@ public class ClusterConnection implements ExtensionContext.Store.CloseableResour
|
||||
Map<String, String> testcontainersProperties = testcontainersProperties(
|
||||
"testcontainers-" + testcontainersConfiguration + ".properties");
|
||||
|
||||
var testcontainersPropertiesLocal = testcontainersProperties("testcontainers-local.properties");
|
||||
testcontainersProperties.putAll(testcontainersPropertiesLocal);
|
||||
|
||||
DockerImageName dockerImageName = getDockerImageName(testcontainersProperties);
|
||||
|
||||
ElasticsearchContainer elasticsearchContainer = new SpringDataElasticsearchContainer(dockerImageName)
|
||||
|
||||
+20
@@ -105,6 +105,26 @@ abstract class SimpleReactiveElasticsearchRepositoryIntegrationTests {
|
||||
return operations.exists(id, IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
}
|
||||
|
||||
@Test // #3093
|
||||
@DisplayName("should save all from empty collection")
|
||||
void shouldSaveAllFromEmptyCollection() {
|
||||
|
||||
repository.saveAll(Collections.emptyList())
|
||||
.as(StepVerifier::create)
|
||||
.expectNextCount(0)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // #3093
|
||||
@DisplayName("should save all from empty flux")
|
||||
void shouldSaveAllFromEmptyFlux() {
|
||||
|
||||
repository.saveAll(Flux.empty())
|
||||
.as(StepVerifier::create)
|
||||
.expectNextCount(0)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
void saveShouldComputeMultipleEntities() {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user