1
0
mirror of synced 2026-05-23 04:33:17 +00:00

Compare commits

...

26 Commits

Author SHA1 Message Date
Christoph Strobl 0a1d10f8b4 Release version 4.3.4 (2021.1.4).
See #2119
2022-04-19 12:03:16 +02:00
Christoph Strobl ef0e47c6bb Prepare 4.3.4 (2021.1.4).
See #2119
2022-04-19 12:02:46 +02:00
Mark Paluch 7e2ebb299c After release cleanups.
See #2091
2022-03-21 15:06:38 +01:00
Mark Paluch 594566a44a Prepare next development iteration.
See #2091
2022-03-21 15:06:36 +01:00
Mark Paluch bf3248b536 Release version 4.3.3 (2021.1.3).
See #2091
2022-03-21 14:58:52 +01:00
Mark Paluch c59bb0b434 Prepare 4.3.3 (2021.1.3).
See #2091
2022-03-21 14:58:28 +01:00
Peter-Josef Meisch d93d1e0738 Set visibility SimpleElasticsearchPersistentEntity.ContextConfiguration to public.
Original Pull Request #2116
Closes #2114

(cherry picked from commit 709b4c615e)
2022-03-21 09:31:08 +01:00
Mark Paluch aebdc8f86b Use Java 8 to build snapshots for Artifactory.
Closes #2095
2022-03-15 14:20:01 +01:00
Peter-Josef Meisch 2be27593d6 MappingBuilder must set configured date formats for date_range fields.
Original Pull Request #2103
Closes #2102

(cherry picked from commit bf080002bc)
2022-02-22 21:13:43 +01:00
Mark Paluch aa22d8239d Update CI properties.
See #2091
2022-02-22 14:09:14 +01:00
Mark Paluch 6746bc5278 Upgrade to Maven Wrapper 3.8.4.
See #2100
2022-02-22 13:56:16 +01:00
Mark Paluch a6cb959605 Polishing.
Fix Javadoc errors.

See #2095
2022-02-22 09:56:23 +01:00
Mark Paluch e04905a1a7 Use Java 17 to build snapshots for Artifactory.
Closes #2095
2022-02-22 08:53:04 +01:00
Peter-Josef Meisch 834b10f578 Remove blocking code in SearchDocument processing.
Original Pull Request #2094
Closes #2025

(cherry picked from commit c1a1ea9724)
2022-02-20 13:34:49 +01:00
Peter-Josef Meisch 823cfa919a keep the documentation change from the cherrypick 2022-02-18 20:51:46 +01:00
Peter-Josef Meisch ab29ae4219 Documentation about compatibility headers.
Original Pull Request #2093
Closes #2088

(cherry picked from commit cf380e289d)
2022-02-18 20:36:51 +01:00
Mark Paluch 62fb89208a After release cleanups.
See #2056
2022-02-18 10:49:02 +01:00
Mark Paluch e8c3badc56 Prepare next development iteration.
See #2056
2022-02-18 10:49:00 +01:00
Mark Paluch 187c9bbd5d Release version 4.3.2 (2021.1.2).
See #2056
2022-02-18 10:41:01 +01:00
Mark Paluch e1a5811406 Prepare 4.3.2 (2021.1.2).
See #2056
2022-02-18 10:40:40 +01:00
Greg L. Turnquist b643669d36 Update CI properties.
See #2056
2022-02-14 14:39:49 -06:00
Peter-Josef Meisch f66af53480 Fix exception on property conversion with criteria exists/empty/non-empty.
Original Pull Request #2081
Closes #2080

(cherry picked from commit 32fa7391c4)
2022-02-09 23:25:34 +01:00
Mark Paluch 14099970bb Polishing.
Extract Docker and Artifactory credentials into properties file.

