1
0
mirror of synced 2026-05-24 05:03:17 +00:00

Compare commits

..

46 Commits

Author SHA1 Message Date
Mark Paluch b5e7c3d012 Release version 5.0.12 (2022.0.12).
See #2735
2023-11-17 10:57:14 +01:00
Mark Paluch 2b8fbc18a2 Prepare 5.0.12 (2022.0.12).
See #2735
2023-11-17 10:57:00 +01:00
Peter-Josef Meisch f1f73a2992 Upgrade dependency of deprecated Elasticsearch client to 7.17.15.
Original Pull Request #2768
Closes #2764
2023-11-15 20:54:30 +01:00
Peter-Josef Meisch 50d606bfb8 Use routing info on delete operations.
Original Pull Request #2755
Closes #2754

(cherry picked from commit 9abcacb2e9)
(cherry picked from commit 64cf9566d9)
2023-11-06 22:17:30 +01:00
Peter-Josef Meisch 44b0ef0dac Fix reactive native sort.
Original Pull Request #2746
Closes #2745

(cherry picked from commit 73fe0869e6)
(cherry picked from commit 2c857178f4)
2023-10-23 22:07:55 +02:00
Peter-Josef Meisch 69329bc62d Fix field parameters in search request creation.
Original Pull Request #2739
Closes #2727

(cherry picked from commit f087d5aac3)
2023-10-14 11:52:45 +02:00
Mark Paluch aaaa18ec4c After release cleanups.
See #2698
2023-10-13 11:44:29 +02:00
Mark Paluch e9c2961133 Prepare next development iteration.
See #2698
2023-10-13 11:44:28 +02:00
Mark Paluch 08ed5b724d Release version 5.0.11 (2022.0.11).
See #2698
2023-10-13 11:41:41 +02:00
Mark Paluch 12b869e9dd Prepare 5.0.11 (2022.0.11).
See #2698
2023-10-13 11:41:28 +02:00
Peter-Josef Meisch d35dd313b1 Upgrade to Elasticsearch 7.17.14.
Original Pull Request #2733
2023-10-13 08:41:05 +02:00
John Blum f65d557689 Upgrade to Maven Wrapper 3.9.5.
See #2716
2023-10-04 18:24:26 -07:00
John Blum 1c4332dd12 Update CI properties.
See #2698
2023-10-04 17:47:35 -07:00
Peter-Josef Meisch b21bd796f6 Fix converting of Range<?> in Lists.
Original Pull Request #2707
Closes #2706

(cherry picked from commit 3330d65edf)
(cherry picked from commit d26d01bab1)
2023-09-23 13:21:50 +02:00
Mark Paluch 16eefb6f60 After release cleanups.
See #2673
2023-09-15 10:20:52 +02:00
Mark Paluch 7ebc4ee13a Prepare next development iteration.
See #2673
2023-09-15 10:20:50 +02:00
Mark Paluch a065ba6a76 Release version 5.0.10 (2022.0.10).
See #2673
2023-09-15 10:17:46 +02:00
Mark Paluch 631e5fe496 Prepare 5.0.10 (2022.0.10).
See #2673
2023-09-15 10:17:32 +02:00
Peter-Josef Meisch b1e6e51e16 Fix refresh policy in UpdateQuery.
Original Pull Request #2696
Closes #2692

(cherry picked from commit 2d0aee08ce)
(cherry picked from commit 572cc7ffea)
2023-09-13 21:55:31 +02:00
Peter-Josef Meisch d5df3bfd20 Upgrade deprecated client to Elasticsearch 7.17.13.
Original Pull Request #2695
Closes #2689
2023-09-13 20:05:41 +02:00
Peter-Josef Meisch 7f390845d3 Polishing.
(cherry picked from commit a82952b124)
(cherry picked from commit 4628908e84)
2023-09-03 16:04:19 +02:00
Sébastien Comeau 868a994e33 Fix: missing PhraseSuggestion.Entry.Option's score and collateMatch values.
Original Pull Request #2680
Closes #2681

