Compare commits

...

30 Commits
main ... 4.1.3

Author SHA1 Message Date
Christoph Strobl
bf32581105 Release version 4.1.3 (2020.0.3).
See #1572
2021-01-13 14:18:34 +01:00
Christoph Strobl
7f89d79b12 Prepare 4.1.3 (2020.0.3).
See #1572
2021-01-13 14:17:43 +01:00
Christoph Strobl
955eb77add Updated changelog.
See #1572
2021-01-13 14:17:43 +01:00
Peter-Josef Meisch
0086d35e54
#1634 - Update Testcontainers dependency.
Original Pull Request: #1635
Closes #1634

(cherry picked from commit 6913d8045bd6e9cc6dbeacccc7756fad987f30e6)
2021-01-07 22:13:38 +01:00
Greg L. Turnquist
53f0e79990
DATAES-996 - Use Docker hub credentials for all CI jobs. 2020-12-17 08:41:33 -06:00
Mark Paluch
3da9eaadc5
DATAES-973 - After release cleanups. 2020-12-09 16:46:55 +01:00
Mark Paluch
310f6f0f22
DATAES-973 - Prepare next development iteration. 2020-12-09 16:46:49 +01:00
Mark Paluch
262af9f0e8
DATAES-973 - Release version 4.1.2 (2020.0.2). 2020-12-09 16:01:27 +01:00
Mark Paluch
bc74126d7e
DATAES-973 - Prepare 4.1.2 (2020.0.2). 2020-12-09 16:00:56 +01:00
Mark Paluch
308de7f8db
DATAES-973 - Updated changelog. 2020-12-09 16:00:51 +01:00
Mark Paluch
ae5f72fb62
DATAES-966 - Updated changelog. 2020-12-09 15:33:31 +01:00
Mark Paluch
170facc3f3
DATAES-964 - Updated changelog. 2020-12-09 12:42:33 +01:00
Mark Paluch
0aaffebf16
DATAES-963 - Updated changelog. 2020-12-09 09:59:18 +01:00
Peter-Josef Meisch
d1da6ac4ed
DATAES-543 - Adjust configuration support classes so they do not require proxying.
Original PR: #557

(cherry picked from commit 54727229e1bfc8e88fdf45025b286c1e9bf29cfa)
2020-12-08 20:36:11 +01:00
Peter-Josef Meisch
133bc315ed
DATAES-990 - Index creation fails with Authentication object cannot be null on startup.
Only do a SpEL resolution if there is a SpEL expressin in the index name; resolve ExpressionDependencies.

Original PR: #565

(cherry picked from commit 6edb8353b5ccfe6ddf4fb28d6450e090e1373ed0)
2020-12-05 12:22:31 +01:00
Peter-Josef Meisch
9f243fd2c9
DATAES-991 - Wrong value for TermVector(with_positions_offets_payloads).
Original PR: #564

(cherry picked from commit 6a6ead5e1ec866812f7bf44af77e587851402ad1)
2020-12-04 08:41:46 +01:00
Peter-Josef Meisch
de57159c7f
DATAES-987 - IndexOperations getMapping fail when using index alias.
Original PR: #560

(cherry picked from commit 7912ae977945846347acbeb8a223ccd3fa64158b)
2020-11-26 07:22:41 +01:00
Peter-Josef Meisch
74ed69877d
DATAES-978 - Accept DateFormat.none for a date property to enable custom Converters.
Original pR: #556

(cherry picked from commit 04ceed29054d167a8b1bff90760f9d79048cd445)
2020-11-19 23:14:22 +01:00
Peter-Josef Meisch
7314bfc21d DATAES-972 - BeforeConvertCallback should be called before index query is built.
Originap PR: #555

(cherry picked from commit 98043348f7d466fe52ad933e4d90421fa756329f)
2020-11-16 13:44:37 +01:00
Peter-Josef Meisch
5909c19ead
DATAES-977 - Fix versions in reference documentation for 4.1. 2020-11-12 19:13:57 +01:00
Mark Paluch
9df27ef289
DATAES-965 - After release cleanups. 2020-11-11 12:14:54 +01:00
Mark Paluch
28d92359b2
DATAES-965 - Prepare next development iteration. 2020-11-11 12:14:51 +01:00
Mark Paluch
d8b0d526b4
DATAES-965 - Release version 4.1.1 (2020.0.1). 2020-11-11 11:59:00 +01:00
Mark Paluch
dc832e75a6
DATAES-965 - Prepare 4.1.1 (2020.0.1). 2020-11-11 11:58:36 +01:00
Mark Paluch
866cd96477
DATAES-965 - Updated changelog. 2020-11-11 11:58:33 +01:00
Peter-Josef Meisch
162c57df31
DATAES-969 - Use ResultProcessor in ElasticsearchPartQuery to build PartTree.
Original PR: #546

(cherry picked from commit d036693f0510748537c682a5ede99c23938b5250)
2020-11-07 18:28:59 +01:00
Mark Paluch
3600452796
DATAES-968 - Enable Maven caching for Jenkins jobs. 2020-10-30 08:36:23 +01:00
Mark Paluch
4b1c4c8000
DATAES-950 - Enable maintenance branch build. 2020-10-29 09:53:38 +01:00
Mark Paluch
70d556e526
DATAES-950 - After release cleanups. 2020-10-28 16:10:55 +01:00
Mark Paluch
214d91f3c1
DATAES-950 - Prepare next development iteration. 2020-10-28 16:10:52 +01:00
32 changed files with 672 additions and 301 deletions

View File

@ -9,7 +9,7 @@ image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-elasticsearch%2
Since this pipeline is purely Docker-based, it's easy to:
* Debug what went wrong on your local machine.
* Test out a a tweak to your `test.sh` script before sending it out.
* Test out a a tweak to your `verify.sh` script before sending it out.
* Experiment against a new image before submitting your pull request.
All of these use cases are great reasons to essentially run what the CI server does on your local machine.

141
Jenkinsfile vendored
View File

