mirror of
https://github.com/spring-projects/spring-data-elasticsearch.git
synced 2025-08-14 05:23:27 +00:00
Compare commits
50 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
3fee02c17e | ||
|
a92b57aedd | ||
|
0b6a77be3b | ||
|
9a2b3b1317 | ||
|
171ea62b9b | ||
|
3b2ff95702 | ||
|
51fb872bea | ||
|
67c570606a | ||
|
eb311d089f | ||
|
93492f86da | ||
|
88b2fa970d | ||
|
11e4ebf12c | ||
|
1272fdc188 | ||
|
96eac3ba73 | ||
|
c47d284e6b | ||
|
f5d651b497 | ||
|
d35d38f7d5 | ||
|
2883280b00 | ||
|
fedd97624e | ||
|
9fab24d5ef | ||
|
bf32581105 | ||
|
7f89d79b12 | ||
|
955eb77add | ||
|
0086d35e54 | ||
|
53f0e79990 | ||
|
3da9eaadc5 | ||
|
310f6f0f22 | ||
|
262af9f0e8 | ||
|
bc74126d7e | ||
|
308de7f8db | ||
|
ae5f72fb62 | ||
|
170facc3f3 | ||
|
0aaffebf16 | ||
|
d1da6ac4ed | ||
|
133bc315ed | ||
|
9f243fd2c9 | ||
|
de57159c7f | ||
|
74ed69877d | ||
|
7314bfc21d | ||
|
5909c19ead | ||
|
9df27ef289 | ||
|
28d92359b2 | ||
|
d8b0d526b4 | ||
|
dc832e75a6 | ||
|
866cd96477 | ||
|
162c57df31 | ||
|
3600452796 | ||
|
4b1c4c8000 | ||
|
70d556e526 | ||
|
214d91f3c1 |
2
CI.adoc
2
CI.adoc
@ -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
141
Jenkinsfile
vendored
@ -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
6
ci/clean.sh
Executable 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
10
ci/verify.sh
Executable 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
13
pom.xml
@ -5,12 +5,12 @@
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-elasticsearch</artifactId>
|
||||
<version>4.1.0</version>
|
||||
<version>4.1.5</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.build</groupId>
|
||||
<artifactId>spring-data-parent</artifactId>
|
||||
<version>2.4.0</version>
|
||||
<version>2.4.5</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.5</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>
|
||||
|
@ -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
|
||||
|
@ -154,7 +154,7 @@ httpHeaders.add("some-header", "on every request") <1>
|
||||
|
||||
ClientConfiguration clientConfiguration = ClientConfiguration.builder()
|
||||
.connectedTo("localhost:9200", "localhost:9291") <2>
|
||||
.useSsl() <3>
|
||||
.usingSsl() <3>
|
||||
.withProxy("localhost:8888") <4>
|
||||
.withPathPrefix("ela") <5>
|
||||
.withConnectTimeout(Duration.ofSeconds(5)) <6>
|
||||
|
@ -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].
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2018-2020 the original author or authors.
|
||||
* Copyright 2018-2021 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.
|
||||
@ -27,6 +27,7 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import org.springframework.web.reactive.function.client.WebClient.Builder;
|
||||
import org.springframework.web.util.DefaultUriBuilderFactory;
|
||||
|
||||
/**
|
||||
* Default {@link WebClientProvider} that uses cached {@link WebClient} instances per {@code hostAndPort}.
|
||||
@ -156,7 +157,16 @@ class DefaultWebClientProvider implements WebClientProvider {
|
||||
|
||||
String baseUrl = String.format("%s://%s:%d%s", this.scheme, socketAddress.getHostString(), socketAddress.getPort(),
|
||||
pathPrefix == null ? "" : '/' + pathPrefix);
|
||||
WebClient webClient = builder.baseUrl(baseUrl).filter((request, next) -> next.exchange(request).doOnError(errorListener)).build();
|
||||
|
||||
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(baseUrl);
|
||||
// the template will already be encoded by the RequestConverters methods
|
||||
uriBuilderFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY);
|
||||
builder.uriBuilderFactory(uriBuilderFactory); //
|
||||
|
||||
WebClient webClient = builder //
|
||||
.filter((request, next) -> next.exchange(request) //
|
||||
.doOnError(errorListener)) //
|
||||
.build(); //
|
||||
return webClientConfigurer.apply(webClient);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-2020 the original author or authors.
|
||||
* Copyright 2019-2021 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.
|
||||
@ -54,6 +54,7 @@ import org.elasticsearch.action.index.IndexRequestBuilder;
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.search.SearchRequestBuilder;
|
||||
import org.elasticsearch.action.support.ActiveShardCount;
|
||||
import org.elasticsearch.action.support.WriteRequest;
|
||||
import org.elasticsearch.action.update.UpdateRequest;
|
||||
import org.elasticsearch.action.update.UpdateRequestBuilder;
|
||||
import org.elasticsearch.client.Client;
|
||||
@ -85,6 +86,7 @@ import org.elasticsearch.script.Script;
|
||||
import org.elasticsearch.script.ScriptType;
|
||||
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
|
||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
|
||||
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
|
||||
import org.elasticsearch.search.sort.FieldSortBuilder;
|
||||
import org.elasticsearch.search.sort.GeoDistanceSortBuilder;
|
||||
@ -860,9 +862,7 @@ class RequestFactory {
|
||||
elasticsearchConverter.updateQuery(searchQuery, clazz);
|
||||
List<MultiGetRequest.Item> items = new ArrayList<>();
|
||||
|
||||
if (!isEmpty(searchQuery.getFields())) {
|
||||
searchQuery.addSourceFilter(new FetchSourceFilter(toArray(searchQuery.getFields()), null));
|
||||
}
|
||||
FetchSourceContext fetchSourceContext = getFetchSourceContext(searchQuery);
|
||||
|
||||
if (!isEmpty(searchQuery.getIds())) {
|
||||
String indexName = index.getIndexName();
|
||||
@ -872,6 +872,11 @@ class RequestFactory {
|
||||
if (searchQuery.getRoute() != null) {
|
||||
item = item.routing(searchQuery.getRoute());
|
||||
}
|
||||
|
||||
if (fetchSourceContext != null) {
|
||||
item.fetchSourceContext(fetchSourceContext);
|
||||
}
|
||||
|
||||
items.add(item);
|
||||
}
|
||||
}
|
||||
@ -1553,38 +1558,6 @@ class RequestFactory {
|
||||
return elasticsearchFilter;
|
||||
}
|
||||
|
||||
// region response stuff
|
||||
|
||||
/**
|
||||
* extract the index settings information for a given index
|
||||
*
|
||||
* @param response the Elasticsearch response
|
||||
* @param indexName the index name
|
||||
* @return settings as {@link Document}
|
||||
*/
|
||||
public Document fromSettingsResponse(GetSettingsResponse response, String indexName) {
|
||||
|
||||
Document settings = Document.create();
|
||||
|
||||
if (!response.getIndexToDefaultSettings().isEmpty()) {
|
||||
Settings defaultSettings = response.getIndexToDefaultSettings().get(indexName);
|
||||
for (String key : defaultSettings.keySet()) {
|
||||
settings.put(key, defaultSettings.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
if (!response.getIndexToSettings().isEmpty()) {
|
||||
Settings customSettings = response.getIndexToSettings().get(indexName);
|
||||
for (String key : customSettings.keySet()) {
|
||||
settings.put(key, customSettings.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region helper functions
|
||||
@Nullable
|
||||
private ElasticsearchPersistentEntity<?> getPersistentEntity(@Nullable Class<?> clazz) {
|
||||
return clazz != null ? elasticsearchConverter.getMappingContext().getPersistentEntity(clazz) : null;
|
||||
@ -1633,6 +1606,57 @@ class RequestFactory {
|
||||
return entity.hasSeqNoPrimaryTermProperty();
|
||||
}
|
||||
|
||||
private FetchSourceContext getFetchSourceContext(Query searchQuery) {
|
||||
FetchSourceContext fetchSourceContext = null;
|
||||
SourceFilter sourceFilter = searchQuery.getSourceFilter();
|
||||
|
||||
if (!isEmpty(searchQuery.getFields())) {
|
||||
if (sourceFilter == null) {
|
||||
sourceFilter = new FetchSourceFilter(toArray(searchQuery.getFields()), null);
|
||||
} else {
|
||||
ArrayList<String> arrayList = new ArrayList<>();
|
||||
Collections.addAll(arrayList, sourceFilter.getIncludes());
|
||||
sourceFilter = new FetchSourceFilter(toArray(arrayList), null);
|
||||
}
|
||||
|
||||
fetchSourceContext = new FetchSourceContext(true, sourceFilter.getIncludes(), sourceFilter.getExcludes());
|
||||
} else if (sourceFilter != null) {
|
||||
fetchSourceContext = new FetchSourceContext(true, sourceFilter.getIncludes(), sourceFilter.getExcludes());
|
||||
}
|
||||
return fetchSourceContext;
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region response stuff
|
||||
|
||||
/**
|
||||
* extract the index settings information for a given index
|
||||
*
|
||||
* @param response the Elasticsearch response
|
||||
* @param indexName the index name
|
||||
* @return settings as {@link Document}
|
||||
*/
|
||||
public Document fromSettingsResponse(GetSettingsResponse response, String indexName) {
|
||||
|
||||
Document settings = Document.create();
|
||||
|
||||
if (!response.getIndexToDefaultSettings().isEmpty()) {
|
||||
Settings defaultSettings = response.getIndexToDefaultSettings().get(indexName);
|
||||
for (String key : defaultSettings.keySet()) {
|
||||
settings.put(key, defaultSettings.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
if (!response.getIndexToSettings().isEmpty()) {
|
||||
Settings customSettings = response.getIndexToSettings().get(indexName);
|
||||
for (String key : customSettings.keySet()) {
|
||||
settings.put(key, customSettings.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
@ -172,19 +172,19 @@ public class GeoConverters {
|
||||
String type = GeoConverters.getGeoJsonType(source);
|
||||
|
||||
switch (type) {
|
||||
case GeoJsonPoint.TYPE:
|
||||
case "point":
|
||||
return MapToGeoJsonPointConverter.INSTANCE.convert(source);
|
||||
case GeoJsonMultiPoint.TYPE:
|
||||
case "multipoint":
|
||||
return MapToGeoJsonMultiPointConverter.INSTANCE.convert(source);
|
||||
case GeoJsonLineString.TYPE:
|
||||
case "linestring":
|
||||
return MapToGeoJsonLineStringConverter.INSTANCE.convert(source);
|
||||
case GeoJsonMultiLineString.TYPE:
|
||||
case "multilinestring":
|
||||
return MapToGeoJsonMultiLineStringConverter.INSTANCE.convert(source);
|
||||
case GeoJsonPolygon.TYPE:
|
||||
case "polygon":
|
||||
return MapToGeoJsonPolygonConverter.INSTANCE.convert(source);
|
||||
case GeoJsonMultiPolygon.TYPE:
|
||||
case "multipolygon":
|
||||
return MapToGeoJsonMultiPolygonConverter.INSTANCE.convert(source);
|
||||
case GeoJsonGeometryCollection.TYPE:
|
||||
case "geometrycollection":
|
||||
return MapToGeoJsonGeometryCollectionConverter.INSTANCE.convert(source);
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown GeoJson type " + type);
|
||||
@ -217,7 +217,7 @@ public class GeoConverters {
|
||||
public GeoJsonPoint convert(Map<String, Object> source) {
|
||||
|
||||
String type = GeoConverters.getGeoJsonType(source);
|
||||
Assert.isTrue(type.equals(GeoJsonPoint.TYPE), "does not contain a type 'Point'");
|
||||
Assert.isTrue(type.equalsIgnoreCase(GeoJsonPoint.TYPE), "does not contain a type 'Point'");
|
||||
|
||||
Object coordinates = source.get("coordinates");
|
||||
Assert.notNull(coordinates, "Document to convert does not contain coordinates");
|
||||
@ -255,7 +255,7 @@ public class GeoConverters {
|
||||
public GeoJsonMultiPoint convert(Map<String, Object> source) {
|
||||
|
||||
String type = GeoConverters.getGeoJsonType(source);
|
||||
Assert.isTrue(type.equals(GeoJsonMultiPoint.TYPE), "does not contain a type 'MultiPoint'");
|
||||
Assert.isTrue(type.equalsIgnoreCase(GeoJsonMultiPoint.TYPE), "does not contain a type 'MultiPoint'");
|
||||
Object coordinates = source.get("coordinates");
|
||||
Assert.notNull(coordinates, "Document to convert does not contain coordinates");
|
||||
Assert.isTrue(coordinates instanceof List, "coordinates must be a List");
|
||||
@ -290,7 +290,7 @@ public class GeoConverters {
|
||||
public GeoJsonLineString convert(Map<String, Object> source) {
|
||||
|
||||
String type = GeoConverters.getGeoJsonType(source);
|
||||
Assert.isTrue(type.equals(GeoJsonLineString.TYPE), "does not contain a type 'LineString'");
|
||||
Assert.isTrue(type.equalsIgnoreCase(GeoJsonLineString.TYPE), "does not contain a type 'LineString'");
|
||||
Object coordinates = source.get("coordinates");
|
||||
Assert.notNull(coordinates, "Document to convert does not contain coordinates");
|
||||
Assert.isTrue(coordinates instanceof List, "coordinates must be a List");
|
||||
@ -322,7 +322,7 @@ public class GeoConverters {
|
||||
public GeoJsonMultiLineString convert(Map<String, Object> source) {
|
||||
|
||||
String type = GeoConverters.getGeoJsonType(source);
|
||||
Assert.isTrue(type.equals(GeoJsonMultiLineString.TYPE), "does not contain a type 'MultiLineString'");
|
||||
Assert.isTrue(type.equalsIgnoreCase(GeoJsonMultiLineString.TYPE), "does not contain a type 'MultiLineString'");
|
||||
List<GeoJsonLineString> lines = geoJsonLineStringsFromMap(source);
|
||||
return GeoJsonMultiLineString.of(lines);
|
||||
}
|
||||
@ -350,7 +350,7 @@ public class GeoConverters {
|
||||
public GeoJsonPolygon convert(Map<String, Object> source) {
|
||||
|
||||
String type = GeoConverters.getGeoJsonType(source);
|
||||
Assert.isTrue(type.equals(GeoJsonPolygon.TYPE), "does not contain a type 'Polygon'");
|
||||
Assert.isTrue(type.equalsIgnoreCase(GeoJsonPolygon.TYPE), "does not contain a type 'Polygon'");
|
||||
List<GeoJsonLineString> lines = geoJsonLineStringsFromMap(source);
|
||||
Assert.isTrue(lines.size() > 0, "no linestrings defined in polygon");
|
||||
GeoJsonPolygon geoJsonPolygon = GeoJsonPolygon.of(lines.get(0));
|
||||
@ -394,7 +394,7 @@ public class GeoConverters {
|
||||
public GeoJsonMultiPolygon convert(Map<String, Object> source) {
|
||||
|
||||
String type = GeoConverters.getGeoJsonType(source);
|
||||
Assert.isTrue(type.equals(GeoJsonMultiPolygon.TYPE), "does not contain a type 'MultiPolygon'");
|
||||
Assert.isTrue(type.equalsIgnoreCase(GeoJsonMultiPolygon.TYPE), "does not contain a type 'MultiPolygon'");
|
||||
Object coordinates = source.get("coordinates");
|
||||
Assert.notNull(coordinates, "Document to convert does not contain coordinates");
|
||||
Assert.isTrue(coordinates instanceof List, "coordinates must be a List");
|
||||
@ -441,7 +441,8 @@ public class GeoConverters {
|
||||
public GeoJsonGeometryCollection convert(Map<String, Object> source) {
|
||||
|
||||
String type = GeoConverters.getGeoJsonType(source);
|
||||
Assert.isTrue(type.equals(GeoJsonGeometryCollection.TYPE), "does not contain a type 'GeometryCollection'");
|
||||
Assert.isTrue(type.equalsIgnoreCase(GeoJsonGeometryCollection.TYPE),
|
||||
"does not contain a type 'GeometryCollection'");
|
||||
Object geometries = source.get("geometries");
|
||||
Assert.notNull(geometries, "Document to convert does not contain geometries");
|
||||
Assert.isTrue(geometries instanceof List, "geometries must be a List");
|
||||
@ -461,7 +462,7 @@ public class GeoConverters {
|
||||
Assert.notNull(type, "Document to convert does not contain a type");
|
||||
Assert.isTrue(type instanceof String, "type must be a String");
|
||||
|
||||
return type.toString();
|
||||
return type.toString().toLowerCase();
|
||||
}
|
||||
|
||||
private static List<Double> toCoordinates(Point point) {
|
||||
|
@ -31,6 +31,11 @@ import org.springframework.data.util.TypeInformation;
|
||||
public class SimpleElasticsearchMappingContext
|
||||
extends AbstractMappingContext<SimpleElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> {
|
||||
|
||||
@Override
|
||||
protected boolean shouldCreatePersistentEntityFor(TypeInformation<?> type) {
|
||||
return !ElasticsearchSimpleTypes.HOLDER.isSimpleType(type.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T> SimpleElasticsearchPersistentEntity<?> createPersistentEntity(TypeInformation<T> typeInformation) {
|
||||
return new SimpleElasticsearchPersistentEntity<>(typeInformation);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -1,6 +1,143 @@
|
||||
Spring Data Elasticsearch Changelog
|
||||
===================================
|
||||
|
||||
Changes in version 4.1.5 (2021-02-18)
|
||||
-------------------------------------
|
||||
|
||||
|
||||
Changes in version 4.2.0-M3 (2021-02-17)
|
||||
----------------------------------------
|
||||
* #1689 - Missing anchor links in documentation.
|
||||
* #1680 - After upgrade to 4.x can't read property id from _source named (different value from _id).
|
||||
* #1679 - Errors are silent in delete by query in ReactiveElasticsearchTemplate.
|
||||
* #1676 - Align MappingElasticsearchConverter with other Spring Data converters.
|
||||
* #1675 - Consider Document as simple type.
|
||||
* #1669 - Cleanup Deprecations from 4.0.
|
||||
* #1668 - Writing a more complex CriteriaQuery.
|
||||
* #1667 - Couldn't find PersistentEntity for type class com.example.demo.dto.Address.
|
||||
* #1665 - ReactiveElasticsearchOperations indexName twice endcoding.
|
||||
* #1662 - Documentation fix.
|
||||
* #1659 - Fix source filter setup in multiget requests.
|
||||
* #1655 - GeoJson types can be lowercase in Elasticsearch.
|
||||
* #1649 - Upgrade to Elasticsearch 7.10.2.
|
||||
* #1647 - Use own implementation of date formatters.
|
||||
* #1644 - Implement update by query.
|
||||
* #1565 - Allow using FieldNamingStrategy for property to fieldname matching [DATAES-993].
|
||||
* #1370 - Add enabled mapping parameter to FieldType configuration [DATAES-798].
|
||||
* #1218 - Add routing parameter to ElasticsearchOperations [DATAES-644].
|
||||
* #1156 - Add @CountQuery annotation [DATAES-584].
|
||||
* #1143 - Support for search_after [DATAES-571].
|
||||
* #803 - Don't update indexed object if it is no persistent entity [DATAES-229].
|
||||
* #725 - Add query Explain Support [DATAES-149].
|
||||
|
||||
|
||||
Changes in version 4.1.4 (2021-02-17)
|
||||
-------------------------------------
|
||||
* #1667 - Couldn't find PersistentEntity for type class com.example.demo.dto.Address.
|
||||
* #1665 - ReactiveElasticsearchOperations indexName twice endcoding.
|
||||
* #1662 - Documentation fix.
|
||||
* #1659 - Fix source filter setup in multiget requests.
|
||||
* #1655 - GeoJson types can be lowercase in Elasticsearch.
|
||||
|
||||
|
||||
Changes in version 4.0.7.RELEASE (2021-02-17)
|
||||
---------------------------------------------
|
||||
* DATAES-996 - Update CI jobs with Docker Login.
|
||||
* #1667 - Couldn't find PersistentEntity for type class com.example.demo.dto.Address.
|
||||
* #1665 - ReactiveElasticsearchOperations indexName twice endcoding.
|
||||
* #1662 - Documentation fix.
|
||||
* #1659 - Fix source filter setup in multiget requests.
|
||||
|
||||
|
||||
Changes in version 3.2.13.RELEASE (2021-02-17)
|
||||
----------------------------------------------
|
||||
* #1694 - Upgrade to Elasticsearch 6.8.14.
|
||||
* #1662 - Documentation fix.
|
||||
|
||||
|
||||
Changes in version 4.2.0-M2 (2021-01-13)
|
||||
----------------------------------------
|
||||
* DATAES-1003 - add timeout to search query.
|
||||
* DATAES-996 - Update CI jobs with Docker Login.
|
||||
* DATAES-982 - Improve refresh handling.
|
||||
* DATAES-946 - Support 'wildcard' field type.
|
||||
* #1640 - Add support for GetFieldMapping request in ReactiveElasticsearchClient.
|
||||
* #1638 - Upgrade to Elasticsearch 7.10.1.
|
||||
* #1634 - Update Testcontainers dependency.
|
||||
* #1632 - Update copyright notice to 2021.
|
||||
* #1629 - Update repository after GitHub issues migration.
|
||||
* #1576 - Add version of Spring dependency to docs [DATAES-1004].
|
||||
* #1056 - Repository initialization should throw an Exception when index cannot be created [DATAES-481].
|
||||
|
||||
|
||||
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.
|
||||
@ -1363,6 +1500,18 @@ Release Notes - Spring Data Elasticsearch - Version 1.0 M1 (2014-02-07)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
Spring Data Elasticsearch 4.1 GA (2020.0.0)
|
||||
Spring Data Elasticsearch 4.1.5 (2020.0.5)
|
||||
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,8 @@ conditions of the subcomponent's license, as noted in the LICENSE 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
|
||||
|
@ -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() {
|
||||
|
||||
|
@ -32,6 +32,8 @@ import java.lang.Boolean;
|
||||
import java.lang.Long;
|
||||
import java.lang.Object;
|
||||
import java.net.ConnectException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@ -1037,6 +1039,32 @@ public class ReactiveElasticsearchTemplateIntegrationTests {
|
||||
assertThat(searchHits.getSearchHits().size()).isEqualTo(5);
|
||||
}).verifyComplete();
|
||||
}
|
||||
|
||||
@Test // #1665
|
||||
@DisplayName("should be able to process date-math-index names")
|
||||
void shouldBeAbleToProcessDateMathIndexNames() {
|
||||
|
||||
String indexName = "foo-" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy.MM"));
|
||||
String dateMathIndexName = "<foo-{now/M{yyyy.MM}}>";
|
||||
|
||||
template.indexOps(IndexCoordinates.of(dateMathIndexName)) //
|
||||
.create() //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(true) //
|
||||
.verifyComplete(); //
|
||||
|
||||
template.indexOps(IndexCoordinates.of(indexName)) //
|
||||
.exists() //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(true) //
|
||||
.verifyComplete(); //
|
||||
|
||||
template.indexOps(IndexCoordinates.of(dateMathIndexName)) //
|
||||
.delete() //
|
||||
.as(StepVerifier::create) //
|
||||
.expectNext(true) //
|
||||
.verifyComplete(); //
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Helper functions
|
||||
@ -1134,5 +1162,6 @@ public class ReactiveElasticsearchTemplateIntegrationTests {
|
||||
@Id private String id;
|
||||
@Version private Long version;
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Copyright 2021 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;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
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.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.Field;
|
||||
import org.springframework.data.elasticsearch.annotations.FieldType;
|
||||
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.core.query.SourceFilter;
|
||||
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 })
|
||||
public class SourceFilterIntegrationTests {
|
||||
|
||||
@Autowired private ElasticsearchOperations operations;
|
||||
private IndexOperations indexOps;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
indexOps = operations.indexOps(Entity.class);
|
||||
indexOps.create();
|
||||
indexOps.putMapping();
|
||||
|
||||
operations.save(Entity.builder().id("42").field1("one").field2("two").field3("three").build());
|
||||
indexOps.refresh();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
indexOps.delete();
|
||||
}
|
||||
|
||||
@Test // #1659
|
||||
@DisplayName("should only return requested fields on search")
|
||||
void shouldOnlyReturnRequestedFieldsOnSearch() {
|
||||
|
||||
Query query = Query.findAll();
|
||||
query.addFields("field2");
|
||||
|
||||
SearchHits<Entity> searchHits = operations.search(query, Entity.class);
|
||||
|
||||
assertThat(searchHits).hasSize(1);
|
||||
Entity entity = searchHits.getSearchHit(0).getContent();
|
||||
assertThat(entity.getField1()).isNull();
|
||||
assertThat(entity.getField2()).isEqualTo("two");
|
||||
assertThat(entity.getField3()).isNull();
|
||||
}
|
||||
|
||||
@Test // #1659
|
||||
@DisplayName("should only return requested fields on multiget")
|
||||
void shouldOnlyReturnRequestedFieldsOnGMultiGet() {
|
||||
|
||||
Query query = new NativeSearchQueryBuilder().withIds(Collections.singleton("42")).build();
|
||||
query.addFields("field2");
|
||||
|
||||
List<Entity> entities = operations.multiGet(query, Entity.class);
|
||||
|
||||
assertThat(entities).hasSize(1);
|
||||
Entity entity = entities.get(0);
|
||||
assertThat(entity.getField1()).isNull();
|
||||
assertThat(entity.getField2()).isEqualTo("two");
|
||||
assertThat(entity.getField3()).isNull();
|
||||
}
|
||||
|
||||
@Test // #1659
|
||||
@DisplayName("should not return excluded fields from SourceFilter on search")
|
||||
void shouldNotReturnExcludedFieldsFromSourceFilterOnSearch() {
|
||||
|
||||
Query query = Query.findAll();
|
||||
query.addSourceFilter(new SourceFilter() {
|
||||
@Override
|
||||
public String[] getIncludes() {
|
||||
return new String[] {};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getExcludes() {
|
||||
return new String[] { "field2" };
|
||||
}
|
||||
});
|
||||
|
||||
SearchHits<Entity> entities = operations.search(query, Entity.class);
|
||||
|
||||
assertThat(entities).hasSize(1);
|
||||
Entity entity = entities.getSearchHit(0).getContent();
|
||||
assertThat(entity.getField1()).isNotNull();
|
||||
assertThat(entity.getField2()).isNull();
|
||||
assertThat(entity.getField3()).isNotNull();
|
||||
}
|
||||
|
||||
@Test // #1659
|
||||
@DisplayName("should not return excluded fields from SourceFilter on multiget")
|
||||
void shouldNotReturnExcludedFieldsFromSourceFilterOnMultiGet() {
|
||||
|
||||
Query query = new NativeSearchQueryBuilder().withIds(Collections.singleton("42")).build();
|
||||
query.addSourceFilter(new SourceFilter() {
|
||||
@Override
|
||||
public String[] getIncludes() {
|
||||
return new String[] {};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getExcludes() {
|
||||
return new String[] { "field2" };
|
||||
}
|
||||
});
|
||||
|
||||
List<Entity> entities = operations.multiGet(query, Entity.class);
|
||||
|
||||
assertThat(entities).hasSize(1);
|
||||
Entity entity = entities.get(0);
|
||||
assertThat(entity.getField1()).isNotNull();
|
||||
assertThat(entity.getField2()).isNull();
|
||||
assertThat(entity.getField3()).isNotNull();
|
||||
}
|
||||
|
||||
@Test // #1659
|
||||
@DisplayName("should only return included fields from SourceFilter on search")
|
||||
void shouldOnlyReturnIncludedFieldsFromSourceFilterOnSearch() {
|
||||
|
||||
Query query = Query.findAll();
|
||||
query.addSourceFilter(new SourceFilter() {
|
||||
@Override
|
||||
public String[] getIncludes() {
|
||||
return new String[] { "field2" };
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getExcludes() {
|
||||
return new String[] {};
|
||||
}
|
||||
});
|
||||
|
||||
SearchHits<Entity> entities = operations.search(query, Entity.class);
|
||||
|
||||
assertThat(entities).hasSize(1);
|
||||
Entity entity = entities.getSearchHit(0).getContent();
|
||||
assertThat(entity.getField1()).isNull();
|
||||
assertThat(entity.getField2()).isNotNull();
|
||||
assertThat(entity.getField3()).isNull();
|
||||
}
|
||||
|
||||
@Test // #1659
|
||||
@DisplayName("should only return included fields from SourceFilter on multiget")
|
||||
void shouldOnlyReturnIncludedFieldsFromSourceFilterOnMultiGet() {
|
||||
|
||||
Query query = new NativeSearchQueryBuilder().withIds(Collections.singleton("42")).build();
|
||||
query.addSourceFilter(new SourceFilter() {
|
||||
@Override
|
||||
public String[] getIncludes() {
|
||||
return new String[] { "field2" };
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getExcludes() {
|
||||
return new String[] {};
|
||||
}
|
||||
});
|
||||
|
||||
List<Entity> entities = operations.multiGet(query, Entity.class);
|
||||
|
||||
assertThat(entities).hasSize(1);
|
||||
Entity entity = entities.get(0);
|
||||
assertThat(entity.getField1()).isNull();
|
||||
assertThat(entity.getField2()).isNotNull();
|
||||
assertThat(entity.getField3()).isNull();
|
||||
}
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Document(indexName = "sourcefilter-tests")
|
||||
public static class Entity {
|
||||
@Id private String id;
|
||||
@Field(type = FieldType.Text) private String field1;
|
||||
@Field(type = FieldType.Text) private String field2;
|
||||
@Field(type = FieldType.Text) private String field3;
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2021 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;
|
||||
|
||||
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchTemplateConfiguration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
/**
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
@ContextConfiguration(classes = { ElasticsearchTemplateConfiguration.class })
|
||||
public class SourceFilterIntegrationTransportTests extends SourceFilterIntegrationTests {}
|
@ -53,6 +53,9 @@ class GeoConvertersUnitTests {
|
||||
@DisplayName("GeoJsonPoint")
|
||||
class GeoJsonPointUnitTests {
|
||||
|
||||
// NOTE: the test converting from a map contains the type names in lowercase, that might be returned from
|
||||
// Elasticsearch
|
||||
|
||||
@Test // DATAES-930
|
||||
@DisplayName("should be converted to a Map")
|
||||
void shouldBeConvertedToAMap() throws JSONException {
|
||||
@ -75,7 +78,7 @@ class GeoConvertersUnitTests {
|
||||
|
||||
// make sure we can read int values as well
|
||||
String json = "{\n" + //
|
||||
" \"type\": \"Point\",\n" + //
|
||||
" \"type\": \"point\",\n" + //
|
||||
" \"coordinates\": [12, 34.0]\n" + //
|
||||
"}"; //
|
||||
|
||||
@ -117,8 +120,14 @@ class GeoConvertersUnitTests {
|
||||
void shouldBeConvertedFromAMap() {
|
||||
|
||||
// make sure we can read int values as well
|
||||
String json = "{\n" + " \"type\": \"MultiPoint\",\n" + " \"coordinates\": [\n" + " [12.0, 34],\n"
|
||||
+ " [56, 78.0]\n" + " ]\n" + "}\n";
|
||||
String json = "{\n" + //
|
||||
" \"type\": \"multipoint\",\n" //
|
||||
+ " \"coordinates\": [\n" + //
|
||||
" [12.0, 34],\n" + //
|
||||
" [56, 78.0]\n" + //
|
||||
" ]\n" + //
|
||||
"}\n"; //
|
||||
|
||||
Document document = Document.parse(json);
|
||||
|
||||
GeoJsonMultiPoint expected = GeoJsonMultiPoint.of(new Point(12, 34), new Point(56, 78));
|
||||
@ -158,7 +167,7 @@ class GeoConvertersUnitTests {
|
||||
|
||||
// make sure we can read int values as well
|
||||
String json = "{\n" + //
|
||||
" \"type\": \"LineString\",\n" + //
|
||||
" \"type\": \"linestring\",\n" + //
|
||||
" \"coordinates\": [\n" + //
|
||||
" [12.0, 34],\n" + //
|
||||
" [56, 78.0]\n" //
|
||||
@ -205,7 +214,7 @@ class GeoConvertersUnitTests {
|
||||
void shouldBeConvertedFromAMap() {
|
||||
// make sure we can read int values as well
|
||||
String json = "{\n" + //
|
||||
" \"type\": \"MultiLineString\",\n" + //
|
||||
" \"type\": \"multilinestring\",\n" + //
|
||||
" \"coordinates\": [\n" + //
|
||||
" [[12, 34.0], [56.0, 78]],\n" + //
|
||||
" [[90.0, 12], [34, 56.0]]\n" + //
|
||||
@ -256,7 +265,7 @@ class GeoConvertersUnitTests {
|
||||
void shouldBeConvertedFromAMap() {
|
||||
|
||||
String json = "{\n" + //
|
||||
" \"type\": \"Polygon\",\n" + //
|
||||
" \"type\": \"polygon\",\n" + //
|
||||
" \"coordinates\": [\n" + //
|
||||
" [[12, 34.0], [56.0, 78], [90, 12.0], [12, 34.0]],\n" + //
|
||||
" [[56.0, 78], [90, 12.0], [34.0, 56], [56.0, 78]]\n" + //
|
||||
@ -308,7 +317,7 @@ class GeoConvertersUnitTests {
|
||||
void shouldBeConvertedFromAMap() {
|
||||
|
||||
String json = "{\n" + //
|
||||
" \"type\": \"MultiPolygon\",\n" + //
|
||||
" \"type\": \"multipolygon\",\n" + //
|
||||
" \"coordinates\": [\n" + //
|
||||
" [[[12, 34.0], [56.0, 78], [90, 12.0], [12, 34.0]]],\n" + //
|
||||
" [[[56, 78.0], [90, 12.0], [34.0, 56], [56, 78.0]]]\n" + //
|
||||
@ -369,14 +378,14 @@ class GeoConvertersUnitTests {
|
||||
void shouldBeConvertedFromAMap() {
|
||||
|
||||
String json = "{\n" + //
|
||||
" \"type\": \"GeometryCollection\",\n" + //
|
||||
" \"type\": \"geometrycollection\",\n" + //
|
||||
" \"geometries\": [\n" + //
|
||||
" {\n" + //
|
||||
" \"type\": \"Point\",\n" + //
|
||||
" \"type\": \"point\",\n" + //
|
||||
" \"coordinates\": [12.0, 34.0]\n" + //
|
||||
" },\n" + //
|
||||
" {\n" + //
|
||||
" \"type\": \"Polygon\",\n" + //
|
||||
" \"type\": \"polygon\",\n" + //
|
||||
" \"coordinates\": [\n" + //
|
||||
" [[12.0, 34.0], [56.0, 78.0], [90.0, 12.0], [12.0, 34.0]]\n" + //
|
||||
" ]\n" + //
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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 {}
|
@ -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 {}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright 2021 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.mapping;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
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.context.annotation.Configuration;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.convert.ReadingConverter;
|
||||
import org.springframework.data.convert.WritingConverter;
|
||||
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.SearchHits;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchCustomConversions;
|
||||
import org.springframework.data.elasticsearch.core.geo.GeoJsonPoint;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
|
||||
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
|
||||
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
/**
|
||||
* Test that a whole entity can be converted using custom conversions
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
@SpringIntegrationTest
|
||||
@ContextConfiguration(classes = { EntityCustomConversionIntegrationTests.Config.class })
|
||||
public class EntityCustomConversionIntegrationTests {
|
||||
|
||||
@Configuration
|
||||
@EnableElasticsearchRepositories(basePackages = { "org.springframework.data.elasticsearch.core.mapping" },
|
||||
considerNestedRepositories = true)
|
||||
static class Config extends ElasticsearchRestTemplateConfiguration {
|
||||
@Override
|
||||
public ElasticsearchCustomConversions elasticsearchCustomConversions() {
|
||||
return new ElasticsearchCustomConversions(Arrays.asList(new EntityToMapConverter(), new MapToEntityConverter()));
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired private ElasticsearchOperations operations;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
IndexOperations indexOps = operations.indexOps(Entity.class);
|
||||
indexOps.create();
|
||||
indexOps.putMapping();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
operations.indexOps(Entity.class).delete();
|
||||
}
|
||||
|
||||
@Test // #1667
|
||||
@DisplayName("should use CustomConversions on entity")
|
||||
void shouldUseCustomConversionsOnEntity() {
|
||||
|
||||
Entity entity = Entity.builder() //
|
||||
.value("hello") //
|
||||
.location(GeoJsonPoint.of(8.0, 42.7)) //
|
||||
.build();
|
||||
|
||||
org.springframework.data.elasticsearch.core.document.Document document = org.springframework.data.elasticsearch.core.document.Document
|
||||
.create();
|
||||
operations.getElasticsearchConverter().write(entity, document);
|
||||
|
||||
assertThat(document.getString("the_value")).isEqualTo("hello");
|
||||
assertThat(document.getString("the_lon")).isEqualTo("8.0");
|
||||
assertThat(document.getString("the_lat")).isEqualTo("42.7");
|
||||
}
|
||||
|
||||
@Test // #1667
|
||||
@DisplayName("should store and load entity from Elasticsearch")
|
||||
void shouldStoreAndLoadEntityFromElasticsearch() {
|
||||
|
||||
Entity entity = Entity.builder() //
|
||||
.value("hello") //
|
||||
.location(GeoJsonPoint.of(8.0, 42.7)) //
|
||||
.build();
|
||||
|
||||
Entity savedEntity = operations.save(entity);
|
||||
operations.indexOps(Entity.class).refresh();
|
||||
|
||||
SearchHits<Entity> searchHits = operations.search(Query.findAll(), Entity.class);
|
||||
assertThat(searchHits.getTotalHits()).isEqualTo(1);
|
||||
Entity foundEntity = searchHits.getSearchHit(0).getContent();
|
||||
assertThat(foundEntity).isEqualTo(entity);
|
||||
}
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Document(indexName = "entity-with-custom-conversions")
|
||||
static class Entity {
|
||||
private String value;
|
||||
private GeoJsonPoint location;
|
||||
}
|
||||
|
||||
@WritingConverter
|
||||
static class EntityToMapConverter implements Converter<Entity, Map<String, Object>> {
|
||||
@Override
|
||||
public Map<String, Object> convert(Entity source) {
|
||||
LinkedHashMap<String, Object> target = new LinkedHashMap<>();
|
||||
target.put("the_value", source.getValue());
|
||||
target.put("the_lat", "" + source.getLocation().getY());
|
||||
target.put("the_lon", "" + source.getLocation().getX());
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
||||
@ReadingConverter
|
||||
static class MapToEntityConverter implements Converter<Map<String, Object>, Entity> {
|
||||
|
||||
@Override
|
||||
public Entity convert(Map<String, Object> source) {
|
||||
Entity entity = new Entity();
|
||||
entity.setValue((String) source.get("the_value"));
|
||||
entity.setLocation(GeoJsonPoint.of( //
|
||||
Double.parseDouble((String) (source.get("the_lon"))), //
|
||||
Double.parseDouble((String) (source.get("the_lat"))) //
|
||||
));
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
}
|
@ -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() {
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user