(cherry picked from commit 7c466395c4)
(cherry picked from commit 922f4b1760)
2023-09-03 16:04:18 +02:00
Peter-Josef Meisch 7d9e6a5a2a Fix search_after field values (#2679)
Closes #2678

(cherry picked from commit 9adc4d2b36)
(cherry picked from commit 4614c62bb5)
2023-08-28 21:19:48 +02:00
Mark Paluch b34ed6fd2b After release cleanups.
See #2629
2023-08-18 10:46:31 +02:00
Mark Paluch ff4e07d350 Prepare next development iteration.
See #2629
2023-08-18 10:46:29 +02:00
Mark Paluch ede29781da Release version 5.0.9 (2022.0.9).
See #2629
2023-08-18 10:43:13 +02:00
Mark Paluch 53a7af879a Prepare 5.0.9 (2022.0.9).
See #2629
2023-08-18 10:42:57 +02:00
Julia Lee c7512fb325 Update CI properties.
See #2629
2023-08-14 10:19:15 -04:00
Julia Lee b4fde9d48b Upgrade to Maven Wrapper 3.9.4.
See #2668
2023-08-14 07:45:53 -04:00
Peter-Josef Meisch 8e56ef77b8 Polishing 2023-08-14 08:15:58 +02:00
Peter-Josef Meisch a2a4114d9c Fix cherrypicked code merge. 2023-08-14 00:01:24 +02:00
Peter-Josef Meisch ec8401a2fb Fix similarity field mapping.
Original Pull Request #2666
Closes #2659

(cherry picked from commit 8c5ff92cd2)
(cherry picked from commit 9b9136d852)

# Conflicts:
#	src/main/java/org/springframework/data/elasticsearch/core/index/MappingParameters.java
#	src/test/java/org/springframework/data/elasticsearch/core/index/MappingBuilderIntegrationTests.java
2023-08-13 21:43:38 +02:00
Peter-Josef Meisch 19ec05dae3 Upgrade deprecated dependency to Elasticsearch 7.17.12.
Original Pull Request #2654
Closes #2652
2023-07-30 19:24:34 +02:00
Peter-Josef Meisch 70d2de7eec Fix MappingElasticsearchConverter.
Original Pull Request #2637
Closes #2627

(cherry picked from commit d9bb9911f9)
(cherry picked from commit c045a8ae29)
2023-07-18 22:54:56 +02:00
Mark Paluch 1666fcee02 After release cleanups.
See #2594
2023-07-14 11:25:33 +02:00
Mark Paluch c513a0c03e Prepare next development iteration.
See #2594
2023-07-14 11:25:31 +02:00
Mark Paluch 22fe50c73a Release version 5.0.8 (2022.0.8).
See #2594
2023-07-14 11:22:39 +02:00
Mark Paluch ef90755c27 Prepare 5.0.8 (2022.0.8).
See #2594
2023-07-14 11:22:25 +02:00
Peter-Josef Meisch 9153d4dff4 Upgrade to Elasticsearch 7.17.11.
Original Pull Request #2624
Closes #2623
2023-07-12 19:12:01 +02:00
Mark Paluch 9109dfa257 Update CI properties.
See #2594
2023-07-03 09:47:08 +02:00
Mark Paluch 64eee02b85 Upgrade to Maven Wrapper 3.9.3.
See #2613
2023-07-03 09:46:39 +02:00
Peter-Josef Meisch 31365fb722 Fix IndicesBoost error.
Original Pull Request #2606
Closes #2598

(cherry picked from commit d9fd722bb6)
2023-06-27 22:28:38 +02:00
Greg L. Turnquist c218c1d5e3 Stop posting build status on internal Slack.
See #2603
2023-06-26 13:38:31 -05:00
seunghyun.cheong 5bce5dd880 Adding GeoDistanceOrder's direction in request.
Original Pull Request #2602
Closes #2601

(cherry picked from commit 8a164b1039)

Polishing

(cherry picked from commit b7570ffa95)

add implementation for old client

(cherry picked from commit d43b44ba9c)

# Conflicts:
#	src/main/java/org/springframework/data/elasticsearch/client/elc/RequestConverter.java
#	src/main/java/org/springframework/data/elasticsearch/client/erhlc/RequestFactory.java
2023-06-24 11:58:57 +02:00
Mark Paluch 32c4c4d03f After release cleanups.
See #2547
2023-06-16 15:43:11 +02:00
Mark Paluch 5102d98e10 Prepare next development iteration.
See #2547
2023-06-16 15:43:09 +02:00
30 changed files with 656 additions and 161 deletions
+2 -2
View File
@@ -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
View File
@@ -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',
+6 -6
View File
@@ -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
+4 -6
View File
@@ -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.
@@ -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 {
@@ -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 //
@@ -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.
*/
@@ -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.
@@ -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);
}
@@ -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;
}
@@ -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()) {
@@ -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
@@ -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) {
@@ -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: {
@@ -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);
});
}
}
@@ -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());
}
+6 -1
View File
@@ -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.
@@ -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();
}
}
@@ -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 {
@@ -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());
@@ -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() {
@@ -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;
}
}
}
@@ -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)));
@@ -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;
@@ -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;
}
}
@@ -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);