@ -3,7 +3,7 @@ pipeline {
triggers {
pollSCM 'H/10 * * * *'
upstream(upstreamProjects: "spring-data-commons/master", threshold: hudson.model.Result.SUCCESS)
upstream(upstreamProjects: "spring-data-commons/2.4.x", threshold: hudson.model.Result.SUCCESS)
}
options {
@ -15,71 +15,83 @@ pipeline {
stage("test: baseline (jdk8)") {
when {
anyOf {
branch 'master'
branch '4.1.x'
not { triggeredBy 'UpstreamCause' }
}
}
agent {
docker {
image 'adoptopenjdk/openjdk8:latest'
label 'data'
args '-v $HOME:/tmp/jenkins-home'
args '-u root -v /var/run/docker.sock:/var/run/docker.sock'
}
label 'data'
}
options { timeout(time: 30, unit: 'MINUTES') }
environment {
DOCKER_HUB = credentials('hub.docker.com-springbuildmaster')
}
steps {
sh 'mkdir -p /tmp/jenkins-home'
sh 'chown -R 1001:1001 .'
sh 'rm -rf ?'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw clean dependency:list verify -Dsort -U -B'
sh 'chown -R 1001:1001 .'
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') {
sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}"
sh 'PROFILE=none ci/verify.sh'
sh "ci/clean.sh"
}
}
}
}
}
stage("Test other configurations") {
when {
allOf {
branch 'master'
branch '4.1.x'
not { triggeredBy 'UpstreamCause' }
}
}
parallel {
stage("test: baseline (jdk11)") {
agent {
docker {
image 'adoptopenjdk/openjdk11:latest'
label 'data'
args '-v $HOME:/tmp/jenkins-home'
args '-u root -v /var/run/docker.sock:/var/run/docker.sock'
}
label 'data'
}
options { timeout(time: 30, unit: 'MINUTES') }
environment {
DOCKER_HUB = credentials('hub.docker.com-springbuildmaster')
}
steps {
sh 'mkdir -p /tmp/jenkins-home'
sh 'chown -R 1001:1001 .'
sh 'rm -rf ?'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pjava11 clean dependency:list verify -Dsort -U -B'
sh 'chown -R 1001:1001 .'
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') {
sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}"
sh 'PROFILE=java11 ci/verify.sh'
sh "ci/clean.sh"
}
}
}
}
}
stage("test: baseline (jdk15)") {
agent {
docker {
image 'adoptopenjdk/openjdk15:latest'
label 'data'
args '-v $HOME:/tmp/jenkins-home'
args '-u root -v /var/run/docker.sock:/var/run/docker.sock'
}
label 'data'
}
options { timeout(time: 30, unit: 'MINUTES') }
environment {
DOCKER_HUB = credentials('hub.docker.com-springbuildmaster')
}
steps {
sh 'mkdir -p /tmp/jenkins-home'
sh 'chown -R 1001:1001 .'
sh 'rm -rf ?'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pjava11 clean dependency:list verify -Dsort -U -B'
sh 'chown -R 1001:1001 .'
script {
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
docker.image('adoptopenjdk/openjdk15: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') {
sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}"
sh 'PROFILE=java11 ci/verify.sh'
sh "ci/clean.sh"
}
}
}
}
}
}
@ -88,16 +100,12 @@ pipeline {
stage('Release to artifactory') {
when {
anyOf {
branch 'master'
branch '4.1.x'
not { triggeredBy 'UpstreamCause' }
}
}
agent {
docker {
image 'adoptopenjdk/openjdk8:latest'
label 'data'
args '-v $HOME:/tmp/jenkins-home'
}
label 'data'
}
options { timeout(time: 20, unit: 'MINUTES') }
@ -106,27 +114,28 @@ pipeline {
}
steps {
sh 'rm -rf ?'
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pci,artifactory ' +
'-Dartifactory.server=https://repo.spring.io ' +
"-Dartifactory.username=${ARTIFACTORY_USR} " +
"-Dartifactory.password=${ARTIFACTORY_PSW} " +
"-Dartifactory.staging-repository=libs-snapshot-local " +
"-Dartifactory.build-name=spring-data-elasticsearch " +
"-Dartifactory.build-number=${BUILD_NUMBER} " +
'-Dmaven.test.skip=true clean deploy -U -B'
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 -Pci,artifactory -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.staging-repository=libs-snapshot-local " +
"-Dartifactory.build-name=spring-data-elasticsearch " +
"-Dartifactory.build-number=${BUILD_NUMBER} " +
'-Dmaven.test.skip=true clean deploy -U -B'
}
}
}
}
}
stage('Publish documentation') {
when {
branch 'master'
branch '4.1.x'
}
agent {
docker {
image 'adoptopenjdk/openjdk8:latest'
label 'data'
args '-v $HOME:/tmp/jenkins-home'
}
label 'data'
}
options { timeout(time: 20, unit: 'MINUTES') }
@ -135,12 +144,18 @@ pipeline {
}
steps {
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pci,distribute ' +
'-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'
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 -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'
}
}
}
}
}
}

6
ci/clean.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/bash -x
set -euo pipefail
MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" \
./mvnw clean -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-elasticsearch

10
ci/verify.sh Executable file
View File

@ -0,0 +1,10 @@
#!/bin/bash -x
set -euo pipefail
mkdir -p /tmp/jenkins-home/.m2/spring-data-elasticsearch
chown -R 1001:1001 .
MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" \
./mvnw \
-P${PROFILE} clean dependency:list verify -Dsort -U -B -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-elasticsearch

13
pom.xml
View File

@ -5,12 +5,12 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>4.1.0</version>
<version>4.1.3</version>
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>2.4.0</version>
<version>2.4.3</version>
</parent>
<name>Spring Data Elasticsearch</name>
@ -22,8 +22,8 @@
<elasticsearch>7.9.3</elasticsearch>
<log4j>2.13.3</log4j>
<netty>4.1.52.Final</netty>
<springdata.commons>2.4.0</springdata.commons>
<testcontainers>1.14.3</testcontainers>
<springdata.commons>2.4.3</springdata.commons>
<testcontainers>1.15.1</testcontainers>
<java-module-name>spring.data.elasticsearch</java-module-name>
</properties>
@ -415,6 +415,11 @@
<id>spring-plugins-release</id>
<url>https://repo.spring.io/plugins-release</url>
</pluginRepository>
<pluginRepository>
<id>bintray-plugins</id>
<name>bintray-plugins</name>
<url>https://jcenter.bintray.com</url>
</pluginRepository>
</pluginRepositories>
</project>

View File

@ -34,7 +34,7 @@ The following table shows the Elasticsearch versions that are used by Spring Dat
[cols="^,^,^,^",options="header"]
|===
| Spring Data Release Train |Spring Data Elasticsearch |Elasticsearch | Spring Boot
| 2020.0.0footnote:cdv[Currently in development] |4.1.xfootnote:cdv[]|7.9.3 |2.3.xfootnote:cdv[]
| 2020.0.0footnote:cdv[Currently in development] |4.1.xfootnote:cdv[]|7.9.3 |2.4.xfootnote:cdv[]
| Neumann | 4.0.x | 7.6.2 |2.3.x
| Moore | 3.2.x |6.8.12 | 2.2.x
| Lovelace | 3.1.x | 6.2.2 |2.1.x

View File

@ -58,13 +58,14 @@ Constructor arguments are mapped by name to the key values in the retrieved Docu
** `name`: The name of the field as it will be represented in the Elasticsearch document, if not set, the Java field name is used.
** `type`: the field type, can be one of _Text, Keyword, Long, Integer, Short, Byte, Double, Float, Half_Float, Scaled_Float, Date, Date_Nanos, Boolean, Binary, Integer_Range, Float_Range, Long_Range, Double_Range, Date_Range, Ip_Range, Object, Nested, Ip, TokenCount, Percolator, Flattened, Search_As_You_Type_.
See https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html[Elasticsearch Mapping Types]
** `format` and `pattern` definitions for the _Date_ type. `format` must be defined for date types.
** `format` and `pattern` definitions for the _Date_ type.
** `store`: Flag whether the original field value should be store in Elasticsearch, default value is _false_.
** `analyzer`, `searchAnalyzer`, `normalizer` for specifying custom analyzers and normalizer.
* `@GeoPoint`: marks a field as _geo_point_ datatype.
Can be omitted if the field is an instance of the `GeoPoint` class.
NOTE: Properties that derive from `TemporalAccessor` must either have a `@Field` annotation of type `FieldType.Date` or a custom converter must be registered for this type. +
NOTE: Properties that derive from `TemporalAccessor` or are of type `java.util.Date` must either have a `@Field` annotation of type `FieldType.Date` and a
format different from `DateFormat.none` or a custom converter must be registered for this type. +
If you are using a custom date format, you need to use _uuuu_ for the year instead of _yyyy_.
This is due to a https://www.elastic.co/guide/en/elasticsearch/reference/current/migrate-to-java-time.html#java-time-migration-incompatible-date-formats[change in Elasticsearch 7].

View File

