Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b5e7c3d012 | |||
| 2b8fbc18a2 | |||
| f1f73a2992 | |||
| 50d606bfb8 | |||
| 44b0ef0dac | |||
| 69329bc62d | |||
| aaaa18ec4c | |||
| e9c2961133 | |||
| 08ed5b724d | |||
| 12b869e9dd | |||
| d35dd313b1 | |||
| f65d557689 | |||
| 1c4332dd12 | |||
| b21bd796f6 | |||
| 16eefb6f60 | |||
| 7ebc4ee13a | |||
| a065ba6a76 | |||
| 631e5fe496 | |||
| b1e6e51e16 | |||
| d5df3bfd20 | |||
| 7f390845d3 | |||
| 868a994e33 | |||
| 7d9e6a5a2a | |||
| b34ed6fd2b | |||
| ff4e07d350 | |||
| ede29781da | |||
| 53a7af879a | |||
| c7512fb325 | |||
| b4fde9d48b | |||
| 8e56ef77b8 | |||
| a2a4114d9c | |||
| ec8401a2fb | |||
| 19ec05dae3 | |||
| 70d2de7eec | |||
| 1666fcee02 | |||
| c513a0c03e | |||
| 22fe50c73a | |||
| ef90755c27 | |||
| 9153d4dff4 | |||
| 9109dfa257 | |||
| 64eee02b85 | |||
| 31365fb722 | |||
| c218c1d5e3 | |||
| 5bce5dd880 | |||
| 32c4c4d03f | |||
| 5102d98e10 |
+2
-2
@@ -1,3 +1,3 @@
|
||||
#Tue Jun 13 08:52:21 CEST 2023
|
||||
#Wed Oct 04 18:24:26 PDT 2023
|
||||
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.2/apache-maven-3.9.2-bin.zip
|
||||
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.5/apache-maven-3.9.5-bin.zip
|
||||
|
||||
Vendored
-4
@@ -111,10 +111,6 @@ pipeline {
|
||||
post {
|
||||
changed {
|
||||
script {
|
||||
slackSend(
|
||||
color: (currentBuild.currentResult == 'SUCCESS') ? 'good' : 'danger',
|
||||
channel: '#spring-data-dev',
|
||||
message: "${currentBuild.fullDisplayName} - `${currentBuild.currentResult}`\n${env.BUILD_URL}")
|
||||
emailext(
|
||||
subject: "[${currentBuild.fullDisplayName}] ${currentBuild.currentResult}",
|
||||
mimeType: 'text/html',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Java versions
|
||||
java.main.tag=17.0.6_10-jdk-focal
|
||||
java.main.tag=17.0.8_7-jdk-focal
|
||||
java.next.tag=20-jdk-jammy
|
||||
|
||||
# Docker container images - standard
|
||||
@@ -7,15 +7,15 @@ docker.java.main.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/ecli
|
||||
docker.java.next.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.next.tag}
|
||||
|
||||
# Supported versions of MongoDB
|
||||
docker.mongodb.4.4.version=4.4.18
|
||||
docker.mongodb.5.0.version=5.0.14
|
||||
docker.mongodb.6.0.version=6.0.4
|
||||
docker.mongodb.4.4.version=4.4.25
|
||||
docker.mongodb.5.0.version=5.0.21
|
||||
docker.mongodb.6.0.version=6.0.10
|
||||
|
||||
# Supported versions of Redis
|
||||
docker.redis.6.version=6.2.10
|
||||
docker.redis.6.version=6.2.13
|
||||
|
||||
# Supported versions of Cassandra
|
||||
docker.cassandra.3.version=3.11.14
|
||||
docker.cassandra.3.version=3.11.16
|
||||
|
||||
# Docker environment settings
|
||||
docker.java.inside.basic=-v $HOME:/tmp/jenkins-home
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-elasticsearch</artifactId>
|
||||
<version>5.0.7</version>
|
||||
<version>5.0.12</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.build</groupId>
|
||||
<artifactId>spring-data-parent</artifactId>
|
||||
<version>3.0.7</version>
|
||||
<version>3.0.12</version>
|
||||
</parent>
|
||||
|
||||
<name>Spring Data Elasticsearch</name>
|
||||
@@ -18,10 +18,10 @@
|
||||
<url>https://github.com/spring-projects/spring-data-elasticsearch</url>
|
||||
|
||||
<properties>
|
||||
<springdata.commons>3.0.7</springdata.commons>
|
||||
<springdata.commons>3.0.12</springdata.commons>
|
||||
|
||||
<!-- version of the RestHighLevelClient -->
|
||||
<elasticsearch-rhlc>7.17.9</elasticsearch-rhlc>
|
||||
<elasticsearch-rhlc>7.17.15</elasticsearch-rhlc>
|
||||
<!-- version of the new ElasticsearchClient -->
|
||||
<elasticsearch-java>8.5.3</elasticsearch-java>
|
||||
|
||||
@@ -502,9 +502,7 @@
|
||||
|
||||
<repositories>
|
||||
|
||||
|
||||
|
||||
|
||||
</repositories>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -154,7 +154,7 @@ The old deprecated `RestHighLevelClient` can still be used, but you will need to
|
||||
<dependency>
|
||||
<groupId>org.elasticsearch.client</groupId>
|
||||
<artifactId>elasticsearch-rest-high-level-client</artifactId>
|
||||
<version>7.17.9</version>
|
||||
<version>7.17.12</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
@@ -165,4 +165,4 @@ The old deprecated `RestHighLevelClient` can still be used, but you will need to
|
||||
----
|
||||
====
|
||||
|
||||
Make sure to specify the version 7.17.9 explicitly, otherwise maven will resolve to 8.5.3, and this does not exist.
|
||||
Make sure to specify the version 7.17.12 explicitly, otherwise maven will resolve to 8.5.3, and this does not exist.
|
||||
|
||||
+30
-73
@@ -15,15 +15,10 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.client.elc;
|
||||
|
||||
import static org.springframework.data.elasticsearch.client.elc.TypeUtils.searchType;
|
||||
import static org.springframework.data.elasticsearch.client.elc.TypeUtils.slices;
|
||||
import static org.springframework.data.elasticsearch.client.elc.TypeUtils.time;
|
||||
import static org.springframework.data.elasticsearch.client.elc.TypeUtils.timeStringMs;
|
||||
import static org.springframework.data.elasticsearch.client.elc.TypeUtils.toFloat;
|
||||
import static org.springframework.util.CollectionUtils.isEmpty;
|
||||
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;
|
||||
@@ -35,20 +30,10 @@ import co.elastic.clients.elasticsearch._types.mapping.Property;
|
||||
import co.elastic.clients.elasticsearch._types.mapping.RuntimeField;
|
||||
import co.elastic.clients.elasticsearch._types.mapping.RuntimeFieldType;
|
||||
import co.elastic.clients.elasticsearch._types.mapping.TypeMapping;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.FieldAndFormat;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.Like;
|
||||
import co.elastic.clients.elasticsearch.cluster.HealthRequest;
|
||||
import co.elastic.clients.elasticsearch.core.BulkRequest;
|
||||
import co.elastic.clients.elasticsearch.core.ClosePointInTimeRequest;
|
||||
import co.elastic.clients.elasticsearch.core.DeleteByQueryRequest;
|
||||
import co.elastic.clients.elasticsearch.core.DeleteRequest;
|
||||
import co.elastic.clients.elasticsearch.core.GetRequest;
|
||||
import co.elastic.clients.elasticsearch.core.IndexRequest;
|
||||
import co.elastic.clients.elasticsearch.core.MgetRequest;
|
||||
import co.elastic.clients.elasticsearch.core.MsearchRequest;
|
||||
import co.elastic.clients.elasticsearch.core.OpenPointInTimeRequest;
|
||||
import co.elastic.clients.elasticsearch.core.SearchRequest;
|
||||
import co.elastic.clients.elasticsearch.core.UpdateByQueryRequest;
|
||||
import co.elastic.clients.elasticsearch.core.UpdateRequest;
|
||||
import co.elastic.clients.elasticsearch.core.*;
|
||||
import co.elastic.clients.elasticsearch.core.bulk.BulkOperation;
|
||||
import co.elastic.clients.elasticsearch.core.bulk.CreateOperation;
|
||||
import co.elastic.clients.elasticsearch.core.bulk.IndexOperation;
|
||||
@@ -58,17 +43,8 @@ import co.elastic.clients.elasticsearch.core.msearch.MultisearchBody;
|
||||
import co.elastic.clients.elasticsearch.core.search.Highlight;
|
||||
import co.elastic.clients.elasticsearch.core.search.Rescore;
|
||||
import co.elastic.clients.elasticsearch.core.search.SourceConfig;
|
||||
import co.elastic.clients.elasticsearch.indices.CreateIndexRequest;
|
||||
import co.elastic.clients.elasticsearch.indices.DeleteIndexRequest;
|
||||
import co.elastic.clients.elasticsearch.indices.*;
|
||||
import co.elastic.clients.elasticsearch.indices.ExistsRequest;
|
||||
import co.elastic.clients.elasticsearch.indices.GetAliasRequest;
|
||||
import co.elastic.clients.elasticsearch.indices.GetIndexRequest;
|
||||
import co.elastic.clients.elasticsearch.indices.GetIndicesSettingsRequest;
|
||||
import co.elastic.clients.elasticsearch.indices.GetMappingRequest;
|
||||
import co.elastic.clients.elasticsearch.indices.IndexSettings;
|
||||
import co.elastic.clients.elasticsearch.indices.PutMappingRequest;
|
||||
import co.elastic.clients.elasticsearch.indices.RefreshRequest;
|
||||
import co.elastic.clients.elasticsearch.indices.UpdateAliasesRequest;
|
||||
import co.elastic.clients.elasticsearch.indices.update_aliases.Action;
|
||||
import co.elastic.clients.json.JsonData;
|
||||
import co.elastic.clients.json.JsonpDeserializer;
|
||||
@@ -85,7 +61,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;
|
||||
@@ -107,19 +82,7 @@ import org.springframework.data.elasticsearch.core.index.PutTemplateRequest;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.query.BaseQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.BulkOptions;
|
||||
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.GeoDistanceOrder;
|
||||
import org.springframework.data.elasticsearch.core.query.IndexQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.MoreLikeThisQuery;
|
||||
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.ScriptData;
|
||||
import org.springframework.data.elasticsearch.core.query.SourceFilter;
|
||||
import org.springframework.data.elasticsearch.core.query.StringQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.*;
|
||||
import org.springframework.data.elasticsearch.core.reindex.ReindexRequest;
|
||||
import org.springframework.data.elasticsearch.core.reindex.Remote;
|
||||
import org.springframework.data.elasticsearch.support.DefaultStringObjectMap;
|
||||
@@ -918,7 +881,7 @@ class RequestConverter {
|
||||
.docAsUpsert(query.getDocAsUpsert()) //
|
||||
.ifSeqNo(query.getIfSeqNo() != null ? Long.valueOf(query.getIfSeqNo()) : null) //
|
||||
.ifPrimaryTerm(query.getIfPrimaryTerm() != null ? Long.valueOf(query.getIfPrimaryTerm()) : null) //
|
||||
.refresh(TypeUtils.refresh(refreshPolicy)) //
|
||||
.refresh(query.getRefreshPolicy() != null ? refresh(query.getRefreshPolicy()) : refresh(refreshPolicy)) //
|
||||
.retryOnConflict(query.getRetryOnConflict()) //
|
||||
;
|
||||
|
||||
@@ -1088,10 +1051,9 @@ class RequestConverter {
|
||||
}
|
||||
|
||||
if (!isEmpty(query.getFields())) {
|
||||
bb.fields(fb -> {
|
||||
query.getFields().forEach(fb::field);
|
||||
return fb;
|
||||
});
|
||||
var fieldAndFormats = query.getFields().stream()
|
||||
.map(field -> FieldAndFormat.of(b -> b.field(field))).toList();
|
||||
bb.fields(fieldAndFormats);
|
||||
}
|
||||
|
||||
if (!isEmpty(query.getStoredFields())) {
|
||||
@@ -1121,8 +1083,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)));
|
||||
@@ -1146,11 +1107,9 @@ class RequestConverter {
|
||||
}
|
||||
|
||||
if (!isEmpty(query.getIndicesBoost())) {
|
||||
Map<String, Double> boosts = new LinkedHashMap<>();
|
||||
query.getIndicesBoost()
|
||||
.forEach(indexBoost -> boosts.put(indexBoost.getIndexName(), (double) indexBoost.getBoost()));
|
||||
// noinspection unchecked
|
||||
bb.indicesBoost(boosts);
|
||||
bb.indicesBoost(query.getIndicesBoost().stream()
|
||||
.map(indexBoost -> Map.of(indexBoost.getIndexName(), Double.valueOf(indexBoost.getBoost())))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
if (query instanceof NativeQuery) {
|
||||
@@ -1210,10 +1169,9 @@ class RequestConverter {
|
||||
builder.source(getSourceConfig(query));
|
||||
|
||||
if (!isEmpty(query.getFields())) {
|
||||
builder.fields(fb -> {
|
||||
query.getFields().forEach(fb::field);
|
||||
return fb;
|
||||
});
|
||||
var fieldAndFormats = query.getFields().stream()
|
||||
.map(field -> FieldAndFormat.of(b -> b.field(field))).toList();
|
||||
builder.fields(fieldAndFormats);
|
||||
}
|
||||
|
||||
if (!isEmpty(query.getStoredFields())) {
|
||||
@@ -1234,6 +1192,13 @@ class RequestConverter {
|
||||
|
||||
builder.searchType(searchType(query.getSearchType()));
|
||||
|
||||
addHighlight(query, builder);
|
||||
|
||||
if (query instanceof NativeQuery) {
|
||||
prepareNativeSearch((NativeQuery) query, builder);
|
||||
}
|
||||
// query.getSort() must be checked after prepareNativeSearch as this already might hav a sort set that must have
|
||||
// higher priority
|
||||
if (query.getSort() != null) {
|
||||
List<SortOptions> sortOptions = getSortOptions(query.getSort(), persistentEntity);
|
||||
|
||||
@@ -1242,12 +1207,6 @@ class RequestConverter {
|
||||
}
|
||||
}
|
||||
|
||||
addHighlight(query, builder);
|
||||
|
||||
if (query instanceof NativeQuery) {
|
||||
prepareNativeSearch((NativeQuery) query, builder);
|
||||
}
|
||||
|
||||
if (query.getTrackTotalHits() != null) {
|
||||
// logic from the RHLC, choose between -1 and Integer.MAX_VALUE
|
||||
int value = query.getTrackTotalHits() ? Integer.MAX_VALUE : -1;
|
||||
@@ -1263,8 +1222,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)));
|
||||
@@ -1301,11 +1259,9 @@ class RequestConverter {
|
||||
}
|
||||
|
||||
if (!isEmpty(query.getIndicesBoost())) {
|
||||
Map<String, Double> boosts = new LinkedHashMap<>();
|
||||
query.getIndicesBoost()
|
||||
.forEach(indexBoost -> boosts.put(indexBoost.getIndexName(), (double) indexBoost.getBoost()));
|
||||
// noinspection unchecked
|
||||
builder.indicesBoost(boosts);
|
||||
builder.indicesBoost(query.getIndicesBoost().stream()
|
||||
.map(indexBoost -> Map.of(indexBoost.getIndexName(), Double.valueOf(indexBoost.getBoost())))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1373,9 +1329,10 @@ class RequestConverter {
|
||||
return SortOptions.of(so -> so //
|
||||
.geoDistance(gd -> gd //
|
||||
.field(fieldName) //
|
||||
.location(loc -> loc.latlon(QueryBuilders.latLon(geoDistanceOrder.getGeoPoint())))//
|
||||
.location(loc -> loc.latlon(QueryBuilders.latLon(geoDistanceOrder.getGeoPoint()))) //
|
||||
.distanceType(TypeUtils.geoDistanceType(geoDistanceOrder.getDistanceType()))
|
||||
.mode(TypeUtils.sortMode(finalMode)) //
|
||||
.order(TypeUtils.sortOrder(geoDistanceOrder.getDirection())) //
|
||||
.unit(TypeUtils.distanceUnit(geoDistanceOrder.getUnit())) //
|
||||
.ignoreUnmapped(geoDistanceOrder.getIgnoreUnmapped())));
|
||||
} else {
|
||||
|
||||
+2
-1
@@ -192,7 +192,8 @@ class SearchDocumentResponseBuilder {
|
||||
var phraseSuggestOptions = phraseSuggest.options();
|
||||
List<PhraseSuggestion.Entry.Option> options = new ArrayList<>();
|
||||
phraseSuggestOptions.forEach(optionES -> options
|
||||
.add(new PhraseSuggestion.Entry.Option(optionES.text(), optionES.highlighted(), null, null)));
|
||||
.add(new PhraseSuggestion.Entry.Option(optionES.text(), optionES.highlighted(), optionES.score(),
|
||||
optionES.collateMatch())));
|
||||
entries.add(new PhraseSuggestion.Entry(phraseSuggest.text(), phraseSuggest.offset(), phraseSuggest.length(),
|
||||
options, null));
|
||||
});
|
||||
|
||||
@@ -24,9 +24,11 @@ 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.json.JsonData;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.elasticsearch.core.RefreshPolicy;
|
||||
import org.springframework.data.elasticsearch.core.query.GeoDistanceOrder;
|
||||
import org.springframework.data.elasticsearch.core.query.IndexQuery;
|
||||
@@ -126,6 +128,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) {
|
||||
|
||||
@@ -136,6 +172,20 @@ final class TypeUtils {
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static SortOrder sortOrder(@Nullable Sort.Direction direction) {
|
||||
|
||||
if (direction == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return switch (direction) {
|
||||
case ASC -> SortOrder.Asc;
|
||||
case DESC -> SortOrder.Desc;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static HighlighterFragmenter highlighterFragmenter(@Nullable String value) {
|
||||
|
||||
|
||||
@@ -963,6 +963,8 @@ class RequestFactory {
|
||||
sort.ignoreUnmapped(geoDistanceOrder.getIgnoreUnmapped());
|
||||
}
|
||||
|
||||
sort.order(order.isAscending() ? SortOrder.ASC : SortOrder.DESC);
|
||||
|
||||
return sort;
|
||||
} else {
|
||||
FieldSortBuilder sort = SortBuilders //
|
||||
|
||||
+6
@@ -658,6 +658,12 @@ abstract public class AbstractReactiveElasticsearchTemplate
|
||||
|
||||
public abstract Mono<String> getClusterVersion();
|
||||
|
||||
@Nullable
|
||||
public String getEntityRouting(Object entity) {
|
||||
return entityOperations.forEntity(entity, converter.getConversionService(), routingResolver)
|
||||
.getRouting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Value class to capture client independent information from a response to an index request.
|
||||
*/
|
||||
|
||||
+11
@@ -115,6 +115,17 @@ public interface ReactiveElasticsearchOperations extends ReactiveDocumentOperati
|
||||
ReactiveClusterOperations cluster();
|
||||
|
||||
// region routing
|
||||
/**
|
||||
* gets the routing for an entity.
|
||||
*
|
||||
* @param entity the entity
|
||||
* @return the routing, may be null if not set.
|
||||
* @since 5.2
|
||||
*/
|
||||
@Nullable
|
||||
String getEntityRouting(Object entity);
|
||||
|
||||
// region customizations
|
||||
/**
|
||||
* Returns a copy of this instance with the same configuration, but that uses a different {@link RoutingResolver} to
|
||||
* obtain routing information.
|
||||
|
||||
+11
-5
@@ -32,9 +32,19 @@ public abstract class AbstractRangePropertyValueConverter<T> extends AbstractPro
|
||||
protected static final String LTE_FIELD = "lte";
|
||||
protected static final String GT_FIELD = "gt";
|
||||
protected static final String GTE_FIELD = "gte";
|
||||
private final Class<?> genericType;
|
||||
|
||||
public AbstractRangePropertyValueConverter(PersistentProperty<?> property) {
|
||||
/**
|
||||
* @param property the property this convertrer belongs to
|
||||
* @param genericType the generic type of the Range
|
||||
*/
|
||||
public AbstractRangePropertyValueConverter(PersistentProperty<?> property, Class<?> genericType) {
|
||||
super(property);
|
||||
this.genericType = genericType;
|
||||
}
|
||||
|
||||
public Class<?> getGenericType() {
|
||||
return genericType;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -117,10 +127,6 @@ public abstract class AbstractRangePropertyValueConverter<T> extends AbstractPro
|
||||
|
||||
protected abstract String format(T value);
|
||||
|
||||
protected Class<?> getGenericType() {
|
||||
return getProperty().getTypeInformation().getTypeArguments().get(0).getType();
|
||||
}
|
||||
|
||||
protected abstract T parse(String value);
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -33,9 +33,9 @@ public class DateRangePropertyValueConverter extends AbstractRangePropertyValueC
|
||||
private final List<ElasticsearchDateConverter> dateConverters;
|
||||
|
||||
public DateRangePropertyValueConverter(PersistentProperty<?> property,
|
||||
List<ElasticsearchDateConverter> dateConverters) {
|
||||
Class<?> genericType, List<ElasticsearchDateConverter> dateConverters) {
|
||||
|
||||
super(property);
|
||||
super(property, genericType);
|
||||
this.dateConverters = dateConverters;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -919,7 +919,7 @@ public class MappingElasticsearchConverter
|
||||
|
||||
Class<?> elementType = element == null ? null : element.getClass();
|
||||
|
||||
if (elementType == null || conversions.isSimpleType(elementType)) {
|
||||
if (elementType == null || isSimpleType(elementType)) {
|
||||
collection.add(getPotentiallyConvertedSimpleWrite(element,
|
||||
componentType != null ? componentType.getType() : Object.class));
|
||||
} else if (element instanceof Collection || elementType.isArray()) {
|
||||
|
||||
+6
-2
@@ -23,8 +23,12 @@ import org.springframework.data.mapping.PersistentProperty;
|
||||
*/
|
||||
public class NumberRangePropertyValueConverter extends AbstractRangePropertyValueConverter<Number> {
|
||||
|
||||
public NumberRangePropertyValueConverter(PersistentProperty<?> property) {
|
||||
super(property);
|
||||
/**
|
||||
* @param property the property this convertrer belongs to
|
||||
* @param genericType the generic type of the Range
|
||||
*/
|
||||
public NumberRangePropertyValueConverter(PersistentProperty<?> property, Class<?> genericType) {
|
||||
super(property, genericType);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-2
@@ -34,9 +34,9 @@ public class TemporalRangePropertyValueConverter extends AbstractRangePropertyVa
|
||||
private final List<ElasticsearchDateConverter> dateConverters;
|
||||
|
||||
public TemporalRangePropertyValueConverter(PersistentProperty<?> property,
|
||||
List<ElasticsearchDateConverter> dateConverters) {
|
||||
Class<?> genericType, List<ElasticsearchDateConverter> dateConverters) {
|
||||
|
||||
super(property);
|
||||
super(property, genericType);
|
||||
|
||||
Assert.notEmpty(dateConverters, "dateConverters must not be empty.");
|
||||
this.dateConverters = dateConverters;
|
||||
|
||||
@@ -332,6 +332,8 @@ public final class MappingParameters {
|
||||
|
||||
if (similarity != Similarity.Default) {
|
||||
objectNode.put(FIELD_PARAM_SIMILARITY, similarity.toString());
|
||||
// similarity must have index explicitly set, otherwise Elasticsearch returns an error
|
||||
objectNode.put(FIELD_PARAM_INDEX, index);
|
||||
}
|
||||
|
||||
if (termVector != TermVector.none) {
|
||||
|
||||
+19
-5
@@ -20,6 +20,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -54,6 +55,7 @@ import org.springframework.data.mapping.model.FieldNamingStrategy;
|
||||
import org.springframework.data.mapping.model.Property;
|
||||
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
|
||||
import org.springframework.data.mapping.model.SimpleTypeHolder;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -158,6 +160,18 @@ public class SimpleElasticsearchPersistentProperty extends
|
||||
return;
|
||||
}
|
||||
|
||||
Supplier<Class<?>> getGenericType = () -> {
|
||||
TypeInformation<?> typeInformation = getTypeInformation();
|
||||
|
||||
if (typeInformation.isCollectionLike()) {
|
||||
// we have a collection of Range<?>
|
||||
typeInformation = typeInformation.getComponentType();
|
||||
}
|
||||
|
||||
Class<?> genericType = typeInformation.getTypeArguments().get(0).getType();
|
||||
return genericType;
|
||||
};
|
||||
|
||||
switch (field.type()) {
|
||||
case Date:
|
||||
case Date_Nanos: {
|
||||
@@ -187,11 +201,11 @@ public class SimpleElasticsearchPersistentProperty extends
|
||||
return;
|
||||
}
|
||||
|
||||
Class<?> genericType = getTypeInformation().getTypeArguments().get(0).getType();
|
||||
var genericType = getGenericType.get();
|
||||
if (TemporalAccessor.class.isAssignableFrom(genericType)) {
|
||||
propertyValueConverter = new TemporalRangePropertyValueConverter(this, dateConverters);
|
||||
propertyValueConverter = new TemporalRangePropertyValueConverter(this, genericType, dateConverters);
|
||||
} else if (Date.class.isAssignableFrom(genericType)) {
|
||||
propertyValueConverter = new DateRangePropertyValueConverter(this, dateConverters);
|
||||
propertyValueConverter = new DateRangePropertyValueConverter(this, genericType, dateConverters);
|
||||
} else {
|
||||
LOGGER.warn(
|
||||
String.format("Unsupported generic type '{%s' for date range property '%s'.", genericType, getName()));
|
||||
@@ -206,7 +220,7 @@ public class SimpleElasticsearchPersistentProperty extends
|
||||
return;
|
||||
}
|
||||
|
||||
Class<?> genericType = getTypeInformation().getTypeArguments().get(0).getType();
|
||||
var genericType = getGenericType.get();
|
||||
if ((field.type() == FieldType.Integer_Range && !Integer.class.isAssignableFrom(genericType))
|
||||
|| (field.type() == FieldType.Float_Range && !Float.class.isAssignableFrom(genericType))
|
||||
|| (field.type() == FieldType.Long_Range && !Long.class.isAssignableFrom(genericType))
|
||||
@@ -216,7 +230,7 @@ public class SimpleElasticsearchPersistentProperty extends
|
||||
return;
|
||||
}
|
||||
|
||||
propertyValueConverter = new NumberRangePropertyValueConverter(this);
|
||||
propertyValueConverter = new NumberRangePropertyValueConverter(this, genericType);
|
||||
break;
|
||||
}
|
||||
case Ip_Range: {
|
||||
|
||||
+9
-4
@@ -39,6 +39,7 @@ import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.query.BaseQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.MoreLikeThisQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.core.routing.RoutingResolver;
|
||||
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
|
||||
import org.springframework.data.util.StreamUtils;
|
||||
import org.springframework.data.util.Streamable;
|
||||
@@ -223,7 +224,7 @@ public class SimpleElasticsearchRepository<T, ID> implements ElasticsearchReposi
|
||||
|
||||
Assert.notNull(id, "Cannot delete entity with id 'null'.");
|
||||
|
||||
doDelete(id, getIndexCoordinates());
|
||||
doDelete(id, null, getIndexCoordinates());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -231,7 +232,7 @@ public class SimpleElasticsearchRepository<T, ID> implements ElasticsearchReposi
|
||||
|
||||
Assert.notNull(entity, "Cannot delete 'null' entity.");
|
||||
|
||||
doDelete(extractIdFromBean(entity), getIndexCoordinates());
|
||||
doDelete(extractIdFromBean(entity), operations.getEntityRouting(entity), getIndexCoordinates());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -271,10 +272,14 @@ public class SimpleElasticsearchRepository<T, ID> implements ElasticsearchReposi
|
||||
deleteAllById(ids);
|
||||
}
|
||||
|
||||
private void doDelete(@Nullable ID id, IndexCoordinates indexCoordinates) {
|
||||
private void doDelete(@Nullable ID id, @Nullable String routing, IndexCoordinates indexCoordinates) {
|
||||
|
||||
if (id != null) {
|
||||
executeAndRefresh(operations -> operations.delete(stringIdRepresentation(id), indexCoordinates));
|
||||
executeAndRefresh(operations -> {
|
||||
var ops = routing != null ? operations.withRouting(RoutingResolver.just(routing)) : operations;
|
||||
// noinspection DataFlowIssue
|
||||
return ops.delete(stringIdRepresentation(id), indexCoordinates);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-1
@@ -31,6 +31,7 @@ import org.springframework.data.elasticsearch.core.SearchHit;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.core.routing.RoutingResolver;
|
||||
import org.springframework.data.elasticsearch.repository.ReactiveElasticsearchRepository;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -196,7 +197,10 @@ public class SimpleReactiveElasticsearchRepository<T, ID> implements ReactiveEla
|
||||
public Mono<Void> delete(T entity) {
|
||||
|
||||
Assert.notNull(entity, "Entity must not be null!");
|
||||
return operations.delete(entity, entityInformation.getIndexCoordinates()) //
|
||||
|
||||
var routing = operations.getEntityRouting(entity);
|
||||
var ops = routing != null ? operations.withRouting(RoutingResolver.just(routing)) : operations;
|
||||
return ops.delete(entity, entityInformation.getIndexCoordinates()) //
|
||||
.then(doRefresh());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Spring Data Elasticsearch 5.0.7 (2022.0.7)
|
||||
Spring Data Elasticsearch 5.0.12 (2022.0.12)
|
||||
Copyright (c) [2013-2021] Pivotal Software, Inc.
|
||||
|
||||
This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
||||
@@ -23,3 +23,8 @@ conditions of the subcomponent's license, as noted in the LICENSE file.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2023 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.client.elc;
|
||||
|
||||
import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
|
||||
import co.elastic.clients.elasticsearch.core.search.Suggestion;
|
||||
import co.elastic.clients.elasticsearch.core.search.TotalHitsRelation;
|
||||
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.assertj.core.api.SoftAssertions;
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
/**
|
||||
* Tests for the factory class to create {@link SearchDocumentResponse} instances.
|
||||
*
|
||||
* @author Sébastien Comeau
|
||||
* @since 5.2
|
||||
*/
|
||||
class SearchDocumentResponseBuilderUnitTests {
|
||||
|
||||
private JacksonJsonpMapper jsonpMapper = new JacksonJsonpMapper();
|
||||
|
||||
@Test // #2681
|
||||
void shouldGetPhraseSuggestion() throws JSONException {
|
||||
// arrange
|
||||
final var hitsMetadata = new HitsMetadata.Builder<EntityAsMap>()
|
||||
.total(total -> total
|
||||
.value(0)
|
||||
.relation(TotalHitsRelation.Eq))
|
||||
.hits(new ArrayList<>())
|
||||
.build();
|
||||
|
||||
final var suggestionTest = new Suggestion.Builder<EntityAsMap>()
|
||||
.phrase(phrase -> phrase
|
||||
.text("National")
|
||||
.offset(0)
|
||||
.length(8)
|
||||
.options(option -> option
|
||||
.text("nations")
|
||||
.highlighted("highlighted-nations")
|
||||
.score(0.11480146)
|
||||
.collateMatch(false))
|
||||
.options(option -> option
|
||||
.text("national")
|
||||
.highlighted("highlighted-national")
|
||||
.score(0.08063514)
|
||||
.collateMatch(false)))
|
||||
.build();
|
||||
|
||||
final var sortProperties = ImmutableMap.<String, List<Suggestion<EntityAsMap>>> builder()
|
||||
.put("suggestionTest", ImmutableList.of(suggestionTest))
|
||||
.build();
|
||||
|
||||
// act
|
||||
final var actual = SearchDocumentResponseBuilder.from(hitsMetadata, null, null, null, sortProperties, null,
|
||||
jsonpMapper);
|
||||
|
||||
// assert
|
||||
SoftAssertions softly = new SoftAssertions();
|
||||
|
||||
softly.assertThat(actual).isNotNull();
|
||||
softly.assertThat(actual.getSuggest()).isNotNull();
|
||||
softly.assertThat(actual.getSuggest().getSuggestions()).isNotNull().hasSize(1);
|
||||
|
||||
final var actualSuggestion = actual.getSuggest().getSuggestions().get(0);
|
||||
softly.assertThat(actualSuggestion.getName()).isEqualTo("suggestionTest");
|
||||
softly.assertThat(actualSuggestion.getEntries()).isNotNull().hasSize(1);
|
||||
|
||||
final var actualEntry = actualSuggestion.getEntries().get(0);
|
||||
softly.assertThat(actualEntry).isNotNull();
|
||||
softly.assertThat(actualEntry.getText()).isEqualTo("National");
|
||||
softly.assertThat(actualEntry.getOffset()).isEqualTo(0);
|
||||
softly.assertThat(actualEntry.getLength()).isEqualTo(8);
|
||||
softly.assertThat(actualEntry.getOptions()).isNotNull().hasSize(2);
|
||||
|
||||
final var actualOption1 = actualEntry.getOptions().get(0);
|
||||
softly.assertThat(actualOption1.getText()).isEqualTo("nations");
|
||||
softly.assertThat(actualOption1.getHighlighted()).isEqualTo("highlighted-nations");
|
||||
softly.assertThat(actualOption1.getScore()).isEqualTo(0.11480146);
|
||||
softly.assertThat(actualOption1.getCollateMatch()).isEqualTo(false);
|
||||
|
||||
final var actualOption2 = actualEntry.getOptions().get(1);
|
||||
softly.assertThat(actualOption2.getText()).isEqualTo("national");
|
||||
softly.assertThat(actualOption2.getHighlighted()).isEqualTo("highlighted-national");
|
||||
softly.assertThat(actualOption2.getScore()).isEqualTo(0.08063514);
|
||||
softly.assertThat(actualOption2.getCollateMatch()).isEqualTo(false);
|
||||
|
||||
softly.assertAll();
|
||||
}
|
||||
}
|
||||
+49
@@ -18,6 +18,7 @@ package org.springframework.data.elasticsearch.core;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.springframework.data.elasticsearch.client.elc.QueryBuilders.*;
|
||||
|
||||
import co.elastic.clients.elasticsearch._types.SortOrder;
|
||||
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
|
||||
import co.elastic.clients.elasticsearch._types.aggregations.Buckets;
|
||||
import co.elastic.clients.elasticsearch._types.aggregations.StringTermsAggregate;
|
||||
@@ -27,10 +28,13 @@ import co.elastic.clients.elasticsearch.core.search.FieldCollapse;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.elasticsearch.ELCQueries;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.elasticsearch.client.elc.Aggregation;
|
||||
import org.springframework.data.elasticsearch.client.elc.ElasticsearchAggregation;
|
||||
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
|
||||
@@ -48,6 +52,51 @@ import org.springframework.test.context.ContextConfiguration;
|
||||
@ContextConfiguration(classes = ReactiveElasticsearchELCIntegrationTests.Config.class)
|
||||
public class ReactiveElasticsearchELCIntegrationTests extends ReactiveElasticsearchIntegrationTests {
|
||||
|
||||
@Test // #2745
|
||||
@DisplayName("should use sort defined in native unbounded query")
|
||||
void shouldUseSortDefinedInNativeUnboundedQuery() {
|
||||
var entity1 = randomEntity(null);
|
||||
entity1.setRate(7);
|
||||
var entity2 = randomEntity(null);
|
||||
entity2.setRate(5);
|
||||
var entity3 = randomEntity(null);
|
||||
entity3.setRate(11);
|
||||
|
||||
operations.saveAll(List.of(entity1, entity2, entity3), SampleEntity.class).blockLast();
|
||||
|
||||
var query = NativeQuery.builder()
|
||||
.withQuery(qb -> qb
|
||||
.matchAll(m -> m))
|
||||
.withSort(sob -> sob
|
||||
.field(f -> f
|
||||
.field("rate")
|
||||
.order(SortOrder.Asc)))
|
||||
.withPageable(Pageable.unpaged())
|
||||
.build();
|
||||
|
||||
var rates = operations.search(query, SampleEntity.class)
|
||||
.map(SearchHit::getContent)
|
||||
.map(SampleEntity::getRate)
|
||||
.collectList().block();
|
||||
assertThat(rates).containsExactly(5, 7, 11);
|
||||
|
||||
query = NativeQuery.builder()
|
||||
.withQuery(qb -> qb
|
||||
.matchAll(m -> m))
|
||||
.withSort(sob -> sob
|
||||
.field(f -> f
|
||||
.field("rate")
|
||||
.order(SortOrder.Desc)))
|
||||
.withPageable(Pageable.unpaged())
|
||||
.build();
|
||||
|
||||
rates = operations.search(query, SampleEntity.class)
|
||||
.map(SearchHit::getContent)
|
||||
.map(SampleEntity::getRate)
|
||||
.collectList().block();
|
||||
assertThat(rates).containsExactly(11, 7, 5);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import({ ReactiveElasticsearchTemplateConfiguration.class })
|
||||
static class Config {
|
||||
|
||||
+2
-2
@@ -96,7 +96,7 @@ import org.springframework.util.StringUtils;
|
||||
@SpringIntegrationTest
|
||||
public abstract class ReactiveElasticsearchIntegrationTests {
|
||||
|
||||
@Autowired private ReactiveElasticsearchOperations operations;
|
||||
@Autowired protected ReactiveElasticsearchOperations operations;
|
||||
@Autowired private IndexNameProvider indexNameProvider;
|
||||
|
||||
// region Setup
|
||||
@@ -1165,7 +1165,7 @@ public abstract class ReactiveElasticsearchIntegrationTests {
|
||||
// endregion
|
||||
|
||||
// region Helper functions
|
||||
private SampleEntity randomEntity(String message) {
|
||||
protected SampleEntity randomEntity(@Nullable String message) {
|
||||
|
||||
SampleEntity entity = new SampleEntity();
|
||||
entity.setId(UUID.randomUUID().toString());
|
||||
|
||||
+83
@@ -31,6 +31,7 @@ import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.Field;
|
||||
import org.springframework.data.elasticsearch.annotations.FieldType;
|
||||
import org.springframework.data.elasticsearch.annotations.Mapping;
|
||||
import org.springframework.data.elasticsearch.annotations.ScriptedField;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.query.Criteria;
|
||||
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
|
||||
@@ -95,6 +96,88 @@ public abstract class RuntimeFieldsIntegrationTests {
|
||||
assertThat(searchHits.getSearchHit(0).getId()).isEqualTo("1");
|
||||
}
|
||||
|
||||
@Test // #2727
|
||||
@DisplayName("should return runtime fields values")
|
||||
void shouldReturnRuntimeFieldsValues() {
|
||||
|
||||
var entity = new SAREntity();
|
||||
entity.setId("42");
|
||||
entity.setValue(3);
|
||||
|
||||
operations.save(entity);
|
||||
|
||||
var runtimeField1 = getRuntimeField("scriptedValue1", 2);
|
||||
var runtimeField2 = getRuntimeField("scriptedValue2", 3);
|
||||
|
||||
var query = CriteriaQuery.builder(Criteria.where("value").is(3)).build();
|
||||
query.addRuntimeField(runtimeField1);
|
||||
query.addRuntimeField(runtimeField2);
|
||||
query.addSourceFilter(new FetchSourceFilterBuilder().withIncludes("*").build());
|
||||
query.addFields("scriptedValue1", "scriptedValue2");
|
||||
var searchHits = operations.search(query, SAREntity.class);
|
||||
|
||||
assertThat(searchHits.getTotalHits()).isEqualTo(1);
|
||||
var foundEntity = searchHits.getSearchHit(0).getContent();
|
||||
assertThat(foundEntity.value).isEqualTo(3);
|
||||
assertThat(foundEntity.getScriptedValue1()).isEqualTo(6);
|
||||
assertThat(foundEntity.getScriptedValue2()).isEqualTo(9);
|
||||
}
|
||||
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}-sar")
|
||||
public static class SAREntity {
|
||||
@Nullable private String id;
|
||||
@Field(type = FieldType.Integer)
|
||||
@Nullable Integer value;
|
||||
@ScriptedField
|
||||
@Nullable Integer scriptedValue1;
|
||||
@ScriptedField
|
||||
@Nullable Integer scriptedValue2;
|
||||
// getter and setter omitted
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(@Nullable String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(@Nullable Integer value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getScriptedValue1() {
|
||||
return scriptedValue1;
|
||||
}
|
||||
|
||||
public void setScriptedValue1(@Nullable Integer scriptedValue1) {
|
||||
this.scriptedValue1 = scriptedValue1;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getScriptedValue2() {
|
||||
return scriptedValue2;
|
||||
}
|
||||
|
||||
public void setScriptedValue2(@Nullable Integer scriptedValue2) {
|
||||
this.scriptedValue2 = scriptedValue2;
|
||||
}
|
||||
}
|
||||
|
||||
private static RuntimeField getRuntimeField(String fieldName, int factor) {
|
||||
return new RuntimeField(
|
||||
fieldName,
|
||||
"long",
|
||||
String.format("emit(doc['value'].size() > 0 ? doc['value'].value * %d : 0)", factor));
|
||||
}
|
||||
|
||||
@Test // #2431
|
||||
@DisplayName("should return value from runtime field defined in mapping")
|
||||
void shouldReturnValueFromRuntimeFieldDefinedInMapping() {
|
||||
|
||||
+111
-15
@@ -908,22 +908,109 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
assertEquals(expected, document.toJson(), false);
|
||||
}
|
||||
|
||||
@Test // #2627
|
||||
@DisplayName("should write Map containing collection containing map")
|
||||
void shouldWriteMapContainingCollectionContainingMap() throws JSONException {
|
||||
|
||||
class EntityWithMapCollectionMap {
|
||||
Map<String, Object> map;
|
||||
}
|
||||
class InnerEntity {
|
||||
String prop1;
|
||||
|
||||
String prop2;
|
||||
|
||||
public InnerEntity() {}
|
||||
|
||||
public InnerEntity(String prop1, String prop2) {
|
||||
this.prop1 = prop1;
|
||||
this.prop2 = prop2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var entity = new EntityWithMapCollectionMap();
|
||||
entity.map = Collections.singletonMap("collection",
|
||||
Collections.singletonList(Collections.singletonMap("destination", new InnerEntity("prop1", "prop2"))));
|
||||
|
||||
var expected = """
|
||||
{
|
||||
"_class": "org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverterUnitTests$1EntityWithMapCollectionMap",
|
||||
"map": {
|
||||
"collection": [
|
||||
{
|
||||
"destination": {
|
||||
"_class": "org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverterUnitTests$1InnerEntity",
|
||||
"prop1": "prop1",
|
||||
"prop2": "prop2"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
Document document = Document.create();
|
||||
|
||||
mappingElasticsearchConverter.write(entity, document);
|
||||
|
||||
assertEquals(expected, document.toJson(), false);
|
||||
}
|
||||
|
||||
@Nested
|
||||
class RangeTests {
|
||||
|
||||
static final String JSON = "{"
|
||||
+ "\"_class\":\"org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverterUnitTests$RangeTests$RangeEntity\","
|
||||
+ "\"integerRange\":{\"gt\":\"1\",\"lt\":\"10\"}," //
|
||||
+ "\"floatRange\":{\"gte\":\"1.2\",\"lte\":\"2.5\"}," //
|
||||
+ "\"longRange\":{\"gt\":\"2\",\"lte\":\"5\"}," //
|
||||
+ "\"doubleRange\":{\"gte\":\"3.2\",\"lt\":\"7.4\"}," //
|
||||
+ "\"dateRange\":{\"gte\":\"1970-01-01T00:00:00.000Z\",\"lte\":\"1970-01-01T01:00:00.000Z\"}," //
|
||||
+ "\"localDateRange\":{\"gte\":\"2021-07-06\"}," //
|
||||
+ "\"localTimeRange\":{\"gte\":\"00:30:00.000\",\"lt\":\"02:30:00.000\"}," //
|
||||
+ "\"localDateTimeRange\":{\"gt\":\"2021-01-01T00:30:00.000\",\"lt\":\"2021-01-01T02:30:00.000\"}," //
|
||||
+ "\"offsetTimeRange\":{\"gte\":\"00:30:00.000+02:00\",\"lt\":\"02:30:00.000+02:00\"}," //
|
||||
+ "\"zonedDateTimeRange\":{\"gte\":\"2021-01-01T00:30:00.000+02:00\",\"lte\":\"2021-01-01T00:30:00.000+02:00\"}," //
|
||||
+ "\"nullRange\":null}";
|
||||
static final String JSON = """
|
||||
{
|
||||
"_class": "org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverterUnitTests$RangeTests$RangeEntity",
|
||||
"integerRange": {
|
||||
"gt": "1",
|
||||
"lt": "10"
|
||||
},
|
||||
"floatRange": {
|
||||
"gte": "1.2",
|
||||
"lte": "2.5"
|
||||
},
|
||||
"longRange": {
|
||||
"gt": "2",
|
||||
"lte": "5"
|
||||
},
|
||||
"doubleRange": {
|
||||
"gte": "3.2",
|
||||
"lt": "7.4"
|
||||
},
|
||||
"dateRange": {
|
||||
"gte": "1970-01-01T00:00:00.000Z",
|
||||
"lte": "1970-01-01T01:00:00.000Z"
|
||||
},
|
||||
"localDateRange": {
|
||||
"gte": "2021-07-06"
|
||||
},
|
||||
"localTimeRange": {
|
||||
"gte": "00:30:00.000",
|
||||
"lt": "02:30:00.000"
|
||||
},
|
||||
"localDateTimeRange": {
|
||||
"gt": "2021-01-01T00:30:00.000",
|
||||
"lt": "2021-01-01T02:30:00.000"
|
||||
},
|
||||
"offsetTimeRange": {
|
||||
"gte": "00:30:00.000+02:00",
|
||||
"lt": "02:30:00.000+02:00"
|
||||
},
|
||||
"zonedDateTimeRange": {
|
||||
"gte": "2021-01-01T00:30:00.000+02:00",
|
||||
"lte": "2021-01-01T00:30:00.000+02:00"
|
||||
},
|
||||
"nullRange": null,
|
||||
"integerRangeList": [
|
||||
{
|
||||
"gte": "2",
|
||||
"lte": "5"
|
||||
}
|
||||
]
|
||||
}
|
||||
""";
|
||||
|
||||
@Test
|
||||
public void shouldReadRanges() throws JSONException {
|
||||
@@ -954,6 +1041,7 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
assertThat(e.getZonedDateTimeRange()).isEqualTo(
|
||||
Range.just(ZonedDateTime.of(LocalDate.of(2021, 1, 1), LocalTime.of(0, 30), ZoneOffset.ofHours(2))));
|
||||
assertThat(e.getNullRange()).isNull();
|
||||
assertThat(e.getIntegerRangeList()).containsExactly(Range.closed(2, 5));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -977,8 +1065,7 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
entity.setZonedDateTimeRange(
|
||||
Range.just(ZonedDateTime.of(LocalDate.of(2021, 1, 1), LocalTime.of(0, 30), ZoneOffset.ofHours(2))));
|
||||
entity.setNullRange(null);
|
||||
|
||||
// when
|
||||
entity.setIntegerRangeList(List.of(Range.closed(2, 5)));
|
||||
Document document = mappingElasticsearchConverter.mapObject(entity);
|
||||
|
||||
// then
|
||||
@@ -1003,6 +1090,8 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
@Field(type = FieldType.Date_Range) private Range<ZonedDateTime> zonedDateTimeRange;
|
||||
@Field(type = FieldType.Date_Range, storeNullValue = true) private Range<ZonedDateTime> nullRange;
|
||||
|
||||
@Field(type = FieldType.Integer_Range) private List<Range<Integer>> integerRangeList;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
@@ -1099,6 +1188,13 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
this.nullRange = nullRange;
|
||||
}
|
||||
|
||||
public List<Range<Integer>> getIntegerRangeList() {
|
||||
return integerRangeList;
|
||||
}
|
||||
|
||||
public void setIntegerRangeList(List<Range<Integer>> integerRangeList) {
|
||||
this.integerRangeList = integerRangeList;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -63,13 +63,14 @@ public class PropertyValueConvertersUnitTests {
|
||||
|
||||
converters.add(new DatePropertyValueConverter(persistentProperty,
|
||||
Collections.singletonList(ElasticsearchDateConverter.of(DateFormat.basic_date))));
|
||||
Class<?> genericType = Object.class;
|
||||
converters.add(new DateRangePropertyValueConverter(persistentProperty,
|
||||
Collections.singletonList(ElasticsearchDateConverter.of(DateFormat.basic_date))));
|
||||
converters.add(new NumberRangePropertyValueConverter(persistentProperty));
|
||||
genericType, Collections.singletonList(ElasticsearchDateConverter.of(DateFormat.basic_date))));
|
||||
converters.add(new NumberRangePropertyValueConverter(persistentProperty, genericType));
|
||||
converters.add(new TemporalPropertyValueConverter(persistentProperty,
|
||||
Collections.singletonList(ElasticsearchDateConverter.of(DateFormat.basic_date))));
|
||||
converters.add(new TemporalRangePropertyValueConverter(persistentProperty,
|
||||
Collections.singletonList(ElasticsearchDateConverter.of(DateFormat.basic_date))));
|
||||
genericType, Collections.singletonList(ElasticsearchDateConverter.of(DateFormat.basic_date))));
|
||||
|
||||
return converters.stream().map(propertyValueConverter -> arguments(
|
||||
Named.of(propertyValueConverter.getClass().getSimpleName(), propertyValueConverter)));
|
||||
|
||||
+2
-10
@@ -37,22 +37,14 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.DisabledIf;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.elasticsearch.NewElasticsearchClientDevelopment;
|
||||
import org.springframework.data.elasticsearch.annotations.DateFormat;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.Dynamic;
|
||||
import org.springframework.data.elasticsearch.annotations.Field;
|
||||
import org.springframework.data.elasticsearch.annotations.FieldType;
|
||||
import org.springframework.data.elasticsearch.annotations.InnerField;
|
||||
import org.springframework.data.elasticsearch.annotations.Mapping;
|
||||
import org.springframework.data.elasticsearch.annotations.MultiField;
|
||||
import org.springframework.data.elasticsearch.annotations.Setting;
|
||||
import org.springframework.data.elasticsearch.annotations.TermVector;
|
||||
import org.springframework.data.elasticsearch.annotations.*;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.core.IndexOperations;
|
||||
import org.springframework.data.elasticsearch.core.MappingContextBaseTests;
|
||||
|
||||
+81
-14
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+35
@@ -1590,6 +1590,41 @@ public abstract class CustomMethodRepositoryIntegrationTests implements NewElast
|
||||
assertThat(searchHits.getSearchHit(2).getId()).isEqualTo("oslo");
|
||||
}
|
||||
|
||||
@Test // #2601
|
||||
void shouldUseGeoSortReverseParameter() {
|
||||
GeoPoint munich = new GeoPoint(48.137154, 11.5761247);
|
||||
GeoPoint berlin = new GeoPoint(52.520008, 13.404954);
|
||||
GeoPoint vienna = new GeoPoint(48.20849, 16.37208);
|
||||
GeoPoint oslo = new GeoPoint(59.9127, 10.7461);
|
||||
|
||||
List<SampleEntity> entities = new ArrayList<>();
|
||||
|
||||
SampleEntity entity1 = new SampleEntity();
|
||||
entity1.setId("berlin");
|
||||
entity1.setLocation(berlin);
|
||||
entities.add(entity1);
|
||||
|
||||
SampleEntity entity2 = new SampleEntity();
|
||||
entity2.setId("vienna");
|
||||
entity2.setLocation(vienna);
|
||||
entities.add(entity2);
|
||||
|
||||
SampleEntity entity3 = new SampleEntity();
|
||||
entity3.setId("oslo");
|
||||
entity3.setLocation(oslo);
|
||||
entities.add(entity3);
|
||||
|
||||
repository.saveAll(entities);
|
||||
|
||||
SearchHits<SampleEntity> searchHits = repository
|
||||
.searchBy(Sort.by(new GeoDistanceOrder("location", munich).with(Sort.Direction.DESC)));
|
||||
|
||||
assertThat(searchHits.getTotalHits()).isEqualTo(3);
|
||||
assertThat(searchHits.getSearchHit(0).getId()).isEqualTo("oslo");
|
||||
assertThat(searchHits.getSearchHit(1).getId()).isEqualTo("berlin");
|
||||
assertThat(searchHits.getSearchHit(2).getId()).isEqualTo("vienna");
|
||||
}
|
||||
|
||||
@Test // DATAES-749
|
||||
void shouldReturnSearchPage() {
|
||||
List<SampleEntity> entities = createSampleEntities("abc", 20);
|
||||
|
||||
Reference in New Issue
Block a user