See #2074
2022-02-04 15:18:50 +01:00
Greg L. Turnquist 0ab253422f Externalize build properties.
See #2074.
2022-02-04 15:18:48 +01:00
Christoph Strobl 0d2a6b98e8 After release cleanups.
See #1987
2022-01-14 10:45:02 +01:00
Christoph Strobl 30602496bd Prepare next development iteration.
See #1987
2022-01-14 10:44:59 +01:00
22 changed files with 279 additions and 122 deletions
+2 -2
View File
@@ -1,3 +1,3 @@
#Mon Oct 11 14:30:32 CEST 2021
#Tue Feb 22 13:56:16 CET 2022
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.8.3/apache-maven-3.8.3-bin.zip
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip
Vendored
+26 -48
View File
@@ -1,3 +1,9 @@
def p = [:]
node {
checkout scm
p = readProperties interpolate: true, file: 'ci/pipeline.properties'
}
pipeline {
agent none
@@ -12,7 +18,7 @@ pipeline {
}
stages {
stage("test: baseline (jdk8)") {
stage("test: baseline (main)") {
when {
anyOf {
branch 'main'
@@ -25,14 +31,14 @@ pipeline {
options { timeout(time: 30, unit: 'MINUTES') }
environment {
DOCKER_HUB = credentials('hub.docker.com-springbuildmaster')
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
DOCKER_HUB = credentials("${p['docker.credentials']}")
ARTIFACTORY = credentials("${p['artifactory.credentials']}")
}
steps {
script {
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
docker.image('adoptopenjdk/openjdk8:latest').inside('-u root -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v $HOME:/tmp/jenkins-home') {
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) {
sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}"
sh 'PROFILE=none ci/verify.sh'
sh "ci/clean.sh"
@@ -50,23 +56,23 @@ pipeline {
}
}
parallel {
stage("test: baseline (jdk11)") {
stage("test: baseline (next)") {
agent {
label 'data'
}
options { timeout(time: 30, unit: 'MINUTES') }
environment {
DOCKER_HUB = credentials('hub.docker.com-springbuildmaster')
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
DOCKER_HUB = credentials("${p['docker.credentials']}")
ARTIFACTORY = credentials("${p['artifactory.credentials']}")
}
steps {
script {
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
docker.image('adoptopenjdk/openjdk11:latest').inside('-u root -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v $HOME:/tmp/jenkins-home') {
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
docker.image(p['docker.java.next.image']).inside(p['docker.java.inside.docker']) {
sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}"
sh 'PROFILE=java11 ci/verify.sh'
sh 'PROFILE=none ci/verify.sh'
sh "ci/clean.sh"
}
}
@@ -74,23 +80,23 @@ pipeline {
}
}
stage("test: baseline (jdk17)") {
stage("test: baseline (LTS)") {
agent {
label 'data'
}
options { timeout(time: 30, unit: 'MINUTES') }
environment {
DOCKER_HUB = credentials('hub.docker.com-springbuildmaster')
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
DOCKER_HUB = credentials("${p['docker.credentials']}")
ARTIFACTORY = credentials("${p['artifactory.credentials']}")
}
steps {
script {
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
docker.image('openjdk:17-bullseye').inside('-u root -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v $HOME:/tmp/jenkins-home') {
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
docker.image(p['docker.java.lts.image']).inside(p['docker.java.inside.docker']) {
sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}"
sh 'PROFILE=java11 ci/verify.sh'
sh 'PROFILE=none ci/verify.sh'
sh "ci/clean.sh"
}
}
@@ -113,13 +119,13 @@ pipeline {
options { timeout(time: 20, unit: 'MINUTES') }
environment {
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
ARTIFACTORY = credentials("${p['artifactory.credentials']}")
}
steps {
script {
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
docker.image('adoptopenjdk/openjdk8:latest').inside('-v $HOME:/tmp/jenkins-home') {
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.basic']) {
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,artifactory -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-elasticsearch-non-root ' +
'-Dartifactory.server=https://repo.spring.io ' +
"-Dartifactory.username=${ARTIFACTORY_USR} " +
@@ -133,34 +139,6 @@ pipeline {
}
}
}
stage('Publish documentation') {
when {
branch 'main'
}
agent {
label 'data'
}
options { timeout(time: 20, unit: 'MINUTES') }
environment {
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
}
steps {
script {
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
docker.image('adoptopenjdk/openjdk8:latest').inside('-v $HOME:/tmp/jenkins-home') {
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,distribute -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-elasticsearch-non-root ' +
'-Dartifactory.server=https://repo.spring.io ' +
"-Dartifactory.username=${ARTIFACTORY_USR} " +
"-Dartifactory.password=${ARTIFACTORY_PSW} " +
"-Dartifactory.distribution-repository=temp-private-local " +
'-Dmaven.test.skip=true clean deploy -U -B'
}
}
}
}
}
}
post {
+29
View File
@@ -0,0 +1,29 @@
# Java versions
java.main.tag=8u322-b06-jdk
java.next.tag=11.0.14.1_1-jdk
java.lts.tag=17.0.2_8-jdk
# Docker container images - standard
docker.java.main.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.main.tag}
docker.java.next.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.next.tag}
docker.java.lts.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.lts.tag}
# Supported versions of MongoDB
docker.mongodb.4.0.version=4.0.28
docker.mongodb.4.4.version=4.4.12
docker.mongodb.5.0.version=5.0.6
# Supported versions of Redis
docker.redis.6.version=6.2.6
# Supported versions of Cassandra
docker.cassandra.3.version=3.11.12
# Docker environment settings
docker.java.inside.basic=-v $HOME:/tmp/jenkins-home
docker.java.inside.docker=-u root -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v $HOME:/tmp/jenkins-home
# Credentials
docker.registry=
docker.credentials=hub.docker.com-springbuildmaster
artifactory.credentials=02bd1690-b54f-4c9f-819d-a77cb7a9822c
+3 -3
View File
@@ -5,12 +5,12 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>4.3.1</version>
<version>4.3.4</version>
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>2.6.1</version>
<version>2.6.4</version>
</parent>
<name>Spring Data Elasticsearch</name>
@@ -21,7 +21,7 @@
<elasticsearch>7.15.2</elasticsearch>
<log4j>2.17.0</log4j>
<netty>4.1.65.Final</netty>
<springdata.commons>2.6.1</springdata.commons>
<springdata.commons>2.6.4</springdata.commons>
<testcontainers>1.15.3</testcontainers>
<blockhound-junit>1.0.6.RELEASE</blockhound-junit>
<java-module-name>spring.data.elasticsearch</java-module-name>
@@ -199,6 +199,29 @@ Default is 5 sec.
IMPORTANT: Adding a Header supplier as shown in above example allows to inject headers that may change over the time, like authentication JWT tokens.
If this is used in the reactive setup, the supplier function *must not* block!
=== Elasticsearch 7 compatibility headers
When using Spring Data Elasticsearch 4 - which uses the Elasticsearch 7 client libraries - and accessing an Elasticsearch cluster that is running on version 8, it is necessary to set the compatibility headers
https://www.elastic.co/guide/en/elasticsearch/reference/8.0/rest-api-compatibility.html[see Elasticserach documentation].
This should be done using a header supplier like shown above:
====
[source,java]
----
ClientConfigurationBuilder configurationBuilder = new ClientConfigurationBuilder();
configurationBuilder //
// ...
.withHeaders(() -> {
HttpHeaders defaultCompatibilityHeaders = new HttpHeaders();
defaultCompatibilityHeaders.add("Accept",
"application/vnd.elasticsearch+json;compatible-with=7");
defaultCompatibilityHeaders.add("Content-Type",
"application/vnd.elasticsearch+json;compatible-with=7");
return defaultCompatibilityHeaders;
});
----
====
[[elasticsearch.clients.logging]]
== Client Logging
@@ -44,7 +44,7 @@ public @interface Document {
* Name of the Elasticsearch index.
* <ul>
* <li>Lowercase only</li>
* <li>Cannot include \, /, *, ?, ", <, >, |, ` ` (space character), ,, #</li>
* <li>Cannot include \, /, *, ?, ", &gt;, &lt;, |, ` ` (space character), ,, #</li>
* <li>Cannot start with -, _, +</li>
* <li>Cannot be . or ..</li>
* <li>Cannot be longer than 255 bytes (note it is bytes, so multi-byte characters will count towards the 255 limit
@@ -20,6 +20,7 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -109,7 +110,8 @@ public abstract class AbstractElasticsearchRestTransportTemplate extends Abstrac
List<SearchHits<T>> res = new ArrayList<>(queries.size());
int c = 0;
for (Query query : queries) {
res.add(callback.doWith(SearchDocumentResponse.from(items[c++].getResponse(), documentCallback::doWith)));
res.add(
callback.doWith(SearchDocumentResponse.from(items[c++].getResponse(), getEntityCreator(documentCallback))));
}
return res;
}
@@ -142,7 +144,7 @@ public abstract class AbstractElasticsearchRestTransportTemplate extends Abstrac
index);
SearchResponse response = items[c++].getResponse();
res.add(callback.doWith(SearchDocumentResponse.from(response, documentCallback::doWith)));
res.add(callback.doWith(SearchDocumentResponse.from(response, getEntityCreator(documentCallback))));
}
return res;
}
@@ -175,7 +177,7 @@ public abstract class AbstractElasticsearchRestTransportTemplate extends Abstrac
index);
SearchResponse response = items[c++].getResponse();
res.add(callback.doWith(SearchDocumentResponse.from(response, documentCallback::doWith)));
res.add(callback.doWith(SearchDocumentResponse.from(response, getEntityCreator(documentCallback))));
}
return res;
}
@@ -215,5 +217,9 @@ public abstract class AbstractElasticsearchRestTransportTemplate extends Abstrac
return suggest(suggestion, getIndexCoordinatesFor(clazz));
}
protected <T> SearchDocumentResponse.EntityCreator<T> getEntityCreator(ReadDocumentCallback<T> documentCallback) {
return searchDocument -> CompletableFuture.completedFuture(documentCallback.doWith(searchDocument));
}
// endregion
}
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2021 the original author or authors.
* Copyright 2013-2022 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.
@@ -319,7 +319,7 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchRestTranspor
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<T>(elasticsearchConverter, clazz, index);
SearchDocumentResponseCallback<SearchHits<T>> callback = new ReadSearchDocumentResponseCallback<>(clazz, index);
return callback.doWith(SearchDocumentResponse.from(response, documentCallback::doWith));
return callback.doWith(SearchDocumentResponse.from(response, getEntityCreator(documentCallback)));
}
@Override
@@ -336,7 +336,7 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchRestTranspor
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<T>(elasticsearchConverter, clazz, index);
SearchDocumentResponseCallback<SearchScrollHits<T>> callback = new ReadSearchScrollDocumentResponseCallback<>(clazz,
index);
return callback.doWith(SearchDocumentResponse.from(response, documentCallback::doWith));
return callback.doWith(SearchDocumentResponse.from(response, getEntityCreator(documentCallback)));
}
@Override
@@ -351,7 +351,7 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchRestTranspor
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<T>(elasticsearchConverter, clazz, index);
SearchDocumentResponseCallback<SearchScrollHits<T>> callback = new ReadSearchScrollDocumentResponseCallback<>(clazz,
index);
return callback.doWith(SearchDocumentResponse.from(response, documentCallback::doWith));
return callback.doWith(SearchDocumentResponse.from(response, getEntityCreator(documentCallback)));
}
@Override
@@ -378,8 +378,8 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchRestTranspor
Assert.isTrue(items.length == request.requests().size(), "Response should has same length with queries");
return items;
}
// endregion
// endregion
// region ClientCallback
/**
* Callback interface to be used with {@link #execute(ClientCallback)} for operating directly on
@@ -354,7 +354,7 @@ public class ElasticsearchTemplate extends AbstractElasticsearchRestTransportTem
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<T>(elasticsearchConverter, clazz, index);
SearchDocumentResponseCallback<SearchHits<T>> callback = new ReadSearchDocumentResponseCallback<>(clazz, index);
return callback.doWith(SearchDocumentResponse.from(response, documentCallback::doWith));
return callback.doWith(SearchDocumentResponse.from(response, getEntityCreator(documentCallback)));
}
@Override
@@ -372,7 +372,7 @@ public class ElasticsearchTemplate extends AbstractElasticsearchRestTransportTem
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<T>(elasticsearchConverter, clazz, index);
SearchDocumentResponseCallback<SearchScrollHits<T>> callback = new ReadSearchScrollDocumentResponseCallback<>(clazz,
index);
return callback.doWith(SearchDocumentResponse.from(response, documentCallback::doWith));
return callback.doWith(SearchDocumentResponse.from(response, getEntityCreator(documentCallback)));
}
@Override
@@ -389,7 +389,7 @@ public class ElasticsearchTemplate extends AbstractElasticsearchRestTransportTem
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<T>(elasticsearchConverter, clazz, index);
SearchDocumentResponseCallback<SearchScrollHits<T>> callback = new ReadSearchScrollDocumentResponseCallback<>(clazz,
index);
return callback.doWith(SearchDocumentResponse.from(response, documentCallback::doWith));
return callback.doWith(SearchDocumentResponse.from(response, getEntityCreator(documentCallback)));
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2018-2021 the original author or authors.
* Copyright 2018-2022 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.
@@ -23,7 +23,6 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.elasticsearch.Version;
@@ -771,15 +770,18 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
});
}
private Mono<SearchDocumentResponse> doFindForResponse(Query query, Class<?> clazz, IndexCoordinates index) {
private <T> Mono<SearchDocumentResponse> doFindForResponse(Query query, Class<?> clazz, IndexCoordinates index) {
return Mono.defer(() -> {
SearchRequest request = requestFactory.searchRequest(query, clazz, index);
request = prepareSearchRequest(request, false);
SearchDocumentCallback<?> documentCallback = new ReadSearchDocumentCallback<>(clazz, index);
// noinspection unchecked
SearchDocumentResponse.EntityCreator<T> entityCreator = searchDocument -> ((Mono<T>) documentCallback
.toEntity(searchDocument)).toFuture();
return doFindForResponse(request, searchDocument -> documentCallback.toEntity(searchDocument).block());
return doFindForResponse(request, entityCreator);
});
}
@@ -896,19 +898,18 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
* Customization hook on the actual execution result {@link Mono}. <br />
*
* @param request the already prepared {@link SearchRequest} ready to be executed.
* @param suggestEntityCreator
* @param entityCreator
* @return a {@link Mono} emitting the result of the operation converted to s {@link SearchDocumentResponse}.
*/
protected Mono<SearchDocumentResponse> doFindForResponse(SearchRequest request,
Function<SearchDocument, ? extends Object> suggestEntityCreator) {
protected <T> Mono<SearchDocumentResponse> doFindForResponse(SearchRequest request,
SearchDocumentResponse.EntityCreator<T> entityCreator) {
if (QUERY_LOGGER.isDebugEnabled()) {
QUERY_LOGGER.debug("Executing doFindForResponse: {}", request);
}
return Mono.from(execute(client1 -> client1.searchForResponse(request))).map(searchResponse -> {
return SearchDocumentResponse.from(searchResponse, suggestEntityCreator);
});
return Mono.from(execute(client -> client.searchForResponse(request)))
.map(searchResponse -> SearchDocumentResponse.from(searchResponse, entityCreator));
}
/**
@@ -123,7 +123,6 @@ public interface ReactiveSearchOperations {
/**
* Search the index for entities matching the given {@link Query query}.
*
* @param <T>
* @param query must not be {@literal null}.
* @param entityType must not be {@literal null}.
* @param <T>
@@ -137,7 +136,6 @@ public interface ReactiveSearchOperations {
/**
* Search the index for entities matching the given {@link Query query}.
*
* @param <T>
* @param query must not be {@literal null}.
* @param entityType must not be {@literal null}.
* @param resultType the projection result type.
@@ -150,7 +148,6 @@ public interface ReactiveSearchOperations {
/**
* Search the index for entities matching the given {@link Query query}.
*
* @param <T>
* @param query must not be {@literal null}.
* @param entityType must not be {@literal null}.
* @param index the target index, must not be {@literal null}
@@ -165,7 +162,6 @@ public interface ReactiveSearchOperations {
/**
* Search the index for entities matching the given {@link Query query}.
*
* @param <T>
* @param query must not be {@literal null}.
* @param entityType must not be {@literal null}.
* @param resultType the projection result type.
@@ -1256,14 +1256,19 @@ public class MappingElasticsearchConverter
PropertyValueConverter propertyValueConverter = Objects
.requireNonNull(persistentProperty.getPropertyValueConverter());
criteria.getQueryCriteriaEntries().forEach(criteriaEntry -> {
Object value = criteriaEntry.getValue();
if (value.getClass().isArray()) {
Object[] objects = (Object[]) value;
for (int i = 0; i < objects.length; i++) {
objects[i] = propertyValueConverter.write(objects[i]);
if (criteriaEntry.getKey().hasValue()) {
Object value = criteriaEntry.getValue();
if (value.getClass().isArray()) {
Object[] objects = (Object[]) value;
for (int i = 0; i < objects.length; i++) {
objects[i] = propertyValueConverter.write(objects[i]);
}
} else {
criteriaEntry.setValue(propertyValueConverter.write(value));
}
} else {
criteriaEntry.setValue(propertyValueConverter.write(value));
}
});
}
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2021 the original author or authors.
* Copyright 2019-2022 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.
@@ -17,8 +17,11 @@ package org.springframework.data.elasticsearch.core.document;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
@@ -38,13 +41,15 @@ import org.springframework.util.Assert;
/**
* This represents the complete search response from Elasticsearch, including the returned documents. Instances must be
* created with the {@link #from(SearchResponse,Function)} method.
* created with the {@link #from(SearchResponse, EntityCreator)} method.
*
* @author Peter-Josef Meisch
* @since 4.0
*/
public class SearchDocumentResponse {
private static final Log LOGGER = LogFactory.getLog(SearchDocumentResponse.class);
private final long totalHits;
private final String totalHitsRelation;
private final float maxScore;
@@ -98,12 +103,11 @@ public class SearchDocumentResponse {
* creates a SearchDocumentResponse from the {@link SearchResponse}
*
* @param searchResponse must not be {@literal null}
* @param suggestEntityCreator function to create an entity from a {@link SearchDocument}
* @param entityCreator function to create an entity from a {@link SearchDocument}
* @param <T> entity type
* @return the SearchDocumentResponse
*/
public static <T> SearchDocumentResponse from(SearchResponse searchResponse,
Function<SearchDocument, T> suggestEntityCreator) {
public static <T> SearchDocumentResponse from(SearchResponse searchResponse, EntityCreator<T> entityCreator) {
Assert.notNull(searchResponse, "searchResponse must not be null");
@@ -112,7 +116,7 @@ public class SearchDocumentResponse {
Aggregations aggregations = searchResponse.getAggregations();
org.elasticsearch.search.suggest.Suggest suggest = searchResponse.getSuggest();
return from(searchHits, scrollId, aggregations, suggest, suggestEntityCreator);
return from(searchHits, scrollId, aggregations, suggest, entityCreator);
}
/**
@@ -122,14 +126,14 @@ public class SearchDocumentResponse {
* @param scrollId scrollId
* @param aggregations aggregations
* @param suggestES the suggestion response from Elasticsearch
* @param suggestEntityCreator function to create an entity from a {@link SearchDocument}
* @param entityCreator function to create an entity from a {@link SearchDocument}
* @param <T> entity type
* @return the {@link SearchDocumentResponse}
* @since 4.3
*/
public static <T> SearchDocumentResponse from(SearchHits searchHits, @Nullable String scrollId,
@Nullable Aggregations aggregations, @Nullable org.elasticsearch.search.suggest.Suggest suggestES,
Function<SearchDocument, T> suggestEntityCreator) {
EntityCreator<T> entityCreator) {
TotalHits responseTotalHits = searchHits.getTotalHits();
@@ -153,14 +157,14 @@ public class SearchDocumentResponse {
}
}
Suggest suggest = suggestFrom(suggestES, suggestEntityCreator);
Suggest suggest = suggestFrom(suggestES, entityCreator);
return new SearchDocumentResponse(totalHits, totalHitsRelation, maxScore, scrollId, searchDocuments, aggregations,
suggest);
}
@Nullable
private static <T> Suggest suggestFrom(@Nullable org.elasticsearch.search.suggest.Suggest suggestES,
Function<SearchDocument, T> entityCreator) {
EntityCreator<T> entityCreator) {
if (suggestES == null) {
return null;
@@ -219,7 +223,19 @@ public class SearchDocumentResponse {
List<CompletionSuggestion.Entry.Option<T>> options = new ArrayList<>();
for (org.elasticsearch.search.suggest.completion.CompletionSuggestion.Entry.Option optionES : entryES) {
SearchDocument searchDocument = optionES.getHit() != null ? DocumentAdapters.from(optionES.getHit()) : null;
T hitEntity = searchDocument != null ? entityCreator.apply(searchDocument) : null;
T hitEntity = null;
if (searchDocument != null) {
try {
hitEntity = entityCreator.apply(searchDocument).get();
} catch (Exception e) {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn("Error creating entity from SearchDocument");
}
}
}
options.add(new CompletionSuggestion.Entry.Option<T>(textToString(optionES.getText()),
textToString(optionES.getHighlighted()), optionES.getScore(), optionES.collateMatch(),
optionES.getContexts(), scoreDocFrom(optionES.getDoc()), searchDocument, hitEntity));
@@ -254,4 +270,14 @@ public class SearchDocumentResponse {
private static String textToString(@Nullable Text text) {
return text != null ? text.string() : "";
}
/**
* A function to convert a {@link SearchDocument} async into an entity. Asynchronous so that it can be used from the
* imperative and the reactive code.
*
* @param <T> the entity type
*/
@FunctionalInterface
public interface EntityCreator<T> extends Function<SearchDocument, CompletableFuture<T>> {}
}
@@ -20,7 +20,7 @@ import org.springframework.data.elasticsearch.core.convert.GeoConverters;
import org.springframework.data.elasticsearch.core.document.Document;
/**
* Interface definition for structures defined in <a href="https://geojson.org/>GeoJSON</a> format. copied from Spring
* Interface definition for structures defined in <a href="https://geojson.org/">GeoJSON</a> format. copied from Spring
* Data Mongodb
*
* @author Christoph Strobl
@@ -239,7 +239,7 @@ public final class MappingParameters {
if (type != FieldType.Auto) {
objectNode.put(FIELD_PARAM_TYPE, type.getMappedName());
if (type == FieldType.Date) {
if (type == FieldType.Date || type == FieldType.Date_Nanos || type == FieldType.Date_Range) {
List<String> formats = new ArrayList<>();
// built-in formats
@@ -172,7 +172,7 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
@Override
public boolean writeTypeHints() {
boolean writeTypeHints = contextConfiguration.writeTypeHints;
boolean writeTypeHints = contextConfiguration.getWriteTypeHints();
if (document != null) {
switch (document.writeTypeHint()) {
@@ -548,7 +548,7 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
/**
* Configuration settings passed in from the creating {@link SimpleElasticsearchMappingContext}.
*/
static class ContextConfiguration {
public static class ContextConfiguration {
private final FieldNamingStrategy fieldNamingStrategy;
private final boolean writeTypeHints;
@@ -561,6 +561,10 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
public FieldNamingStrategy getFieldNamingStrategy() {
return fieldNamingStrategy;
}
public boolean getWriteTypeHints() {
return writeTypeHints;
}
}
@Override
@@ -954,7 +954,23 @@ public class Criteria {
/**
* @since 4.3
*/
NOT_EMPTY
NOT_EMPTY;
/**
* @return true if this key does not have an associated value
* @since 4.4
*/
public boolean hasNoValue() {
return this == OperationKey.EXISTS || this == OperationKey.EMPTY || this == OperationKey.NOT_EMPTY;
}
/**
* @return true if this key does have an associated value
* @since 4.4
*/
public boolean hasValue() {
return !hasNoValue();
}
}
/**
@@ -967,9 +983,8 @@ public class Criteria {
protected CriteriaEntry(OperationKey key) {
boolean keyIsValid = key == OperationKey.EXISTS || key == OperationKey.EMPTY || key == OperationKey.NOT_EMPTY;
Assert.isTrue(keyIsValid,
"key must be OperationKey.EXISTS, OperationKey.EMPTY or OperationKey.EMPTY for this call");
Assert.isTrue(key.hasNoValue(),
"key must be OperationKey.EXISTS, OperationKey.EMPTY or OperationKey.NOT_EMPTY for this call");
this.key = key;
}
@@ -30,7 +30,7 @@ import org.springframework.lang.Nullable;
* @author Mohsin Husen
* @author Peter-Josef Meisch
* @author Farid Faoudi
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update.html>docs</a>
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update.html">docs</a>
*/
public class UpdateQuery {
+4 -1
View File
@@ -1,4 +1,4 @@
Spring Data Elasticsearch 4.3.1 (2021.1.1)
Spring Data Elasticsearch 4.3.4 (2021.1.4)
Copyright (c) [2013-2021] Pivotal Software, Inc.
This product is licensed to you under the Apache License, Version 2.0 (the "License").
@@ -29,6 +29,9 @@ conditions of the subcomponent's license, as noted in the LICENSE file.
@@ -66,6 +66,9 @@ import org.springframework.data.elasticsearch.core.geo.GeoJsonPolygon;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.data.elasticsearch.core.mapping.PropertyValueConverter;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm;
import org.springframework.data.geo.Box;
import org.springframework.data.geo.Circle;
@@ -1496,6 +1499,16 @@ public class MappingElasticsearchConverterUnitTests {
assertThat(entity.getDontConvert()).isEqualTo("Monty Python's Flying Circus");
}
@Test // #2080
@DisplayName("should not try to call property converter on updating criteria exists")
void shouldNotTryToCallPropertyConverterOnUpdatingCriteriaExists() {
// don't care if the query makes no sense, we just add all criteria without values
Query query = new CriteriaQuery(Criteria.where("fieldWithClassBasedConverter").exists().empty().notEmpty());
mappingElasticsearchConverter.updateQuery(query, EntityWithCustomValueConverters.class);
}
private Map<String, Object> writeToMap(Object source) {
Document sink = Document.create();
@@ -2429,6 +2442,7 @@ public class MappingElasticsearchConverterUnitTests {
return reverse(value);
}
}
// endregion
private static String reverse(Object o) {
@@ -2436,5 +2450,4 @@ public class MappingElasticsearchConverterUnitTests {
return new StringBuilder().append(o.toString()).reverse().toString();
}
// endregion
}
@@ -42,6 +42,7 @@ import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.elasticsearch.annotations.*;
import org.springframework.data.elasticsearch.core.MappingContextBaseTests;
import org.springframework.data.elasticsearch.core.Range;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
import org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm;
@@ -721,6 +722,29 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
assertEquals(expected, mapping, false);
}
@Test // #2102
@DisplayName("should write date formats for date range fields")
void shouldWriteDateFormatsForDateRangeFields() throws JSONException {
String expected = "{\n" + //
" \"properties\": {\n" + //
" \"_class\": {\n" + //
" \"type\": \"keyword\",\n" + //
" \"index\": false,\n" + //
" \"doc_values\": false\n" + //
" },\n" + //
" \"field2\": {\n" + //
" \"type\": \"date_range\",\n" + //
" \"format\": \"date\"\n" + //
" }\n" + //
" }\n" + //
"}\n"; //
String mapping = getMappingBuilder().buildPropertyMapping(DateRangeEntity.class);
assertEquals(expected, mapping, false);
}
@Test // #1454
@DisplayName("should write type hints when context is configured to do so")
void shouldWriteTypeHintsWhenContextIsConfiguredToDoSo() throws JSONException {
@@ -1911,6 +1935,31 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
}
}
private static class DateRangeEntity {
@Nullable
@Id private String id;
@Nullable
@Field(type = Date_Range, format = DateFormat.date) private Range<LocalDateTime> field2;
@Nullable
public String getId() {
return id;
}
public void setId(@Nullable String id) {
this.id = id;
}
@Nullable
public Range<LocalDateTime> getField2() {
return field2;
}
public void setField2(@Nullable Range<LocalDateTime> field2) {
this.field2 = field2;
}
}
@Document(indexName = "magazine")
private static class Magazine {
@Id @Nullable private String id;
@@ -1,5 +1,5 @@
/*
* Copyright 2021 the original author or authors.
* Copyright 2021-2022 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.
@@ -17,6 +17,7 @@ package org.springframework.data.elasticsearch.core.suggest;
import static org.assertj.core.api.Assertions.*;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.util.ArrayList;
@@ -39,8 +40,8 @@ import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.suggest.response.CompletionSuggestion;
import org.springframework.data.elasticsearch.core.suggest.response.Suggest;
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchRestTemplateConfiguration;
@@ -86,13 +87,11 @@ public class ReactiveElasticsearchTemplateSuggestIntegrationTests {
@DisplayName("should find suggestions for given prefix completion")
void shouldFindSuggestionsForGivenPrefixCompletion() {
loadCompletionObjectEntities();
NativeSearchQuery query = new NativeSearchQueryBuilder().withSuggestBuilder(new SuggestBuilder()
.addSuggestion("test-suggest", SuggestBuilders.completionSuggestion("suggest").prefix("m", Fuzziness.AUTO)))
.build();
operations.suggest(query, CompletionEntity.class) //
loadCompletionObjectEntities() //
.flatMap(unused -> {
Query query = getSuggestQuery("test-suggest", "suggest", "m");
return operations.suggest(query, CompletionEntity.class);
}) //
.as(StepVerifier::create) //
.assertNext(suggest -> {
Suggest.Suggestion<? extends Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option>> suggestion = suggest
@@ -105,13 +104,21 @@ public class ReactiveElasticsearchTemplateSuggestIntegrationTests {
assertThat(options).hasSize(2);
assertThat(options.get(0).getText()).isIn("Marchand", "Mohsin");
assertThat(options.get(1).getText()).isIn("Marchand", "Mohsin");
}) //
.verifyComplete();
}
protected Query getSuggestQuery(String suggestionName, String fieldName, String prefix) {
return new NativeSearchQueryBuilder() //
.withSuggestBuilder(new SuggestBuilder() //
.addSuggestion(suggestionName, //
SuggestBuilders.completionSuggestion(fieldName) //
.prefix(prefix, Fuzziness.AUTO))) //
.build(); //
}
// region helper functions
private void loadCompletionObjectEntities() {
private Mono<CompletionEntity> loadCompletionObjectEntities() {
CompletionEntity rizwan_idrees = new CompletionEntityBuilder("1").name("Rizwan Idrees")
.suggest(new String[] { "Rizwan Idrees" }).build();
@@ -124,7 +131,7 @@ public class ReactiveElasticsearchTemplateSuggestIntegrationTests {
List<CompletionEntity> entities = new ArrayList<>(
Arrays.asList(rizwan_idrees, franck_marchand, mohsin_husen, artur_konczak));
IndexCoordinates index = IndexCoordinates.of(indexNameProvider.indexName());
operations.saveAll(entities, index).blockLast();
return operations.saveAll(entities, index).last();
}
// endregion
@@ -132,11 +139,13 @@ public class ReactiveElasticsearchTemplateSuggestIntegrationTests {
@Document(indexName = "#{@indexNameProvider.indexName()}")
static class CompletionEntity {
@Nullable @Id private String id;
@Nullable
@Id private String id;
@Nullable private String name;
@Nullable @CompletionField(maxInputLength = 100) private Completion suggest;
@Nullable
@CompletionField(maxInputLength = 100) private Completion suggest;
private CompletionEntity() {}