@ -20,5 +20,5 @@ package org.springframework.data.elasticsearch.annotations;
* @since 4.0
*/
public enum TermVector {
none, no, yes, with_positions, with_offsets, with_positions_offsets, with_positions_payloads, with_positions_offets_payloads
none, no, yes, with_positions, with_offsets, with_positions_offsets, with_positions_payloads, with_positions_offsets_payloads
}

View File

@ -31,20 +31,25 @@ public abstract class AbstractElasticsearchConfiguration extends ElasticsearchCo
/**
* Return the {@link RestHighLevelClient} instance used to connect to the cluster. <br />
* Annotate with {@link Bean} in case you want to expose a {@link RestHighLevelClient} instance to the
* {@link org.springframework.context.ApplicationContext}.
*
* @return never {@literal null}.
*/
@Bean
public abstract RestHighLevelClient elasticsearchClient();
/**
* Creates {@link ElasticsearchOperations}.
*
* @return never {@literal null}.
*/
/**
* Creates {@link ElasticsearchOperations}. <br/>
* NOTE: in version 4.1.2 the second parameter was added, previously this implementation called
* {@link #elasticsearchClient()} directly. This is not possible anymore, as the base configuration classes don not
* use proxied bean methods anymore.
*
* @param elasticsearchConverter the {@link ElasticsearchConverter} to use*
* @param elasticsearchClient the {@link RestHighLevelClient} to use
* @return never {@literal null}.
*/
@Bean(name = { "elasticsearchOperations", "elasticsearchTemplate" })
public ElasticsearchOperations elasticsearchOperations(ElasticsearchConverter elasticsearchConverter) {
return new ElasticsearchRestTemplate(elasticsearchClient(), elasticsearchConverter);
public ElasticsearchOperations elasticsearchOperations(ElasticsearchConverter elasticsearchConverter,
RestHighLevelClient elasticsearchClient) {
return new ElasticsearchRestTemplate(elasticsearchClient, elasticsearchConverter);
}
}

View File

@ -18,7 +18,6 @@ package org.springframework.data.elasticsearch.config;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.support.WriteRequest.RefreshPolicy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchTemplate;
@ -31,16 +30,14 @@ import org.springframework.lang.Nullable;
* @since 3.2
* @see ElasticsearchConfigurationSupport
*/
@Configuration
public abstract class AbstractReactiveElasticsearchConfiguration extends ElasticsearchConfigurationSupport {
/**
* Return the {@link ReactiveElasticsearchClient} instance used to connect to the cluster. <br />
* Annotate with {@link Bean} in case you want to expose a {@link ReactiveElasticsearchClient} instance to the
* {@link org.springframework.context.ApplicationContext}.
*
* @return never {@literal null}.
*/
@Bean
public abstract ReactiveElasticsearchClient reactiveElasticsearchClient();
/**
@ -49,9 +46,10 @@ public abstract class AbstractReactiveElasticsearchConfiguration extends Elastic
* @return never {@literal null}.
*/
@Bean
public ReactiveElasticsearchOperations reactiveElasticsearchTemplate(ElasticsearchConverter elasticsearchConverter) {
public ReactiveElasticsearchOperations reactiveElasticsearchTemplate(ElasticsearchConverter elasticsearchConverter,
ReactiveElasticsearchClient reactiveElasticsearchClient) {
ReactiveElasticsearchTemplate template = new ReactiveElasticsearchTemplate(reactiveElasticsearchClient(),
ReactiveElasticsearchTemplate template = new ReactiveElasticsearchTemplate(reactiveElasticsearchClient,
elasticsearchConverter);
template.setIndicesOptions(indicesOptions());
template.setRefreshPolicy(refreshPolicy());

View File

@ -40,15 +40,16 @@ import org.springframework.util.StringUtils;
* @author Peter-Josef Meisch
* @since 3.2
*/
@Configuration
@Configuration(proxyBeanMethods = false)
public class ElasticsearchConfigurationSupport {
@Bean
public ElasticsearchConverter elasticsearchEntityMapper(
SimpleElasticsearchMappingContext elasticsearchMappingContext) {
SimpleElasticsearchMappingContext elasticsearchMappingContext, ElasticsearchCustomConversions elasticsearchCustomConversions) {
MappingElasticsearchConverter elasticsearchConverter = new MappingElasticsearchConverter(
elasticsearchMappingContext);
elasticsearchConverter.setConversions(elasticsearchCustomConversions());
elasticsearchConverter.setConversions(elasticsearchCustomConversions);
return elasticsearchConverter;
}
@ -60,11 +61,11 @@ public class ElasticsearchConfigurationSupport {
* @return never {@literal null}.
*/
@Bean
public SimpleElasticsearchMappingContext elasticsearchMappingContext() {
public SimpleElasticsearchMappingContext elasticsearchMappingContext(ElasticsearchCustomConversions elasticsearchCustomConversions) {
SimpleElasticsearchMappingContext mappingContext = new SimpleElasticsearchMappingContext();
mappingContext.setInitialEntitySet(getInitialEntitySet());
mappingContext.setSimpleTypeHolder(elasticsearchCustomConversions().getSimpleTypeHolder());
mappingContext.setSimpleTypeHolder(elasticsearchCustomConversions.getSimpleTypeHolder());
return mappingContext;
}

View File

@ -147,13 +147,14 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
Assert.notNull(entity, "entity must not be null");
Assert.notNull(index, "index must not be null");
IndexQuery query = getIndexQuery(entity);
index(query, index);
T entityAfterBeforeConvert = maybeCallbackBeforeConvert(entity, index);
// suppressing because it's either entity itself or something of a correct type returned by an entity callback
@SuppressWarnings("unchecked")
T castResult = (T) query.getObject();
return castResult;
IndexQuery query = getIndexQuery(entityAfterBeforeConvert);
doIndex(query, index);
T entityAfterAfterSave = maybeCallbackAfterSave(entityAfterBeforeConvert, index);
return entityAfterAfterSave;
}
@Override
@ -192,6 +193,20 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
return save(Arrays.asList(entities));
}
@Override
public String index(IndexQuery query, IndexCoordinates index) {
maybeCallbackBeforeConvertWithQuery(query, index);
String documentId = doIndex(query, index);
maybeCallbackAfterSaveWithQuery(query, index);
return documentId;
}
public abstract String doIndex(IndexQuery query, IndexCoordinates indexCoordinates);
@Override
@Nullable
public <T> T get(String id, Class<T> clazz) {
@ -261,11 +276,38 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
return bulkIndex(queries, bulkOptions, getIndexCoordinatesFor(clazz));
}
@Override
public final List<IndexedObjectInformation> bulkIndex(List<IndexQuery> queries, BulkOptions bulkOptions,
IndexCoordinates index) {
Assert.notNull(queries, "List of IndexQuery must not be null");
Assert.notNull(bulkOptions, "BulkOptions must not be null");
return bulkOperation(queries, bulkOptions, index);
}
@Override
public void bulkUpdate(List<UpdateQuery> queries, Class<?> clazz) {
bulkUpdate(queries, getIndexCoordinatesFor(clazz));
}
public List<IndexedObjectInformation> bulkOperation(List<?> queries, BulkOptions bulkOptions,
IndexCoordinates index) {
Assert.notNull(queries, "List of IndexQuery must not be null");
Assert.notNull(bulkOptions, "BulkOptions must not be null");
maybeCallbackBeforeConvertWithQueries(queries, index);
List<IndexedObjectInformation> indexedObjectInformations = doBulkOperation(queries, bulkOptions, index);
maybeCallbackAfterSaveWithQueries(queries, index);
return indexedObjectInformations;
}
public abstract List<IndexedObjectInformation> doBulkOperation(List<?> queries, BulkOptions bulkOptions,
IndexCoordinates index);
// endregion
// region SearchOperations
@ -620,6 +662,20 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
if (queryObject != null) {
queryObject = maybeCallbackBeforeConvert(queryObject, index);
indexQuery.setObject(queryObject);
// the callback might have set som values relevant for the IndexQuery
IndexQuery newQuery = getIndexQuery(queryObject);
if (indexQuery.getRouting() == null && newQuery.getRouting() != null) {
indexQuery.setRouting(newQuery.getRouting());
}
if (indexQuery.getSeqNo() == null && newQuery.getSeqNo() != null) {
indexQuery.setSeqNo(newQuery.getSeqNo());
}
if (indexQuery.getPrimaryTerm() == null && newQuery.getPrimaryTerm() != null) {
indexQuery.setPrimaryTerm(newQuery.getPrimaryTerm());
}
}
}
}

View File

@ -16,6 +16,7 @@
package org.springframework.data.elasticsearch.core;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -34,11 +35,13 @@ import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.GetIndexTemplatesRequest;
import org.elasticsearch.client.indices.GetIndexTemplatesResponse;
import org.elasticsearch.client.indices.GetMappingsRequest;
import org.elasticsearch.client.indices.GetMappingsResponse;
import org.elasticsearch.client.indices.IndexTemplatesExistRequest;
import org.elasticsearch.client.indices.PutIndexTemplateRequest;
import org.elasticsearch.client.indices.PutMappingRequest;
import org.elasticsearch.cluster.metadata.AliasMetadata;
import org.elasticsearch.cluster.metadata.MappingMetadata;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.index.AliasActions;
import org.springframework.data.elasticsearch.core.index.AliasData;
@ -61,6 +64,8 @@ import org.springframework.util.Assert;
*/
class DefaultIndexOperations extends AbstractDefaultIndexOperations implements IndexOperations {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultIndexOperations.class);
private final ElasticsearchRestTemplate restTemplate;
public DefaultIndexOperations(ElasticsearchRestTemplate restTemplate, Class<?> boundClass) {
@ -117,10 +122,19 @@ class DefaultIndexOperations extends AbstractDefaultIndexOperations implements I
GetMappingsRequest mappingsRequest = requestFactory.getMappingsRequest(index);
return restTemplate.execute(client -> {
GetMappingsResponse mapping = client.indices().getMapping(mappingsRequest, RequestOptions.DEFAULT);
// we only return data for the first index name that was requested (always have done so)
String index1 = mappingsRequest.indices()[0];
return mapping.mappings().get(index1).getSourceAsMap();
Map<String, MappingMetadata> mappings = client.indices() //
.getMapping(mappingsRequest, RequestOptions.DEFAULT) //
.mappings(); //
if (mappings == null || mappings.size() == 0) {
return Collections.emptyMap();
}
if (mappings.size() > 1) {
LOGGER.warn("more than one mapping returned for " + index.getIndexName());
}
// we have at least one, take the first from the iterator
return mappings.entrySet().iterator().next().getValue().getSourceAsMap();
});
}

View File

@ -15,6 +15,7 @@
*/
package org.springframework.data.elasticsearch.core;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
@ -40,6 +41,7 @@ import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateReque
import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.metadata.AliasMetadata;
import org.elasticsearch.cluster.metadata.IndexTemplateMetadata;
import org.elasticsearch.cluster.metadata.MappingMetadata;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.compress.CompressedXContent;
import org.elasticsearch.common.settings.Settings;
@ -126,10 +128,19 @@ class DefaultTransportIndexOperations extends AbstractDefaultIndexOperations imp
GetMappingsRequest mappingsRequest = requestFactory.getMappingsRequest(client, index);
return client.admin().indices().getMappings( //
ImmutableOpenMap<String, ImmutableOpenMap<String, MappingMetadata>> mappings = client.admin().indices().getMappings( //
mappingsRequest).actionGet() //
.getMappings().get(mappingsRequest.indices()[0]).get(IndexCoordinates.TYPE) //
.getSourceAsMap();
.getMappings();
if (mappings == null || mappings.size() == 0) {
return Collections.emptyMap();
}
if (mappings.size() > 1) {
LOGGER.warn("more than one mapping returned for " + index.getIndexName());
}
// we have at least one, take the first from the iterator
return mappings.iterator().next().value.get(IndexCoordinates.TYPE).getSourceAsMap();
}
@Override

View File

@ -137,10 +137,7 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
// endregion
// region DocumentOperations
@Override
public String index(IndexQuery query, IndexCoordinates index) {
maybeCallbackBeforeConvertWithQuery(query, index);
public String doIndex(IndexQuery query, IndexCoordinates index) {
IndexRequest request = requestFactory.indexRequest(query, index);
IndexResponse indexResponse = execute(client -> client.index(request, RequestOptions.DEFAULT));
@ -152,8 +149,6 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
indexResponse.getPrimaryTerm(), indexResponse.getVersion()));
}
maybeCallbackAfterSaveWithQuery(query, index);
return indexResponse.getId();
}
@ -187,16 +182,6 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
return execute(client -> client.get(request, RequestOptions.DEFAULT).isExists());
}
@Override
public List<IndexedObjectInformation> bulkIndex(List<IndexQuery> queries, BulkOptions bulkOptions,
IndexCoordinates index) {
Assert.notNull(queries, "List of IndexQuery must not be null");
Assert.notNull(bulkOptions, "BulkOptions must not be null");
return doBulkOperation(queries, bulkOptions, index);
}
@Override
public void bulkUpdate(List<UpdateQuery> queries, BulkOptions bulkOptions, IndexCoordinates index) {
@ -237,14 +222,12 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
return new UpdateResponse(result);
}
private List<IndexedObjectInformation> doBulkOperation(List<?> queries, BulkOptions bulkOptions,
public List<IndexedObjectInformation> doBulkOperation(List<?> queries, BulkOptions bulkOptions,
IndexCoordinates index) {
maybeCallbackBeforeConvertWithQueries(queries, index);
BulkRequest bulkRequest = requestFactory.bulkRequest(queries, bulkOptions, index);
List<IndexedObjectInformation> indexedObjectInformationList = checkForBulkOperationFailure(
execute(client -> client.bulk(bulkRequest, RequestOptions.DEFAULT)));
updateIndexedObjectsWithQueries(queries, indexedObjectInformationList);
maybeCallbackAfterSaveWithQueries(queries, index);
return indexedObjectInformationList;
}
// endregion

View File

@ -144,10 +144,7 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
// endregion
// region DocumentOperations
@Override
public String index(IndexQuery query, IndexCoordinates index) {
maybeCallbackBeforeConvertWithQuery(query, index);
public String doIndex(IndexQuery query, IndexCoordinates index) {
IndexRequestBuilder indexRequestBuilder = requestFactory.indexRequestBuilder(client, query, index);
ActionFuture<IndexResponse> future = indexRequestBuilder.execute();
@ -166,8 +163,6 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
response.getPrimaryTerm(), response.getVersion()));
}
maybeCallbackAfterSaveWithQuery(query, index);
return documentId;
}
@ -201,22 +196,6 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
return getRequestBuilder.execute().actionGet().isExists();
}
@Override
public List<IndexedObjectInformation> bulkIndex(List<IndexQuery> queries, BulkOptions bulkOptions,
IndexCoordinates index) {
Assert.notNull(queries, "List of IndexQuery must not be null");
Assert.notNull(bulkOptions, "BulkOptions must not be null");
List<IndexedObjectInformation> indexedObjectInformations = doBulkOperation(queries, bulkOptions, index);
updateIndexedObjectsWithQueries(queries, indexedObjectInformations);
maybeCallbackAfterSaveWithQueries(queries, index);
return indexedObjectInformations;
}
@Override
public void bulkUpdate(List<UpdateQuery> queries, BulkOptions bulkOptions, IndexCoordinates index) {
@ -261,11 +240,13 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
return new UpdateResponse(result);
}
private List<IndexedObjectInformation> doBulkOperation(List<?> queries, BulkOptions bulkOptions,
public List<IndexedObjectInformation> doBulkOperation(List<?> queries, BulkOptions bulkOptions,
IndexCoordinates index) {
maybeCallbackBeforeConvertWithQueries(queries, index);
BulkRequestBuilder bulkRequest = requestFactory.bulkRequestBuilder(client, queries, bulkOptions, index);
return checkForBulkOperationFailure(bulkRequest.execute().actionGet());
final List<IndexedObjectInformation> indexedObjectInformations = checkForBulkOperationFailure(
bulkRequest.execute().actionGet());
updateIndexedObjectsWithQueries(queries, indexedObjectInformations);
return indexedObjectInformations;
}
// endregion

View File

@ -31,6 +31,9 @@ import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.model.BasicPersistentEntity;
import org.springframework.data.mapping.model.PersistentPropertyAccessorFactory;
import org.springframework.data.spel.EvaluationContextProvider;
import org.springframework.data.spel.ExpressionDependencies;
import org.springframework.data.util.Lazy;
import org.springframework.data.util.TypeInformation;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
@ -74,7 +77,10 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
private @Nullable VersionType versionType;
private boolean createIndexAndMapping;
private final Map<String, ElasticsearchPersistentProperty> fieldNamePropertyCache = new ConcurrentHashMap<>();
private EvaluationContextProvider evaluationContextProvider = EvaluationContextProvider.DEFAULT;;
private final ConcurrentHashMap<String, Expression> indexNameExpressions = new ConcurrentHashMap<>();
private final Lazy<EvaluationContext> indexNameEvaluationContext = Lazy.of(this::getIndexNameEvaluationContext);
public SimpleElasticsearchPersistentEntity(TypeInformation<T> typeInformation) {
@ -302,12 +308,20 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
return seqNoPrimaryTermProperty;
}
@Nullable
@Override
public ElasticsearchPersistentProperty getJoinFieldProperty() {
return joinFieldProperty;
}
// region SpEL handling
@Override
public void setEvaluationContextProvider(EvaluationContextProvider provider) {
super.setEvaluationContextProvider(provider);
this.evaluationContextProvider = provider;
}
/**
* resolves all the names in the IndexCoordinates object. If a name cannot be resolved, the original name is returned.
*
@ -316,14 +330,12 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
*/
private IndexCoordinates resolve(IndexCoordinates indexCoordinates) {
EvaluationContext context = getEvaluationContext(null);
String[] indexNames = indexCoordinates.getIndexNames();
String[] resolvedNames = new String[indexNames.length];
for (int i = 0; i < indexNames.length; i++) {
String indexName = indexNames[i];
resolvedNames[i] = resolve(context, indexName);
resolvedNames[i] = resolve(indexName);
}
return IndexCoordinates.of(resolvedNames);
@ -332,22 +344,49 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
/**
* tries to resolve the given name. If this is not successful, the original value is returned
*
* @param context SpEL evaluation context
* @param name name to resolve
* @return the resolved name or the input name if it cannot be resolved
*/
private String resolve(EvaluationContext context, String name) {
private String resolve(String name) {
Assert.notNull(name, "name must not be null");
Expression expression = indexNameExpressions.computeIfAbsent(name, s -> {
Expression expr = PARSER.parseExpression(name, ParserContext.TEMPLATE_EXPRESSION);
return expr instanceof LiteralExpression ? null : expr;
});
Expression expression = getExpressionForIndexName(name);
String resolvedName = expression != null ? expression.getValue(context, String.class) : null;
String resolvedName = expression != null ? expression.getValue(indexNameEvaluationContext.get(), String.class) : null;
return resolvedName != null ? resolvedName : name;
}
/**
* returns an {@link Expression} for #name if name contains a {@link ParserContext#TEMPLATE_EXPRESSION} otherwise
* returns {@literal null}.
*
* @param name the name to get the expression for
* @return Expression may be null
*/
@Nullable
private Expression getExpressionForIndexName(String name) {
return indexNameExpressions.computeIfAbsent(name, s -> {
Expression expr = PARSER.parseExpression(s, ParserContext.TEMPLATE_EXPRESSION);
return expr instanceof LiteralExpression ? null : expr;
});
}
/**
* build the {@link EvaluationContext} considering {@link ExpressionDependencies} from the name returned by
* {@link #getIndexName()}.
*
* @return EvaluationContext
*/
private EvaluationContext getIndexNameEvaluationContext() {
Expression expression = getExpressionForIndexName(getIndexName());
ExpressionDependencies expressionDependencies = expression != null ? ExpressionDependencies.discover(expression)
: ExpressionDependencies.none();
return evaluationContextProvider.getEvaluationContext(null, expressionDependencies);
}
// endregion
@Override
@ -363,5 +402,4 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
.put("index.refresh_interval", getRefreshInterval()).put("index.store.type", getIndexStoreType()).map();
return Document.from(map);
}
}

View File

@ -163,10 +163,13 @@ public class SimpleElasticsearchPersistentProperty extends
&& (isTemporalAccessor || isDate)) {
DateFormat dateFormat = field.format();
String property = getOwner().getType().getSimpleName() + "." + getName();
if (dateFormat == DateFormat.none) {
throw new MappingException(
String.format("Property %s is annotated with FieldType.%s but has no DateFormat defined",
getOwner().getType().getSimpleName() + "." + getName(), field.type().name()));
LOGGER.warn(
String.format("No DateFormat defined for property %s. Make sure you have a Converter registered for %s",
property, actualType.getSimpleName()));
return;
}
ElasticsearchDateConverter converter;
@ -177,7 +180,7 @@ public class SimpleElasticsearchPersistentProperty extends
if (!StringUtils.hasLength(pattern)) {
throw new MappingException(
String.format("Property %s is annotated with FieldType.%s and a custom format but has no pattern defined",
getOwner().getType().getSimpleName() + "." + getName(), field.type().name()));
property, field.type().name()));
}
converter = ElasticsearchDateConverter.of(pattern);

View File

@ -54,14 +54,14 @@ public class ElasticsearchPartQuery extends AbstractElasticsearchRepositoryQuery
public ElasticsearchPartQuery(ElasticsearchQueryMethod method, ElasticsearchOperations elasticsearchOperations) {
super(method, elasticsearchOperations);
this.tree = new PartTree(method.getName(), method.getEntityInformation().getJavaType());
this.tree = new PartTree(queryMethod.getName(), queryMethod.getResultProcessor().getReturnedType().getDomainType());
this.elasticsearchConverter = elasticsearchOperations.getElasticsearchConverter();
this.mappingContext = elasticsearchConverter.getMappingContext();
}
@Override
public Object execute(Object[] parameters) {
Class<?> clazz = queryMethod.getEntityInformation().getJavaType();
Class<?> clazz = queryMethod.getResultProcessor().getReturnedType().getDomainType();
ParametersParameterAccessor accessor = new ParametersParameterAccessor(queryMethod.getParameters(), parameters);
CriteriaQuery query = createQuery(accessor);

View File

@ -71,7 +71,7 @@ public class ElasticsearchStringQuery extends AbstractElasticsearchRepositoryQue
@Override
public Object execute(Object[] parameters) {
Class<?> clazz = queryMethod.getEntityInformation().getJavaType();
Class<?> clazz = queryMethod.getResultProcessor().getReturnedType().getDomainType();
ParametersParameterAccessor accessor = new ParametersParameterAccessor(queryMethod.getParameters(), parameters);
StringQuery stringQuery = createQuery(accessor);

View File

@ -1,6 +1,74 @@
Spring Data Elasticsearch Changelog
===================================
Changes in version 4.1.3 (2021-01-13)
-------------------------------------
* DATAES-996 - Update CI jobs with Docker Login.
* #1634 - Update Testcontainers dependency.
Changes in version 4.1.2 (2020-12-09)
-------------------------------------
* DATAES-991 - Wrong value for TermVector(with_positions_offets_payloads).
* DATAES-990 - Index creation fails with Authentication object cannot be null on startup.
* DATAES-987 - IndexOperations getMapping fail when using index alias.
* DATAES-978 - Accept DateFormat.none for a date property to enable custom Converters.
* DATAES-977 - Fix versions in reference documentation for 4.1.
* DATAES-973 - Release 4.1.2 (2020.0.2).
* DATAES-972 - BeforeConvertCallback should be called before index query is built.
* DATAES-543 - Adjust configuration support classes so they do not require proxying.
Changes in version 4.2.0-M1 (2020-12-09)
----------------------------------------
* DATAES-995 - Code Cleanup after DATACMNS-1838.
* DATAES-994 - Add setup for mutation testing.
* DATAES-991 - Wrong value for TermVector(with_positions_offets_payloads).
* DATAES-990 - Index creation fails with Authentication object cannot be null on startup.
* DATAES-989 - Improve deprecation warning for id properties without annotation.
* DATAES-988 - Allow specifying max results in NativeSearchQueryBuilder.
* DATAES-987 - IndexOperations getMapping fail when using index alias.
* DATAES-986 - Fix Javadoc.
* DATAES-985 - Add builder method for track_total_hits to NativeSearchQueryBuilder.
* DATAES-983 - Test dependency hoverfly-java-junit5 leaks into compile scope.
* DATAES-978 - Accept DateFormat.none for a date property to enable custom Converters.
* DATAES-976 - Implement CrudRepository.delete(Iterable<ID> ids).
* DATAES-975 - Upgrade to Elasticsearch 7.10.
* DATAES-974 - remove usage of deprecated WebClient exchange() method.
* DATAES-972 - BeforeConvertCallback should be called before index query is built.
* DATAES-971 - Fix tests for using a proxy with reactive client.
* DATAES-970 - Take Testcontainers version from the Spring Data Build pom.
* DATAES-969 - Use ResultProcessor in ElasticsearchPartQuery to build PartTree.
* DATAES-968 - Enable Maven caching for Jenkins jobs.
* DATAES-966 - Release 4.2 M1 (2021.0.0).
* DATAES-882 - HLRC Configuration - add ability to set max connections for the underlying HttpClient.
* DATAES-588 - Add support for custom callbacks in High Level/Low Level REST Client builder.
* DATAES-543 - Adjust configuration support classes so they do not require proxying.
* DATAES-362 - Add support for composable meta annotations.
* DATAES-247 - Support OpType in IndexQuery.
Changes in version 4.0.6.RELEASE (2020-12-09)
---------------------------------------------
* DATAES-991 - Wrong value for TermVector(with_positions_offets_payloads).
* DATAES-969 - Use ResultProcessor in ElasticsearchPartQuery to build PartTree.
* DATAES-968 - Enable Maven caching for Jenkins jobs.
* DATAES-964 - Release 4.0.6 (Neumann SR6).
Changes in version 3.2.12.RELEASE (2020-12-09)
----------------------------------------------
* DATAES-969 - Use ResultProcessor in ElasticsearchPartQuery to build PartTree.
* DATAES-963 - Release 3.2.12 (Moore SR12).
Changes in version 4.1.1 (2020-11-11)
-------------------------------------
* DATAES-969 - Use ResultProcessor in ElasticsearchPartQuery to build PartTree.
* DATAES-968 - Enable Maven caching for Jenkins jobs.
* DATAES-965 - Release 4.1.1 (2020.0.1).
Changes in version 4.1.0 (2020-10-28)
-------------------------------------
* DATAES-962 - Deprecate Joda support.
@ -1369,6 +1437,12 @@ Release Notes - Spring Data Elasticsearch - Version 1.0 M1 (2014-02-07)

View File

@ -1,4 +1,4 @@
Spring Data Elasticsearch 4.1 GA (2020.0.0)
Spring Data Elasticsearch 4.1.3 (2020.0.3)
Copyright (c) [2013-2019] Pivotal Software, Inc.
This product is licensed to you under the Apache License, Version 2.0 (the "License").
@ -20,3 +20,6 @@ conditions of the subcomponent's license, as noted in the LICENSE file.

View File

@ -18,6 +18,7 @@ package org.springframework.data.elasticsearch.config;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import org.springframework.context.annotation.Bean;
import reactor.core.publisher.Mono;
import java.util.Collection;
@ -114,6 +115,7 @@ public class ElasticsearchConfigurationSupportUnitTests {
static class ReactiveRestConfig extends AbstractReactiveElasticsearchConfiguration {
@Override
@Bean
public ReactiveElasticsearchClient reactiveElasticsearchClient() {
ReactiveElasticsearchClient client = mock(ReactiveElasticsearchClient.class);
when(client.info()).thenReturn(Mono

View File

@ -121,14 +121,12 @@ import org.springframework.lang.Nullable;
*/
public abstract class ElasticsearchTemplateTests {
protected static final String INDEX_NAME_JOIN_SAMPLE_ENTITY = "test-index-sample-join-template";
private static final String INDEX_NAME_SAMPLE_ENTITY = "test-index-sample-core-template";
private static final String INDEX_1_NAME = "test-index-1";
private static final String INDEX_2_NAME = "test-index-2";
private static final String INDEX_3_NAME = "test-index-3";
protected final IndexCoordinates index = IndexCoordinates.of(INDEX_NAME_SAMPLE_ENTITY);
protected static final String INDEX_NAME_JOIN_SAMPLE_ENTITY = "test-index-sample-join-template";
@Autowired protected ElasticsearchOperations operations;
protected IndexOperations indexOperations;
@ -1466,6 +1464,30 @@ public abstract class ElasticsearchTemplateTests {
assertThat(mapping.get("properties")).isNotNull();
}
@Test // DATAES-987
@DisplayName("should read mappings from alias")
void shouldReadMappingsFromAlias() {
String aliasName = INDEX_NAME_SAMPLE_ENTITY + "alias";
indexOperations.alias( //
new AliasActions( //
new AliasAction.Add( //
AliasActionParameters.builder() //
.withIndices(INDEX_NAME_SAMPLE_ENTITY) //
.withAliases(aliasName) //
.build()) //
) //
);
IndexOperations aliasIndexOps = operations.indexOps(IndexCoordinates.of(aliasName));
Map<String, Object> mappingFromAlias = aliasIndexOps.getMapping();
assertThat(mappingFromAlias).isNotNull();
assertThat(
((Map<String, Object>) ((Map<String, Object>) mappingFromAlias.get("properties")).get("message")).get("type"))
.isEqualTo("text");
}
@Test
public void shouldDeleteIndexForGivenEntity() {

View File

@ -0,0 +1,235 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core.event;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import lombok.Data;
import java.util.Collections;
import java.util.List;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.JoinTypeRelation;
import org.springframework.data.elasticsearch.annotations.JoinTypeRelations;
import org.springframework.data.elasticsearch.core.AbstractElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.join.JoinField;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
/**
* @author Peter-Josef Meisch
* @author Roman Puchkovskiy
*/
@SpringIntegrationTest
abstract class ElasticsearchOperationsCallbackIntegrationTest {
private static final String INDEX = "test-operations-callback";
@Autowired private ElasticsearchOperations originalOperations;
// need a spy here on the abstract implementation class
private AbstractElasticsearchTemplate operations;
@Nullable private static SeqNoPrimaryTerm seqNoPrimaryTerm = null;
@Configuration
static class Config {
@Component
static class SampleEntityBeforeConvertCallback implements BeforeConvertCallback<SampleEntity> {
@Override
public SampleEntity onBeforeConvert(SampleEntity entity, IndexCoordinates index) {
entity.setText("converted");
JoinField<String> joinField = new JoinField<>("answer", "42");
entity.setJoinField(joinField);
if (seqNoPrimaryTerm != null) {
entity.setSeqNoPrimaryTerm(seqNoPrimaryTerm);
}
return entity;
}
}
}
@BeforeEach
void setUp() {
seqNoPrimaryTerm = null;
operations = (AbstractElasticsearchTemplate) spy(originalOperations);
IndexOperations indexOps = operations.indexOps(SampleEntity.class);
indexOps.delete();
indexOps.create();
indexOps.putMapping(SampleEntity.class);
// store one entity to have a seq_no and primary_term
final SampleEntity initial = new SampleEntity("1", "initial");
final SampleEntity saved = operations.save(initial);
seqNoPrimaryTerm = saved.getSeqNoPrimaryTerm();
}
@AfterEach
void tearDown() {
IndexOperations indexOps = operations.indexOps(SampleEntity.class);
indexOps.delete();
}
@Test // DATAES-68
void shouldCallBeforeConvertCallback() {
SampleEntity entity = new SampleEntity("1", "test");
SampleEntity saved = operations.save(entity);
assertThat(saved.getText()).isEqualTo("converted");
}
@Test // DATAES-972
@DisplayName("should apply conversion result to IndexQuery on save")
void shouldApplyConversionResultToIndexQueryOnSave() {
SampleEntity entity = new SampleEntity("1", "test");
operations.save(entity);
ArgumentCaptor<IndexQuery> indexQueryCaptor = ArgumentCaptor.forClass(IndexQuery.class);
verify(operations, times(2)).doIndex(indexQueryCaptor.capture(), any());
final IndexQuery capturedIndexQuery = indexQueryCaptor.getValue();
SampleEntity convertedEntity = (SampleEntity) capturedIndexQuery.getObject();
final JoinField<String> joinField = convertedEntity.getJoinField();
assertThat(joinField.getName()).isEqualTo("answer");
assertThat(joinField.getParent()).isEqualTo("42");
assertThat(capturedIndexQuery.getRouting()).isEqualTo("42");
assertThat(capturedIndexQuery.getSeqNo()).isEqualTo(seqNoPrimaryTerm.getSequenceNumber());
assertThat(capturedIndexQuery.getPrimaryTerm()).isEqualTo(seqNoPrimaryTerm.getPrimaryTerm());
}
@Test // DATAES-972
@DisplayName("should apply conversion result to IndexQuery when not set ")
void shouldApplyConversionResultToIndexQueryWhenNotSet() {
SampleEntity entity = new SampleEntity("1", "test");
final IndexQuery indexQuery = new IndexQuery();
indexQuery.setId(entity.getId());
indexQuery.setObject(entity);
operations.index(indexQuery, IndexCoordinates.of(INDEX));
ArgumentCaptor<IndexQuery> indexQueryCaptor = ArgumentCaptor.forClass(IndexQuery.class);
verify(operations, times(2)).doIndex(indexQueryCaptor.capture(), any());
final IndexQuery capturedIndexQuery = indexQueryCaptor.getValue();
SampleEntity convertedEntity = (SampleEntity) capturedIndexQuery.getObject();
final JoinField<String> joinField = convertedEntity.getJoinField();
assertThat(joinField.getName()).isEqualTo("answer");
assertThat(joinField.getParent()).isEqualTo("42");
assertThat(capturedIndexQuery.getRouting()).isEqualTo("42");
assertThat(capturedIndexQuery.getSeqNo()).isEqualTo(seqNoPrimaryTerm.getSequenceNumber());
assertThat(capturedIndexQuery.getPrimaryTerm()).isEqualTo(seqNoPrimaryTerm.getPrimaryTerm());
}
@Test // DATAES-972
@DisplayName("should not apply conversion result to IndexQuery when already set ")
void shouldNotApplyConversionResultToIndexQueryWhenAlreadySet() {
SeqNoPrimaryTerm seqNoPrimaryTermOriginal = seqNoPrimaryTerm;
seqNoPrimaryTerm = new SeqNoPrimaryTerm(7, 8);
SampleEntity entity = new SampleEntity("1", "test");
final IndexQuery indexQuery = new IndexQuery();
indexQuery.setId(entity.getId());
indexQuery.setObject(entity);
indexQuery.setRouting("12");
indexQuery.setSeqNo(seqNoPrimaryTermOriginal.getSequenceNumber());
indexQuery.setPrimaryTerm(seqNoPrimaryTermOriginal.getPrimaryTerm());
operations.index(indexQuery, IndexCoordinates.of(INDEX));
ArgumentCaptor<IndexQuery> indexQueryCaptor = ArgumentCaptor.forClass(IndexQuery.class);
verify(operations, times(2)).doIndex(indexQueryCaptor.capture(), any());
final IndexQuery capturedIndexQuery = indexQueryCaptor.getValue();
SampleEntity convertedEntity = (SampleEntity) capturedIndexQuery.getObject();
final JoinField<String> joinField = convertedEntity.getJoinField();
assertThat(joinField.getName()).isEqualTo("answer");
assertThat(joinField.getParent()).isEqualTo("42");
assertThat(capturedIndexQuery.getRouting()).isEqualTo("12");
assertThat(capturedIndexQuery.getSeqNo()).isEqualTo(seqNoPrimaryTermOriginal.getSequenceNumber());
assertThat(capturedIndexQuery.getPrimaryTerm()).isEqualTo(seqNoPrimaryTermOriginal.getPrimaryTerm());
}
@Test // DATAES-972
@DisplayName("should apply conversion result to IndexQuery in bulkIndex")
void shouldApplyConversionResultToIndexQueryInBulkIndex() {
SampleEntity entity = new SampleEntity("1", "test");
final IndexQuery indexQuery = new IndexQuery();
indexQuery.setId(entity.getId());
indexQuery.setObject(entity);
operations.bulkIndex(Collections.singletonList(indexQuery), SampleEntity.class);
ArgumentCaptor<List<IndexQuery>> indexQueryListCaptor = ArgumentCaptor.forClass(List.class);
verify(operations, times(1)).bulkOperation(indexQueryListCaptor.capture(), any(), any());
final List<IndexQuery> capturedIndexQueries = indexQueryListCaptor.getValue();
assertThat(capturedIndexQueries).hasSize(1);
final IndexQuery capturedIndexQuery = capturedIndexQueries.get(0);
SampleEntity convertedEntity = (SampleEntity) capturedIndexQuery.getObject();
final JoinField<String> joinField = convertedEntity.getJoinField();
assertThat(joinField.getName()).isEqualTo("answer");
assertThat(joinField.getParent()).isEqualTo("42");
assertThat(capturedIndexQuery.getRouting()).isEqualTo("42");
assertThat(capturedIndexQuery.getSeqNo()).isEqualTo(seqNoPrimaryTerm.getSequenceNumber());
assertThat(capturedIndexQuery.getPrimaryTerm()).isEqualTo(seqNoPrimaryTerm.getPrimaryTerm());
}
@Data
@Document(indexName = INDEX)
static class SampleEntity {
@Id private String id;
private String text;
@JoinTypeRelations(relations = { @JoinTypeRelation(parent = "question",
children = { "answer" }) }) @Nullable private JoinField<String> joinField;
private SeqNoPrimaryTerm seqNoPrimaryTerm;
public SampleEntity(String id, String text) {
this.id = id;
this.text = text;
}
}
}

View File

@ -1,102 +0,0 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.core.event;
import static org.assertj.core.api.Assertions.*;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.stereotype.Component;
/**
* @author Peter-Josef Meisch
* @author Roman Puchkovskiy
*/
abstract class ElasticsearchOperationsCallbackTest {
@Autowired private ElasticsearchOperations operations;
@Configuration
static class Config {
@Component
static class SampleEntityBeforeConvertCallback implements BeforeConvertCallback<SampleEntity> {
@Override
public SampleEntity onBeforeConvert(SampleEntity entity, IndexCoordinates index) {
entity.setText("converted");
return entity;
}
}
}
@BeforeEach
void setUp() {
IndexOperations indexOps = operations.indexOps(SampleEntity.class);
indexOps.delete();
indexOps.create();
indexOps.putMapping(SampleEntity.class);
}
@AfterEach
void tearDown() {
IndexOperations indexOps = operations.indexOps(SampleEntity.class);
indexOps.delete();
}
@Test
void shouldCallBeforeConvertCallback() {
SampleEntity entity = new SampleEntity("1", "test");
SampleEntity saved = operations.save(entity);
assertThat(saved.getText()).isEqualTo("converted");
}
@Document(indexName = "test-operations-callback")
static class SampleEntity {
@Id private String id;
private String text;
public SampleEntity(String id, String text) {
this.id = id;
this.text = text;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
}

View File

@ -16,12 +16,11 @@
package org.springframework.data.elasticsearch.core.event;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
*/
@SpringIntegrationTest
@ContextConfiguration(classes = { ElasticsearchRestTemplateConfiguration.class, ElasticsearchOperationsCallbackTest.Config.class })
class ElasticsearchRestOperationsCallbackTest extends ElasticsearchOperationsCallbackTest {}
@ContextConfiguration(classes = { ElasticsearchRestTemplateConfiguration.class,
ElasticsearchOperationsCallbackIntegrationTest.Config.class })
class ElasticsearchRestOperationsCallbackIntegrationTest extends ElasticsearchOperationsCallbackIntegrationTest {}

View File

@ -16,12 +16,10 @@
package org.springframework.data.elasticsearch.core.event;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchTemplateConfiguration;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
*/
@SpringIntegrationTest
@ContextConfiguration(classes = { ElasticsearchTemplateConfiguration.class, ElasticsearchOperationsCallbackTest.Config.class })
class ElasticsearchTransportOperationsCallbackTest extends ElasticsearchOperationsCallbackTest {}
@ContextConfiguration(classes = { ElasticsearchTemplateConfiguration.class, ElasticsearchOperationsCallbackIntegrationTest.Config.class })
class ElasticsearchTransportOperationsCallbackIntegrationTest extends ElasticsearchOperationsCallbackIntegrationTest {}

View File

@ -45,6 +45,7 @@ import org.assertj.core.data.Percentage;
import org.elasticsearch.search.suggest.completion.context.ContextMapping;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Id;
@ -249,6 +250,16 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
assertThat(fieldLastName.get("copy_to")).isEqualTo(copyToValue);
}
@Test // DATAES-991
@DisplayName("should write correct TermVector values")
void shouldWriteCorrectTermVectorValues() {
IndexOperations indexOps = operations.indexOps(TermVectorFieldEntity.class);
indexOps.create();
indexOps.putMapping();
}
/**
* @author Xiao Yu
*/
@ -621,4 +632,19 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
@Field(type = FieldType.Rank_Features) private Map<String, Integer> topics;
}
@Data
@Document(indexName = "termvectors-test")
static class TermVectorFieldEntity {
@Id private String id;
@Field(type = FieldType.Text, termVector = TermVector.no) private String no;
@Field(type = FieldType.Text, termVector = TermVector.yes) private String yes;
@Field(type = FieldType.Text, termVector = TermVector.with_positions) private String with_positions;
@Field(type = FieldType.Text, termVector = TermVector.with_offsets) private String with_offsets;
@Field(type = FieldType.Text, termVector = TermVector.with_positions_offsets) private String with_positions_offsets;
@Field(type = FieldType.Text,
termVector = TermVector.with_positions_payloads) private String with_positions_payloads;
@Field(type = FieldType.Text,
termVector = TermVector.with_positions_offsets_payloads) private String with_positions_offsets_payloads;
}
}

View File

@ -34,8 +34,9 @@ abstract class MappingContextBaseTests {
private SimpleElasticsearchMappingContext setupMappingContext() {
SimpleElasticsearchMappingContext mappingContext = new ElasticsearchConfigurationSupport()
.elasticsearchMappingContext();
ElasticsearchConfigurationSupport configurationSupport = new ElasticsearchConfigurationSupport();
SimpleElasticsearchMappingContext mappingContext = configurationSupport
.elasticsearchMappingContext(configurationSupport.elasticsearchCustomConversions());
mappingContext.initialize();
return mappingContext;
}

View File

@ -192,20 +192,6 @@ public class SimpleElasticsearchPersistentPropertyUnitTests {
assertThat(seqNoProperty.isReadable()).isFalse();
}
@Test // DATAES-828
void shouldRequireFormatForDateField() {
assertThatExceptionOfType(MappingException.class) //
.isThrownBy(() -> context.getRequiredPersistentEntity(DateFieldWithNoFormat.class)) //
.withMessageContaining("date");
}
@Test // DATAES-828
void shouldRequireFormatForDateNanosField() {
assertThatExceptionOfType(MappingException.class) //
.isThrownBy(() -> context.getRequiredPersistentEntity(DateNanosFieldWithNoFormat.class)) //
.withMessageContaining("date");
}
@Test // DATAES-924
@DisplayName("should require pattern for custom date format")
void shouldRequirePatternForCustomDateFormat() {

View File

@ -68,9 +68,9 @@ public class ElasticsearchRestTemplateConfiguration extends AbstractElasticsearc
}
@Override
public ElasticsearchOperations elasticsearchOperations(ElasticsearchConverter elasticsearchConverter) {
RestHighLevelClient client = elasticsearchClient();
return new ElasticsearchRestTemplate(client, elasticsearchConverter) {
public ElasticsearchOperations elasticsearchOperations(ElasticsearchConverter elasticsearchConverter,
RestHighLevelClient elasticsearchClient) {
return new ElasticsearchRestTemplate(elasticsearchClient, elasticsearchConverter) {
@Override
public <T> T execute(ClientCallback<T> callback) {
try {