mirror of
https://github.com/spring-projects/spring-data-elasticsearch.git
synced 2025-08-06 01:23:32 +00:00
Compare commits
45 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
c92480b97d | ||
|
6e8357caa7 | ||
|
541faff07f | ||
|
9aeac006d1 | ||
|
6e52b97b76 | ||
|
c7550e8d82 | ||
|
c17159ce1c | ||
|
0a1d10f8b4 | ||
|
ef0e47c6bb | ||
|
7e2ebb299c | ||
|
594566a44a | ||
|
bf3248b536 | ||
|
c59bb0b434 | ||
|
d93d1e0738 | ||
|
aebdc8f86b | ||
|
2be27593d6 | ||
|
aa22d8239d | ||
|
6746bc5278 | ||
|
a6cb959605 | ||
|
e04905a1a7 | ||
|
834b10f578 | ||
|
823cfa919a | ||
|
ab29ae4219 | ||
|
62fb89208a | ||
|
e8c3badc56 | ||
|
187c9bbd5d | ||
|
e1a5811406 | ||
|
b643669d36 | ||
|
f66af53480 | ||
|
14099970bb | ||
|
0ab253422f | ||
|
0d2a6b98e8 | ||
|
30602496bd | ||
|
95762b4fde | ||
|
0771e90031 | ||
|
6729330500 | ||
|
8796292611 | ||
|
f3f9ca4002 | ||
|
083a38ed57 | ||
|
4f3aa52958 | ||
|
3256a2bfe0 | ||
|
95401a5bd7 | ||
|
12cd64cfc8 | ||
|
7e557317d1 | ||
|
c9846ab8ad |
4
.mvn/wrapper/maven-wrapper.properties
vendored
4
.mvn/wrapper/maven-wrapper.properties
vendored
@ -1,3 +1,3 @@
|
||||
#Mon Oct 11 14:30:32 CEST 2021
|
||||
#Fri Jun 03 09:42:29 CEST 2022
|
||||
wrapperUrl=https\://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
|
||||
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.3/apache-maven-3.8.3-bin.zip
|
||||
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.5/apache-maven-3.8.5-bin.zip
|
||||
|
76
Jenkinsfile
vendored
76
Jenkinsfile
vendored
@ -1,9 +1,15 @@
|
||||
def p = [:]
|
||||
node {
|
||||
checkout scm
|
||||
p = readProperties interpolate: true, file: 'ci/pipeline.properties'
|
||||
}
|
||||
|
||||
pipeline {
|
||||
agent none
|
||||
|
||||
triggers {
|
||||
pollSCM 'H/10 * * * *'
|
||||
upstream(upstreamProjects: "spring-data-commons/main", threshold: hudson.model.Result.SUCCESS)
|
||||
upstream(upstreamProjects: "spring-data-commons/2.6.x", threshold: hudson.model.Result.SUCCESS)
|
||||
}
|
||||
|
||||
options {
|
||||
@ -12,7 +18,7 @@ pipeline {
|
||||
}
|
||||
|
||||
stages {
|
||||
stage("test: baseline (jdk8)") {
|
||||
stage("test: baseline (main)") {
|
||||
when {
|
||||
anyOf {
|
||||
branch 'main'
|
||||
@ -25,14 +31,14 @@ pipeline {
|
||||
options { timeout(time: 30, unit: 'MINUTES') }
|
||||
|
||||
environment {
|
||||
DOCKER_HUB = credentials('hub.docker.com-springbuildmaster')
|
||||
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
|
||||
DOCKER_HUB = credentials("${p['docker.credentials']}")
|
||||
ARTIFACTORY = credentials("${p['artifactory.credentials']}")
|
||||
}
|
||||
|
||||
steps {
|
||||
script {
|
||||
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
|
||||
docker.image('adoptopenjdk/openjdk8:latest').inside('-u root -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v $HOME:/tmp/jenkins-home') {
|
||||
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
|
||||
docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) {
|
||||
sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}"
|
||||
sh 'PROFILE=none ci/verify.sh'
|
||||
sh "ci/clean.sh"
|
||||
@ -50,23 +56,23 @@ pipeline {
|
||||
}
|
||||
}
|
||||
parallel {
|
||||
stage("test: baseline (jdk11)") {
|
||||
stage("test: baseline (next)") {
|
||||
agent {
|
||||
label 'data'
|
||||
}
|
||||
options { timeout(time: 30, unit: 'MINUTES') }
|
||||
|
||||
environment {
|
||||
DOCKER_HUB = credentials('hub.docker.com-springbuildmaster')
|
||||
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
|
||||
DOCKER_HUB = credentials("${p['docker.credentials']}")
|
||||
ARTIFACTORY = credentials("${p['artifactory.credentials']}")
|
||||
}
|
||||
|
||||
steps {
|
||||
script {
|
||||
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
|
||||
docker.image('adoptopenjdk/openjdk11:latest').inside('-u root -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v $HOME:/tmp/jenkins-home') {
|
||||
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
|
||||
docker.image(p['docker.java.next.image']).inside(p['docker.java.inside.docker']) {
|
||||
sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}"
|
||||
sh 'PROFILE=java11 ci/verify.sh'
|
||||
sh 'PROFILE=none ci/verify.sh'
|
||||
sh "ci/clean.sh"
|
||||
}
|
||||
}
|
||||
@ -74,23 +80,23 @@ pipeline {
|
||||
}
|
||||
}
|
||||
|
||||
stage("test: baseline (jdk17)") {
|
||||
stage("test: baseline (LTS)") {
|
||||
agent {
|
||||
label 'data'
|
||||
}
|
||||
options { timeout(time: 30, unit: 'MINUTES') }
|
||||
|
||||
environment {
|
||||
DOCKER_HUB = credentials('hub.docker.com-springbuildmaster')
|
||||
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
|
||||
DOCKER_HUB = credentials("${p['docker.credentials']}")
|
||||
ARTIFACTORY = credentials("${p['artifactory.credentials']}")
|
||||
}
|
||||
|
||||
steps {
|
||||
script {
|
||||
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
|
||||
docker.image('openjdk:17-bullseye').inside('-u root -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v $HOME:/tmp/jenkins-home') {
|
||||
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
|
||||
docker.image(p['docker.java.lts.image']).inside(p['docker.java.inside.docker']) {
|
||||
sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}"
|
||||
sh 'PROFILE=java11 ci/verify.sh'
|
||||
sh 'PROFILE=none ci/verify.sh'
|
||||
sh "ci/clean.sh"
|
||||
}
|
||||
}
|
||||
@ -113,13 +119,13 @@ pipeline {
|
||||
options { timeout(time: 20, unit: 'MINUTES') }
|
||||
|
||||
environment {
|
||||
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
|
||||
ARTIFACTORY = credentials("${p['artifactory.credentials']}")
|
||||
}
|
||||
|
||||
steps {
|
||||
script {
|
||||
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
|
||||
docker.image('adoptopenjdk/openjdk8:latest').inside('-v $HOME:/tmp/jenkins-home') {
|
||||
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
|
||||
docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.basic']) {
|
||||
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,artifactory -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-elasticsearch-non-root ' +
|
||||
'-Dartifactory.server=https://repo.spring.io ' +
|
||||
"-Dartifactory.username=${ARTIFACTORY_USR} " +
|
||||
@ -133,34 +139,6 @@ pipeline {
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Publish documentation') {
|
||||
when {
|
||||
branch 'main'
|
||||
}
|
||||
agent {
|
||||
label 'data'
|
||||
}
|
||||
options { timeout(time: 20, unit: 'MINUTES') }
|
||||
|
||||
environment {
|
||||
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
|
||||
}
|
||||
|
||||
steps {
|
||||
script {
|
||||
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
|
||||
docker.image('adoptopenjdk/openjdk8:latest').inside('-v $HOME:/tmp/jenkins-home') {
|
||||
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,distribute -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-elasticsearch-non-root ' +
|
||||
'-Dartifactory.server=https://repo.spring.io ' +
|
||||
"-Dartifactory.username=${ARTIFACTORY_USR} " +
|
||||
"-Dartifactory.password=${ARTIFACTORY_PSW} " +
|
||||
"-Dartifactory.distribution-repository=temp-private-local " +
|
||||
'-Dmaven.test.skip=true clean deploy -U -B'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post {
|
||||
|
29
ci/pipeline.properties
Normal file
29
ci/pipeline.properties
Normal file
@ -0,0 +1,29 @@
|
||||
# Java versions
|
||||
java.main.tag=8u332-b09-jdk
|
||||
java.next.tag=11.0.15_10-jdk
|
||||
java.lts.tag=17.0.3_7-jdk
|
||||
|
||||
# Docker container images - standard
|
||||
docker.java.main.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.main.tag}
|
||||
docker.java.next.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.next.tag}
|
||||
docker.java.lts.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.lts.tag}
|
||||
|
||||
# Supported versions of MongoDB
|
||||
docker.mongodb.4.0.version=4.0.28
|
||||
docker.mongodb.4.4.version=4.4.12
|
||||
docker.mongodb.5.0.version=5.0.6
|
||||
|
||||
# Supported versions of Redis
|
||||
docker.redis.6.version=6.2.6
|
||||
|
||||
# Supported versions of Cassandra
|
||||
docker.cassandra.3.version=3.11.12
|
||||
|
||||
# Docker environment settings
|
||||
docker.java.inside.basic=-v $HOME:/tmp/jenkins-home
|
||||
docker.java.inside.docker=-u root -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v $HOME:/tmp/jenkins-home
|
||||
|
||||
# Credentials
|
||||
docker.registry=
|
||||
docker.credentials=hub.docker.com-springbuildmaster
|
||||
artifactory.credentials=02bd1690-b54f-4c9f-819d-a77cb7a9822c
|
24
pom.xml
24
pom.xml
@ -5,12 +5,12 @@
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-elasticsearch</artifactId>
|
||||
<version>4.3.0</version>
|
||||
<version>4.3.5</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.build</groupId>
|
||||
<artifactId>spring-data-parent</artifactId>
|
||||
<version>2.6.0</version>
|
||||
<version>2.6.5</version>
|
||||
</parent>
|
||||
|
||||
<name>Spring Data Elasticsearch</name>
|
||||
@ -19,9 +19,9 @@
|
||||
|
||||
<properties>
|
||||
<elasticsearch>7.15.2</elasticsearch>
|
||||
<log4j>2.14.1</log4j>
|
||||
<log4j>2.17.0</log4j>
|
||||
<netty>4.1.65.Final</netty>
|
||||
<springdata.commons>2.6.0</springdata.commons>
|
||||
<springdata.commons>2.6.5</springdata.commons>
|
||||
<testcontainers>1.15.3</testcontainers>
|
||||
<blockhound-junit>1.0.6.RELEASE</blockhound-junit>
|
||||
<java-module-name>spring.data.elasticsearch</java-module-name>
|
||||
@ -145,6 +145,12 @@
|
||||
<groupId>org.elasticsearch.client</groupId>
|
||||
<artifactId>transport</artifactId>
|
||||
<version>${elasticsearch}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@ -152,6 +158,12 @@
|
||||
<groupId>org.elasticsearch.plugin</groupId>
|
||||
<artifactId>transport-netty4-client</artifactId>
|
||||
<version>${elasticsearch}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@ -461,7 +473,9 @@
|
||||
</module>
|
||||
</checkstyleRules>
|
||||
<includes>**/*</includes>
|
||||
<excludes>.git/**/*,target/**/*,**/target/**/*,.idea/**/*,**/spring.schemas,**/*.svg,mvnw,mvnw.cmd,**/*.policy</excludes>
|
||||
<excludes>
|
||||
.git/**/*,target/**/*,**/target/**/*,.idea/**/*,**/spring.schemas,**/*.svg,mvnw,mvnw.cmd,**/*.policy
|
||||
</excludes>
|
||||
<sourceDirectories>./</sourceDirectories>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
@ -199,6 +199,29 @@ Default is 5 sec.
|
||||
IMPORTANT: Adding a Header supplier as shown in above example allows to inject headers that may change over the time, like authentication JWT tokens.
|
||||
If this is used in the reactive setup, the supplier function *must not* block!
|
||||
|
||||
=== Elasticsearch 7 compatibility headers
|
||||
|
||||
When using Spring Data Elasticsearch 4 - which uses the Elasticsearch 7 client libraries - and accessing an Elasticsearch cluster that is running on version 8, it is necessary to set the compatibility headers
|
||||
https://www.elastic.co/guide/en/elasticsearch/reference/8.0/rest-api-compatibility.html[see Elasticserach documentation].
|
||||
This should be done using a header supplier like shown above:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
ClientConfigurationBuilder configurationBuilder = new ClientConfigurationBuilder();
|
||||
configurationBuilder //
|
||||
// ...
|
||||
.withHeaders(() -> {
|
||||
HttpHeaders defaultCompatibilityHeaders = new HttpHeaders();
|
||||
defaultCompatibilityHeaders.add("Accept",
|
||||
"application/vnd.elasticsearch+json;compatible-with=7");
|
||||
defaultCompatibilityHeaders.add("Content-Type",
|
||||
"application/vnd.elasticsearch+json;compatible-with=7");
|
||||
return defaultCompatibilityHeaders;
|
||||
});
|
||||
----
|
||||
====
|
||||
|
||||
[[elasticsearch.clients.logging]]
|
||||
== Client Logging
|
||||
|
||||
|
@ -46,6 +46,16 @@ public @interface CompletionContext {
|
||||
* @since 4.3
|
||||
*/
|
||||
enum ContextMappingType {
|
||||
CATEGORY, GEO
|
||||
CATEGORY("category"), GEO("geo");
|
||||
|
||||
private final String mappedName;
|
||||
|
||||
ContextMappingType(String mappedName) {
|
||||
this.mappedName = mappedName;
|
||||
}
|
||||
|
||||
public String getMappedName() {
|
||||
return mappedName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ public @interface Document {
|
||||
* Name of the Elasticsearch index.
|
||||
* <ul>
|
||||
* <li>Lowercase only</li>
|
||||
* <li>Cannot include \, /, *, ?, ", <, >, |, ` ` (space character), ,, #</li>
|
||||
* <li>Cannot include \, /, *, ?, ", >, <, |, ` ` (space character), ,, #</li>
|
||||
* <li>Cannot start with -, _, +</li>
|
||||
* <li>Cannot be . or ..</li>
|
||||
* <li>Cannot be longer than 255 bytes (note it is bytes, so multi-byte characters will count towards the 255 limit
|
||||
|
@ -17,7 +17,7 @@ package org.springframework.data.elasticsearch.annotations;
|
||||
|
||||
/**
|
||||
* Values for the {@code dynamic} mapping parameter.
|
||||
*
|
||||
*
|
||||
* @author Sascha Woo
|
||||
* @since 4.3
|
||||
*/
|
||||
@ -25,26 +25,36 @@ public enum Dynamic {
|
||||
/**
|
||||
* New fields are added to the mapping.
|
||||
*/
|
||||
TRUE,
|
||||
TRUE("true"),
|
||||
/**
|
||||
* New fields are added to the mapping as
|
||||
* <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/runtime.html">runtime fields</a>. These
|
||||
* fields are not indexed, and are loaded from {@code _source} at query time.
|
||||
*/
|
||||
RUNTIME,
|
||||
RUNTIME("runtime"),
|
||||
/**
|
||||
* New fields are ignored. These fields will not be indexed or searchable, but will still appear in the
|
||||
* {@code _source} field of returned hits. These fields will not be added to the mapping, and new fields must be added
|
||||
* explicitly.
|
||||
*/
|
||||
FALSE,
|
||||
FALSE("false"),
|
||||
/**
|
||||
* If new fields are detected, an exception is thrown and the document is rejected. New fields must be explicitly
|
||||
* added to the mapping.
|
||||
*/
|
||||
STRICT,
|
||||
STRICT("strict"),
|
||||
/**
|
||||
* Inherit the dynamic setting from their parent object or from the mapping type.
|
||||
*/
|
||||
INHERIT
|
||||
INHERIT("nherit");
|
||||
|
||||
private final String mappedName;
|
||||
|
||||
Dynamic(String mappedName) {
|
||||
this.mappedName = mappedName;
|
||||
}
|
||||
|
||||
public String getMappedName() {
|
||||
return mappedName;
|
||||
}
|
||||
}
|
||||
|
@ -17,13 +17,23 @@ package org.springframework.data.elasticsearch.annotations;
|
||||
|
||||
/**
|
||||
* values for the {@link DynamicMapping annotation}
|
||||
*
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Sascha Woo
|
||||
* @since 4.0
|
||||
* @deprecated since 4.3, use {@link Document#dynamic()} or {@link Field#dynamic()} instead.
|
||||
* @deprecated since 4.3, use {@link Document#dynamic()} or {@link Field#dynamic()} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public enum DynamicMappingValue {
|
||||
True, False, Strict
|
||||
True("true"), False("false"), Strict("strict");
|
||||
|
||||
private final String mappedName;
|
||||
|
||||
DynamicMappingValue(String mappedName) {
|
||||
this.mappedName = mappedName;
|
||||
}
|
||||
|
||||
public String getMappedName() {
|
||||
return mappedName;
|
||||
}
|
||||
}
|
||||
|
@ -26,40 +26,51 @@ package org.springframework.data.elasticsearch.annotations;
|
||||
* @author Morgan Lutz
|
||||
*/
|
||||
public enum FieldType {
|
||||
Auto, //
|
||||
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, //
|
||||
Auto("auto"), //
|
||||
Text("text"), //
|
||||
Keyword("keyword"), //
|
||||
Long("long"), //
|
||||
Integer("integer"), //
|
||||
Short("short"), //
|
||||
Byte("byte"), //
|
||||
Double("double"), //
|
||||
Float("float"), //
|
||||
Half_Float("half_float"), //
|
||||
Scaled_Float("scaled_float"), //
|
||||
Date("date"), //
|
||||
Date_Nanos("date_nanos"), //
|
||||
Boolean("boolean"), //
|
||||
Binary("binary"), //
|
||||
Integer_Range("integer_range"), //
|
||||
Float_Range("float_range"), //
|
||||
Long_Range("long_range"), //
|
||||
Double_Range("double_range"), //
|
||||
Date_Range("date_range"), //
|
||||
Ip_Range("ip_range"), //
|
||||
Object("object"), //
|
||||
Nested("nested"), //
|
||||
Ip("ip"), //
|
||||
TokenCount("token_count"), //
|
||||
Percolator("percolator"), //
|
||||
Flattened("flattened"), //
|
||||
Search_As_You_Type("search_as_you_type"), //
|
||||
/** @since 4.1 */
|
||||
Rank_Feature, //
|
||||
Rank_Feature("rank_feature"), //
|
||||
/** @since 4.1 */
|
||||
Rank_Features, //
|
||||
Rank_Features("rank_features"), //
|
||||
/** since 4.2 */
|
||||
Wildcard, //
|
||||
Wildcard("wildcard"), //
|
||||
/** @since 4.2 */
|
||||
Dense_Vector //
|
||||
Dense_Vector("dense_vector") //
|
||||
;
|
||||
|
||||
private final String mappedName;
|
||||
|
||||
FieldType(String mappedName) {
|
||||
this.mappedName = mappedName;
|
||||
}
|
||||
|
||||
public String getMappedName() {
|
||||
return mappedName;
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@ -109,7 +110,8 @@ public abstract class AbstractElasticsearchRestTransportTemplate extends Abstrac
|
||||
List<SearchHits<T>> res = new ArrayList<>(queries.size());
|
||||
int c = 0;
|
||||
for (Query query : queries) {
|
||||
res.add(callback.doWith(SearchDocumentResponse.from(items[c++].getResponse(), documentCallback::doWith)));
|
||||
res.add(
|
||||
callback.doWith(SearchDocumentResponse.from(items[c++].getResponse(), getEntityCreator(documentCallback))));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@ -142,7 +144,7 @@ public abstract class AbstractElasticsearchRestTransportTemplate extends Abstrac
|
||||
index);
|
||||
|
||||
SearchResponse response = items[c++].getResponse();
|
||||
res.add(callback.doWith(SearchDocumentResponse.from(response, documentCallback::doWith)));
|
||||
res.add(callback.doWith(SearchDocumentResponse.from(response, getEntityCreator(documentCallback))));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@ -175,7 +177,7 @@ public abstract class AbstractElasticsearchRestTransportTemplate extends Abstrac
|
||||
index);
|
||||
|
||||
SearchResponse response = items[c++].getResponse();
|
||||
res.add(callback.doWith(SearchDocumentResponse.from(response, documentCallback::doWith)));
|
||||
res.add(callback.doWith(SearchDocumentResponse.from(response, getEntityCreator(documentCallback))));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@ -215,5 +217,9 @@ public abstract class AbstractElasticsearchRestTransportTemplate extends Abstrac
|
||||
return suggest(suggestion, getIndexCoordinatesFor(clazz));
|
||||
}
|
||||
|
||||
protected <T> SearchDocumentResponse.EntityCreator<T> getEntityCreator(ReadDocumentCallback<T> documentCallback) {
|
||||
return searchDocument -> CompletableFuture.completedFuture(documentCallback.doWith(searchDocument));
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
@ -64,9 +64,9 @@ public class ElasticsearchExceptionTranslator implements PersistenceExceptionTra
|
||||
}
|
||||
|
||||
if (elasticsearchException instanceof ElasticsearchStatusException) {
|
||||
ElasticsearchStatusException restStatusException = (ElasticsearchStatusException) elasticsearchException;
|
||||
return new RestStatusException(restStatusException.status().getStatus(), restStatusException.getMessage(),
|
||||
restStatusException.getCause());
|
||||
ElasticsearchStatusException elasticsearchStatusException = (ElasticsearchStatusException) elasticsearchException;
|
||||
return new RestStatusException(elasticsearchStatusException.status().getStatus(),
|
||||
elasticsearchStatusException.getMessage(), elasticsearchStatusException);
|
||||
}
|
||||
|
||||
return new UncategorizedElasticsearchException(ex.getMessage(), ex);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -319,7 +319,7 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchRestTranspor
|
||||
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<T>(elasticsearchConverter, clazz, index);
|
||||
SearchDocumentResponseCallback<SearchHits<T>> callback = new ReadSearchDocumentResponseCallback<>(clazz, index);
|
||||
|
||||
return callback.doWith(SearchDocumentResponse.from(response, documentCallback::doWith));
|
||||
return callback.doWith(SearchDocumentResponse.from(response, getEntityCreator(documentCallback)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -336,7 +336,7 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchRestTranspor
|
||||
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<T>(elasticsearchConverter, clazz, index);
|
||||
SearchDocumentResponseCallback<SearchScrollHits<T>> callback = new ReadSearchScrollDocumentResponseCallback<>(clazz,
|
||||
index);
|
||||
return callback.doWith(SearchDocumentResponse.from(response, documentCallback::doWith));
|
||||
return callback.doWith(SearchDocumentResponse.from(response, getEntityCreator(documentCallback)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -351,7 +351,7 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchRestTranspor
|
||||
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<T>(elasticsearchConverter, clazz, index);
|
||||
SearchDocumentResponseCallback<SearchScrollHits<T>> callback = new ReadSearchScrollDocumentResponseCallback<>(clazz,
|
||||
index);
|
||||
return callback.doWith(SearchDocumentResponse.from(response, documentCallback::doWith));
|
||||
return callback.doWith(SearchDocumentResponse.from(response, getEntityCreator(documentCallback)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -378,8 +378,8 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchRestTranspor
|
||||
Assert.isTrue(items.length == request.requests().size(), "Response should has same length with queries");
|
||||
return items;
|
||||
}
|
||||
// endregion
|
||||
|
||||
// endregion
|
||||
// region ClientCallback
|
||||
/**
|
||||
* Callback interface to be used with {@link #execute(ClientCallback)} for operating directly on
|
||||
|
@ -354,7 +354,7 @@ public class ElasticsearchTemplate extends AbstractElasticsearchRestTransportTem
|
||||
|
||||
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<T>(elasticsearchConverter, clazz, index);
|
||||
SearchDocumentResponseCallback<SearchHits<T>> callback = new ReadSearchDocumentResponseCallback<>(clazz, index);
|
||||
return callback.doWith(SearchDocumentResponse.from(response, documentCallback::doWith));
|
||||
return callback.doWith(SearchDocumentResponse.from(response, getEntityCreator(documentCallback)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -372,7 +372,7 @@ public class ElasticsearchTemplate extends AbstractElasticsearchRestTransportTem
|
||||
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<T>(elasticsearchConverter, clazz, index);
|
||||
SearchDocumentResponseCallback<SearchScrollHits<T>> callback = new ReadSearchScrollDocumentResponseCallback<>(clazz,
|
||||
index);
|
||||
return callback.doWith(SearchDocumentResponse.from(response, documentCallback::doWith));
|
||||
return callback.doWith(SearchDocumentResponse.from(response, getEntityCreator(documentCallback)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -389,7 +389,7 @@ public class ElasticsearchTemplate extends AbstractElasticsearchRestTransportTem
|
||||
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<T>(elasticsearchConverter, clazz, index);
|
||||
SearchDocumentResponseCallback<SearchScrollHits<T>> callback = new ReadSearchScrollDocumentResponseCallback<>(clazz,
|
||||
index);
|
||||
return callback.doWith(SearchDocumentResponse.from(response, documentCallback::doWith));
|
||||
return callback.doWith(SearchDocumentResponse.from(response, getEntityCreator(documentCallback)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2018-2021 the original author or authors.
|
||||
* Copyright 2018-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -23,7 +23,6 @@ import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.elasticsearch.Version;
|
||||
@ -771,15 +770,18 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
});
|
||||
}
|
||||
|
||||
private Mono<SearchDocumentResponse> doFindForResponse(Query query, Class<?> clazz, IndexCoordinates index) {
|
||||
private <T> Mono<SearchDocumentResponse> doFindForResponse(Query query, Class<?> clazz, IndexCoordinates index) {
|
||||
|
||||
return Mono.defer(() -> {
|
||||
SearchRequest request = requestFactory.searchRequest(query, clazz, index);
|
||||
request = prepareSearchRequest(request, false);
|
||||
|
||||
SearchDocumentCallback<?> documentCallback = new ReadSearchDocumentCallback<>(clazz, index);
|
||||
// noinspection unchecked
|
||||
SearchDocumentResponse.EntityCreator<T> entityCreator = searchDocument -> ((Mono<T>) documentCallback
|
||||
.toEntity(searchDocument)).toFuture();
|
||||
|
||||
return doFindForResponse(request, searchDocument -> documentCallback.toEntity(searchDocument).block());
|
||||
return doFindForResponse(request, entityCreator);
|
||||
});
|
||||
}
|
||||
|
||||
@ -896,19 +898,18 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
* Customization hook on the actual execution result {@link Mono}. <br />
|
||||
*
|
||||
* @param request the already prepared {@link SearchRequest} ready to be executed.
|
||||
* @param suggestEntityCreator
|
||||
* @param entityCreator
|
||||
* @return a {@link Mono} emitting the result of the operation converted to s {@link SearchDocumentResponse}.
|
||||
*/
|
||||
protected Mono<SearchDocumentResponse> doFindForResponse(SearchRequest request,
|
||||
Function<SearchDocument, ? extends Object> suggestEntityCreator) {
|
||||
protected <T> Mono<SearchDocumentResponse> doFindForResponse(SearchRequest request,
|
||||
SearchDocumentResponse.EntityCreator<T> entityCreator) {
|
||||
|
||||
if (QUERY_LOGGER.isDebugEnabled()) {
|
||||
QUERY_LOGGER.debug("Executing doFindForResponse: {}", request);
|
||||
}
|
||||
|
||||
return Mono.from(execute(client1 -> client1.searchForResponse(request))).map(searchResponse -> {
|
||||
return SearchDocumentResponse.from(searchResponse, suggestEntityCreator);
|
||||
});
|
||||
return Mono.from(execute(client -> client.searchForResponse(request)))
|
||||
.map(searchResponse -> SearchDocumentResponse.from(searchResponse, entityCreator));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -123,7 +123,6 @@ public interface ReactiveSearchOperations {
|
||||
/**
|
||||
* Search the index for entities matching the given {@link Query query}.
|
||||
*
|
||||
* @param <T>
|
||||
* @param query must not be {@literal null}.
|
||||
* @param entityType must not be {@literal null}.
|
||||
* @param <T>
|
||||
@ -137,7 +136,6 @@ public interface ReactiveSearchOperations {
|
||||
/**
|
||||
* Search the index for entities matching the given {@link Query query}.
|
||||
*
|
||||
* @param <T>
|
||||
* @param query must not be {@literal null}.
|
||||
* @param entityType must not be {@literal null}.
|
||||
* @param resultType the projection result type.
|
||||
@ -150,7 +148,6 @@ public interface ReactiveSearchOperations {
|
||||
/**
|
||||
* Search the index for entities matching the given {@link Query query}.
|
||||
*
|
||||
* @param <T>
|
||||
* @param query must not be {@literal null}.
|
||||
* @param entityType must not be {@literal null}.
|
||||
* @param index the target index, must not be {@literal null}
|
||||
@ -165,7 +162,6 @@ public interface ReactiveSearchOperations {
|
||||
/**
|
||||
* Search the index for entities matching the given {@link Query query}.
|
||||
*
|
||||
* @param <T>
|
||||
* @param query must not be {@literal null}.
|
||||
* @param entityType must not be {@literal null}.
|
||||
* @param resultType the projection result type.
|
||||
|
@ -24,6 +24,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.document.Document;
|
||||
import org.springframework.data.elasticsearch.core.document.NestedMetaData;
|
||||
@ -45,6 +46,7 @@ import org.springframework.util.Assert;
|
||||
* @author Mark Paluch
|
||||
* @author Roman Puchkovskiy
|
||||
* @author Matt Gilene
|
||||
* @author Sascha Woo
|
||||
* @since 4.0
|
||||
*/
|
||||
class SearchHitMapping<T> {
|
||||
@ -194,7 +196,7 @@ class SearchHitMapping<T> {
|
||||
*/
|
||||
private SearchHits<?> mapInnerDocuments(SearchHits<SearchDocument> searchHits, Class<T> type) {
|
||||
|
||||
if (searchHits.getTotalHits() == 0) {
|
||||
if (searchHits.isEmpty()) {
|
||||
return searchHits;
|
||||
}
|
||||
|
||||
@ -239,7 +241,7 @@ class SearchHitMapping<T> {
|
||||
searchHits.getSuggest());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("Could not map inner_hits", e);
|
||||
throw new UncategorizedElasticsearchException("Unable to convert inner hits.", e);
|
||||
}
|
||||
|
||||
return searchHits;
|
||||
|
@ -76,7 +76,10 @@ public abstract class AbstractRangePropertyValueConverter<T> extends AbstractPro
|
||||
public Object write(Object value) {
|
||||
|
||||
Assert.notNull(value, "value must not be null.");
|
||||
Assert.isInstanceOf(Range.class, value, "value must be instance of Range.");
|
||||
|
||||
if (!Range.class.isAssignableFrom(value.getClass())) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
try {
|
||||
Range<T> range = (Range<T>) value;
|
||||
|
@ -58,6 +58,10 @@ public class DatePropertyValueConverter extends AbstractPropertyValueConverter {
|
||||
@Override
|
||||
public Object write(Object value) {
|
||||
|
||||
if (!Date.class.isAssignableFrom(value.getClass())) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
try {
|
||||
return dateConverters.get(0).format((Date) value);
|
||||
} catch (Exception e) {
|
||||
|
@ -66,7 +66,6 @@ final public class ElasticsearchDateConverter {
|
||||
*/
|
||||
public static ElasticsearchDateConverter of(String pattern) {
|
||||
|
||||
Assert.notNull(pattern, "pattern must not be null");
|
||||
Assert.hasText(pattern, "pattern must not be empty");
|
||||
|
||||
String[] subPatterns = pattern.split("\\|\\|");
|
||||
@ -86,7 +85,7 @@ final public class ElasticsearchDateConverter {
|
||||
*/
|
||||
public String format(TemporalAccessor accessor) {
|
||||
|
||||
Assert.notNull("accessor", "accessor must not be null");
|
||||
Assert.notNull(accessor, "accessor must not be null");
|
||||
|
||||
if (accessor instanceof Instant) {
|
||||
Instant instant = (Instant) accessor;
|
||||
|
@ -1256,14 +1256,19 @@ public class MappingElasticsearchConverter
|
||||
PropertyValueConverter propertyValueConverter = Objects
|
||||
.requireNonNull(persistentProperty.getPropertyValueConverter());
|
||||
criteria.getQueryCriteriaEntries().forEach(criteriaEntry -> {
|
||||
Object value = criteriaEntry.getValue();
|
||||
if (value.getClass().isArray()) {
|
||||
Object[] objects = (Object[]) value;
|
||||
for (int i = 0; i < objects.length; i++) {
|
||||
objects[i] = propertyValueConverter.write(objects[i]);
|
||||
|
||||
if (criteriaEntry.getKey().hasValue()) {
|
||||
Object value = criteriaEntry.getValue();
|
||||
|
||||
if (value.getClass().isArray()) {
|
||||
Object[] objects = (Object[]) value;
|
||||
|
||||
for (int i = 0; i < objects.length; i++) {
|
||||
objects[i] = propertyValueConverter.write(objects[i]);
|
||||
}
|
||||
} else {
|
||||
criteriaEntry.setValue(propertyValueConverter.write(value));
|
||||
}
|
||||
} else {
|
||||
criteriaEntry.setValue(propertyValueConverter.write(value));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -61,6 +61,10 @@ public class TemporalPropertyValueConverter extends AbstractPropertyValueConvert
|
||||
@Override
|
||||
public Object write(Object value) {
|
||||
|
||||
if (!TemporalAccessor.class.isAssignableFrom(value.getClass())) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
try {
|
||||
return dateConverters.get(0).format((TemporalAccessor) value);
|
||||
} catch (Exception e) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-2021 the original author or authors.
|
||||
* Copyright 2019-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -17,8 +17,11 @@ package org.springframework.data.elasticsearch.core.document;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.lucene.search.TotalHits;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.common.text.Text;
|
||||
@ -38,13 +41,15 @@ import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* This represents the complete search response from Elasticsearch, including the returned documents. Instances must be
|
||||
* created with the {@link #from(SearchResponse,Function)} method.
|
||||
* created with the {@link #from(SearchResponse, EntityCreator)} method.
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.0
|
||||
*/
|
||||
public class SearchDocumentResponse {
|
||||
|
||||
private static final Log LOGGER = LogFactory.getLog(SearchDocumentResponse.class);
|
||||
|
||||
private final long totalHits;
|
||||
private final String totalHitsRelation;
|
||||
private final float maxScore;
|
||||
@ -98,12 +103,11 @@ public class SearchDocumentResponse {
|
||||
* creates a SearchDocumentResponse from the {@link SearchResponse}
|
||||
*
|
||||
* @param searchResponse must not be {@literal null}
|
||||
* @param suggestEntityCreator function to create an entity from a {@link SearchDocument}
|
||||
* @param entityCreator function to create an entity from a {@link SearchDocument}
|
||||
* @param <T> entity type
|
||||
* @return the SearchDocumentResponse
|
||||
*/
|
||||
public static <T> SearchDocumentResponse from(SearchResponse searchResponse,
|
||||
Function<SearchDocument, T> suggestEntityCreator) {
|
||||
public static <T> SearchDocumentResponse from(SearchResponse searchResponse, EntityCreator<T> entityCreator) {
|
||||
|
||||
Assert.notNull(searchResponse, "searchResponse must not be null");
|
||||
|
||||
@ -112,7 +116,7 @@ public class SearchDocumentResponse {
|
||||
Aggregations aggregations = searchResponse.getAggregations();
|
||||
org.elasticsearch.search.suggest.Suggest suggest = searchResponse.getSuggest();
|
||||
|
||||
return from(searchHits, scrollId, aggregations, suggest, suggestEntityCreator);
|
||||
return from(searchHits, scrollId, aggregations, suggest, entityCreator);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -122,14 +126,14 @@ public class SearchDocumentResponse {
|
||||
* @param scrollId scrollId
|
||||
* @param aggregations aggregations
|
||||
* @param suggestES the suggestion response from Elasticsearch
|
||||
* @param suggestEntityCreator function to create an entity from a {@link SearchDocument}
|
||||
* @param entityCreator function to create an entity from a {@link SearchDocument}
|
||||
* @param <T> entity type
|
||||
* @return the {@link SearchDocumentResponse}
|
||||
* @since 4.3
|
||||
*/
|
||||
public static <T> SearchDocumentResponse from(SearchHits searchHits, @Nullable String scrollId,
|
||||
@Nullable Aggregations aggregations, @Nullable org.elasticsearch.search.suggest.Suggest suggestES,
|
||||
Function<SearchDocument, T> suggestEntityCreator) {
|
||||
EntityCreator<T> entityCreator) {
|
||||
|
||||
TotalHits responseTotalHits = searchHits.getTotalHits();
|
||||
|
||||
@ -153,14 +157,14 @@ public class SearchDocumentResponse {
|
||||
}
|
||||
}
|
||||
|
||||
Suggest suggest = suggestFrom(suggestES, suggestEntityCreator);
|
||||
Suggest suggest = suggestFrom(suggestES, entityCreator);
|
||||
return new SearchDocumentResponse(totalHits, totalHitsRelation, maxScore, scrollId, searchDocuments, aggregations,
|
||||
suggest);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static <T> Suggest suggestFrom(@Nullable org.elasticsearch.search.suggest.Suggest suggestES,
|
||||
Function<SearchDocument, T> entityCreator) {
|
||||
EntityCreator<T> entityCreator) {
|
||||
|
||||
if (suggestES == null) {
|
||||
return null;
|
||||
@ -219,7 +223,19 @@ public class SearchDocumentResponse {
|
||||
List<CompletionSuggestion.Entry.Option<T>> options = new ArrayList<>();
|
||||
for (org.elasticsearch.search.suggest.completion.CompletionSuggestion.Entry.Option optionES : entryES) {
|
||||
SearchDocument searchDocument = optionES.getHit() != null ? DocumentAdapters.from(optionES.getHit()) : null;
|
||||
T hitEntity = searchDocument != null ? entityCreator.apply(searchDocument) : null;
|
||||
|
||||
T hitEntity = null;
|
||||
|
||||
if (searchDocument != null) {
|
||||
try {
|
||||
hitEntity = entityCreator.apply(searchDocument).get();
|
||||
} catch (Exception e) {
|
||||
if (LOGGER.isWarnEnabled()) {
|
||||
LOGGER.warn("Error creating entity from SearchDocument");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
options.add(new CompletionSuggestion.Entry.Option<T>(textToString(optionES.getText()),
|
||||
textToString(optionES.getHighlighted()), optionES.getScore(), optionES.collateMatch(),
|
||||
optionES.getContexts(), scoreDocFrom(optionES.getDoc()), searchDocument, hitEntity));
|
||||
@ -254,4 +270,14 @@ public class SearchDocumentResponse {
|
||||
private static String textToString(@Nullable Text text) {
|
||||
return text != null ? text.string() : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* A function to convert a {@link SearchDocument} async into an entity. Asynchronous so that it can be used from the
|
||||
* imperative and the reactive code.
|
||||
*
|
||||
* @param <T> the entity type
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface EntityCreator<T> extends Function<SearchDocument, CompletableFuture<T>> {}
|
||||
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ import org.springframework.data.elasticsearch.core.convert.GeoConverters;
|
||||
import org.springframework.data.elasticsearch.core.document.Document;
|
||||
|
||||
/**
|
||||
* Interface definition for structures defined in <a href="https://geojson.org/>GeoJSON</a> format. copied from Spring
|
||||
* Interface definition for structures defined in <a href="https://geojson.org/">GeoJSON</a> format. copied from Spring
|
||||
* Data Mongodb
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
|
@ -230,8 +230,7 @@ public class MappingBuilder {
|
||||
boolean writeNestedProperties = !isRootObject && (isAnyPropertyAnnotatedWithField(entity) || nestedOrObjectField);
|
||||
if (writeNestedProperties) {
|
||||
|
||||
String type = nestedOrObjectField ? fieldType.toString().toLowerCase()
|
||||
: FieldType.Object.toString().toLowerCase();
|
||||
String type = nestedOrObjectField ? fieldType.getMappedName() : FieldType.Object.getMappedName();
|
||||
|
||||
ObjectNode nestedObjectNode = objectMapper.createObjectNode();
|
||||
nestedObjectNode.put(FIELD_PARAM_TYPE, type);
|
||||
@ -247,9 +246,9 @@ public class MappingBuilder {
|
||||
}
|
||||
|
||||
if (entity != null && entity.dynamic() != Dynamic.INHERIT) {
|
||||
objectNode.put(TYPE_DYNAMIC, entity.dynamic().name().toLowerCase());
|
||||
objectNode.put(TYPE_DYNAMIC, entity.dynamic().getMappedName());
|
||||
} else if (dynamicMapping != null) {
|
||||
objectNode.put(TYPE_DYNAMIC, dynamicMapping.value().name().toLowerCase());
|
||||
objectNode.put(TYPE_DYNAMIC, dynamicMapping.value().getMappedName());
|
||||
}
|
||||
|
||||
ObjectNode propertiesNode = objectNode.putObject(FIELD_PROPERTIES);
|
||||
@ -418,7 +417,7 @@ public class MappingBuilder {
|
||||
|
||||
ObjectNode contextNode = contextsNode.addObject();
|
||||
contextNode.put(FIELD_CONTEXT_NAME, context.name());
|
||||
contextNode.put(FIELD_CONTEXT_TYPE, context.type().name().toLowerCase());
|
||||
contextNode.put(FIELD_CONTEXT_TYPE, context.type().getMappedName());
|
||||
|
||||
if (context.precision().length() > 0) {
|
||||
contextNode.put(FIELD_CONTEXT_PRECISION, context.precision());
|
||||
@ -450,7 +449,7 @@ public class MappingBuilder {
|
||||
}
|
||||
|
||||
propertiesNode.set(property.getFieldName(), objectMapper.createObjectNode() //
|
||||
.put(FIELD_PARAM_TYPE, field.type().name().toLowerCase()) //
|
||||
.put(FIELD_PARAM_TYPE, field.type().getMappedName()) //
|
||||
.put(MAPPING_ENABLED, false) //
|
||||
);
|
||||
|
||||
@ -479,9 +478,9 @@ public class MappingBuilder {
|
||||
|
||||
if (nestedOrObjectField) {
|
||||
if (annotation.dynamic() != Dynamic.INHERIT) {
|
||||
fieldNode.put(TYPE_DYNAMIC, annotation.dynamic().name().toLowerCase());
|
||||
fieldNode.put(TYPE_DYNAMIC, annotation.dynamic().getMappedName());
|
||||
} else if (dynamicMapping != null) {
|
||||
fieldNode.put(TYPE_DYNAMIC, dynamicMapping.value().name().toLowerCase());
|
||||
fieldNode.put(TYPE_DYNAMIC, dynamicMapping.value().getMappedName());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -530,9 +529,9 @@ public class MappingBuilder {
|
||||
|
||||
if (nestedOrObjectField) {
|
||||
if (annotation.mainField().dynamic() != Dynamic.INHERIT) {
|
||||
mainFieldNode.put(TYPE_DYNAMIC, annotation.mainField().dynamic().name().toLowerCase());
|
||||
mainFieldNode.put(TYPE_DYNAMIC, annotation.mainField().dynamic().getMappedName());
|
||||
} else if (dynamicMapping != null) {
|
||||
mainFieldNode.put(TYPE_DYNAMIC, dynamicMapping.value().name().toLowerCase());
|
||||
mainFieldNode.put(TYPE_DYNAMIC, dynamicMapping.value().getMappedName());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -237,9 +237,9 @@ public final class MappingParameters {
|
||||
}
|
||||
|
||||
if (type != FieldType.Auto) {
|
||||
objectNode.put(FIELD_PARAM_TYPE, type.toString().toLowerCase());
|
||||
objectNode.put(FIELD_PARAM_TYPE, type.getMappedName());
|
||||
|
||||
if (type == FieldType.Date) {
|
||||
if (type == FieldType.Date || type == FieldType.Date_Nanos || type == FieldType.Date_Range) {
|
||||
List<String> formats = new ArrayList<>();
|
||||
|
||||
// built-in formats
|
||||
|
@ -24,7 +24,8 @@ package org.springframework.data.elasticsearch.core.mapping;
|
||||
public interface PropertyValueConverter {
|
||||
|
||||
/**
|
||||
* Converts a property value to an elasticsearch value.
|
||||
* Converts a property value to an elasticsearch value. If the converter cannot convert the value, it must return a
|
||||
* String representation.
|
||||
*
|
||||
* @param value the value to convert, must not be {@literal null}
|
||||
* @return The elasticsearch property value, must not be {@literal null}
|
||||
|
@ -172,7 +172,7 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
|
||||
@Override
|
||||
public boolean writeTypeHints() {
|
||||
|
||||
boolean writeTypeHints = contextConfiguration.writeTypeHints;
|
||||
boolean writeTypeHints = contextConfiguration.getWriteTypeHints();
|
||||
|
||||
if (document != null) {
|
||||
switch (document.writeTypeHint()) {
|
||||
@ -548,7 +548,7 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
|
||||
/**
|
||||
* Configuration settings passed in from the creating {@link SimpleElasticsearchMappingContext}.
|
||||
*/
|
||||
static class ContextConfiguration {
|
||||
public static class ContextConfiguration {
|
||||
|
||||
private final FieldNamingStrategy fieldNamingStrategy;
|
||||
private final boolean writeTypeHints;
|
||||
@ -561,6 +561,10 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
|
||||
public FieldNamingStrategy getFieldNamingStrategy() {
|
||||
return fieldNamingStrategy;
|
||||
}
|
||||
|
||||
public boolean getWriteTypeHints() {
|
||||
return writeTypeHints;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -954,7 +954,23 @@ public class Criteria {
|
||||
/**
|
||||
* @since 4.3
|
||||
*/
|
||||
NOT_EMPTY
|
||||
NOT_EMPTY;
|
||||
|
||||
/**
|
||||
* @return true if this key does not have an associated value
|
||||
* @since 4.4
|
||||
*/
|
||||
public boolean hasNoValue() {
|
||||
return this == OperationKey.EXISTS || this == OperationKey.EMPTY || this == OperationKey.NOT_EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this key does have an associated value
|
||||
* @since 4.4
|
||||
*/
|
||||
public boolean hasValue() {
|
||||
return !hasNoValue();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -967,9 +983,8 @@ public class Criteria {
|
||||
|
||||
protected CriteriaEntry(OperationKey key) {
|
||||
|
||||
boolean keyIsValid = key == OperationKey.EXISTS || key == OperationKey.EMPTY || key == OperationKey.NOT_EMPTY;
|
||||
Assert.isTrue(keyIsValid,
|
||||
"key must be OperationKey.EXISTS, OperationKey.EMPTY or OperationKey.EMPTY for this call");
|
||||
Assert.isTrue(key.hasNoValue(),
|
||||
"key must be OperationKey.EXISTS, OperationKey.EMPTY or OperationKey.NOT_EMPTY for this call");
|
||||
|
||||
this.key = key;
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ import org.springframework.lang.Nullable;
|
||||
* @author Mohsin Husen
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Farid Faoudi
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update.html>docs</a>
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update.html">docs</a>
|
||||
*/
|
||||
public class UpdateQuery {
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
Spring Data Elasticsearch 4.3 GA (2021.1.0)
|
||||
Spring Data Elasticsearch 4.3.5 (2021.1.5)
|
||||
Copyright (c) [2013-2021] Pivotal Software, Inc.
|
||||
|
||||
This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
||||
@ -26,6 +26,11 @@ conditions of the subcomponent's license, as noted in the LICENSE file.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -2783,6 +2783,33 @@ public abstract class ElasticsearchTemplateTests {
|
||||
assertThat(searchHits.getSearchHit(1).getInnerHits("innerHits").getTotalHits()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test // #1997
|
||||
@DisplayName("should return document with inner hits size zero")
|
||||
void shouldReturnDocumentWithInnerHitsSizeZero() {
|
||||
|
||||
// given
|
||||
SampleEntity sampleEntity = SampleEntity.builder().id(nextIdAsString()).message("message 1").rate(1)
|
||||
.version(System.currentTimeMillis()).build();
|
||||
|
||||
List<IndexQuery> indexQueries = getIndexQueries(Arrays.asList(sampleEntity));
|
||||
|
||||
operations.bulkIndex(indexQueries, IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
|
||||
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery())
|
||||
.withCollapseBuilder(new CollapseBuilder("rate").setInnerHits(new InnerHitBuilder("innerHits").setSize(0)))
|
||||
.build();
|
||||
|
||||
// when
|
||||
SearchHits<SampleEntity> searchHits = operations.search(searchQuery, SampleEntity.class,
|
||||
IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
|
||||
// then
|
||||
assertThat(searchHits).isNotNull();
|
||||
assertThat(searchHits.getTotalHits()).isEqualTo(1);
|
||||
assertThat(searchHits.getSearchHits()).hasSize(1);
|
||||
assertThat(searchHits.getSearchHit(0).getContent().getMessage()).isEqualTo("message 1");
|
||||
}
|
||||
|
||||
private IndexQuery getIndexQuery(SampleEntity sampleEntity) {
|
||||
return new IndexQueryBuilder().withId(sampleEntity.getId()).withObject(sampleEntity)
|
||||
.withVersion(sampleEntity.getVersion()).build();
|
||||
|
@ -66,6 +66,9 @@ import org.springframework.data.elasticsearch.core.geo.GeoJsonPolygon;
|
||||
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
|
||||
import org.springframework.data.elasticsearch.core.mapping.PropertyValueConverter;
|
||||
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
|
||||
import org.springframework.data.elasticsearch.core.query.Criteria;
|
||||
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm;
|
||||
import org.springframework.data.geo.Box;
|
||||
import org.springframework.data.geo.Circle;
|
||||
@ -1496,6 +1499,16 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
assertThat(entity.getDontConvert()).isEqualTo("Monty Python's Flying Circus");
|
||||
}
|
||||
|
||||
@Test // #2080
|
||||
@DisplayName("should not try to call property converter on updating criteria exists")
|
||||
void shouldNotTryToCallPropertyConverterOnUpdatingCriteriaExists() {
|
||||
|
||||
// don't care if the query makes no sense, we just add all criteria without values
|
||||
Query query = new CriteriaQuery(Criteria.where("fieldWithClassBasedConverter").exists().empty().notEmpty());
|
||||
|
||||
mappingElasticsearchConverter.updateQuery(query, EntityWithCustomValueConverters.class);
|
||||
}
|
||||
|
||||
private Map<String, Object> writeToMap(Object source) {
|
||||
|
||||
Document sink = Document.create();
|
||||
@ -2429,6 +2442,7 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
return reverse(value);
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
private static String reverse(Object o) {
|
||||
|
||||
@ -2436,5 +2450,4 @@ public class MappingElasticsearchConverterUnitTests {
|
||||
|
||||
return new StringBuilder().append(o.toString()).reverse().toString();
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.convert;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.junit.jupiter.params.provider.Arguments.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Named;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.springframework.data.elasticsearch.annotations.DateFormat;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
|
||||
import org.springframework.data.elasticsearch.core.mapping.PropertyValueConverter;
|
||||
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
|
||||
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchPersistentEntity;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
public class PropertyValueConvertersUnitTests {
|
||||
|
||||
@ParameterizedTest(name = "{0}") // #2018
|
||||
@MethodSource("propertyValueConverters")
|
||||
@DisplayName("should return original object on write if it cannot be converted")
|
||||
void shouldReturnOriginalObjectOnWriteIfItCannotBeConverted(PropertyValueConverter converter) {
|
||||
|
||||
NoConverterForThisClass value = new NoConverterForThisClass();
|
||||
|
||||
Object written = converter.write(value);
|
||||
|
||||
assertThat(written).isEqualTo(value.toString());
|
||||
}
|
||||
|
||||
static Stream<Arguments> propertyValueConverters() {
|
||||
|
||||
SimpleElasticsearchMappingContext context = new SimpleElasticsearchMappingContext();
|
||||
SimpleElasticsearchPersistentEntity<?> persistentEntity = context
|
||||
.getRequiredPersistentEntity(NoConverterForThisClass.class);
|
||||
ElasticsearchPersistentProperty persistentProperty = persistentEntity.getRequiredPersistentProperty("property");
|
||||
|
||||
List<PropertyValueConverter> converters = new ArrayList<>();
|
||||
|
||||
converters.add(new DatePropertyValueConverter(persistentProperty,
|
||||
Collections.singletonList(ElasticsearchDateConverter.of(DateFormat.basic_date))));
|
||||
converters.add(new DateRangePropertyValueConverter(persistentProperty,
|
||||
Collections.singletonList(ElasticsearchDateConverter.of(DateFormat.basic_date))));
|
||||
converters.add(new NumberRangePropertyValueConverter(persistentProperty));
|
||||
converters.add(new TemporalPropertyValueConverter(persistentProperty,
|
||||
Collections.singletonList(ElasticsearchDateConverter.of(DateFormat.basic_date))));
|
||||
converters.add(new TemporalRangePropertyValueConverter(persistentProperty,
|
||||
Collections.singletonList(ElasticsearchDateConverter.of(DateFormat.basic_date))));
|
||||
|
||||
return converters.stream().map(propertyValueConverter -> arguments(
|
||||
Named.of(propertyValueConverter.getClass().getSimpleName(), propertyValueConverter)));
|
||||
}
|
||||
|
||||
static class NoConverterForThisClass {
|
||||
@SuppressWarnings("unused")
|
||||
@Nullable Long property;
|
||||
}
|
||||
}
|
@ -38,10 +38,14 @@ import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.assertj.core.data.Percentage;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.elasticsearch.annotations.*;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||
@ -57,6 +61,7 @@ import org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm;
|
||||
import org.springframework.data.elasticsearch.core.suggest.Completion;
|
||||
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
|
||||
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
|
||||
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
|
||||
import org.springframework.data.geo.Box;
|
||||
import org.springframework.data.geo.Circle;
|
||||
import org.springframework.data.geo.Point;
|
||||
@ -79,10 +84,25 @@ import org.springframework.test.context.ContextConfiguration;
|
||||
* @author Morgan Lutz
|
||||
*/
|
||||
@SpringIntegrationTest
|
||||
@ContextConfiguration(classes = { ElasticsearchRestTemplateConfiguration.class })
|
||||
@ContextConfiguration(classes = { MappingBuilderIntegrationTests.Config.class })
|
||||
public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
||||
|
||||
@Configuration
|
||||
@Import({ ElasticsearchRestTemplateConfiguration.class })
|
||||
static class Config {
|
||||
@Bean
|
||||
IndexNameProvider indexNameProvider() {
|
||||
return new IndexNameProvider("mapping-builder");
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired private ElasticsearchOperations operations;
|
||||
@Autowired IndexNameProvider indexNameProvider;
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
indexNameProvider.increment();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(java.lang.Integer.MAX_VALUE)
|
||||
@ -132,8 +152,7 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
||||
@Test // DATAES-76
|
||||
public void shouldAddSampleInheritedEntityDocumentToIndex() {
|
||||
// given
|
||||
IndexCoordinates index = IndexCoordinates.of("test-index-sample-inherited-mapping-builder");
|
||||
IndexOperations indexOps = operations.indexOps(index);
|
||||
IndexOperations indexOps = operations.indexOps(SampleInheritedEntity.class);
|
||||
|
||||
// when
|
||||
indexOps.create();
|
||||
@ -142,11 +161,10 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
||||
String message = "msg";
|
||||
String id = "abc";
|
||||
operations.index(new SampleInheritedEntityBuilder(id).createdDate(createdDate).message(message).buildIndex(),
|
||||
index);
|
||||
operations.indexOps(SampleInheritedEntity.class).refresh();
|
||||
IndexCoordinates.of(indexNameProvider.indexName()));
|
||||
|
||||
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).build();
|
||||
SearchHits<SampleInheritedEntity> result = operations.search(searchQuery, SampleInheritedEntity.class, index);
|
||||
SearchHits<SampleInheritedEntity> result = operations.search(searchQuery, SampleInheritedEntity.class);
|
||||
|
||||
// then
|
||||
assertThat(result).hasSize(1);
|
||||
@ -163,7 +181,7 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
||||
IndexOperations indexOpsUser = operations.indexOps(User.class);
|
||||
indexOpsUser.create();
|
||||
indexOpsUser.putMapping(User.class);
|
||||
|
||||
indexNameProvider.increment();
|
||||
IndexOperations indexOpsGroup = operations.indexOps(Group.class);
|
||||
indexOpsGroup.create();
|
||||
indexOpsGroup.putMapping(Group.class);
|
||||
@ -336,11 +354,19 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
||||
|
||||
}
|
||||
|
||||
@Test // #2024
|
||||
@DisplayName("should map all field type values")
|
||||
void shouldMapAllFieldTypeValues() {
|
||||
operations.indexOps(EntityWithAllTypes.class).createWithMapping();
|
||||
}
|
||||
|
||||
// region entities
|
||||
@Document(indexName = "ignore-above-index")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
static class IgnoreAboveEntity {
|
||||
@Nullable @Id private String id;
|
||||
@Nullable @Field(type = FieldType.Keyword, ignoreAbove = 10) private String message;
|
||||
@Nullable
|
||||
@Id private String id;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Keyword, ignoreAbove = 10) private String message;
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
@ -365,57 +391,76 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
||||
|
||||
@Document(indexName = "fieldname-index")
|
||||
static class IdEntity {
|
||||
@Nullable @Id @Field("id-property") private String id;
|
||||
@Nullable
|
||||
@Id
|
||||
@Field("id-property") private String id;
|
||||
}
|
||||
|
||||
@Document(indexName = "fieldname-index")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
static class TextEntity {
|
||||
|
||||
@Nullable @Id @Field("id-property") private String id;
|
||||
@Nullable
|
||||
@Id
|
||||
@Field("id-property") private String id;
|
||||
|
||||
@Field(name = "text-property", type = FieldType.Text) //
|
||||
@Nullable private String textProperty;
|
||||
}
|
||||
|
||||
@Document(indexName = "fieldname-index")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
static class MappingEntity {
|
||||
|
||||
@Nullable @Id @Field("id-property") private String id;
|
||||
@Nullable
|
||||
@Id
|
||||
@Field("id-property") private String id;
|
||||
|
||||
@Field("mapping-property") @Mapping(mappingPath = "/mappings/test-field-analyzed-mappings.json") //
|
||||
@Field("mapping-property")
|
||||
@Mapping(mappingPath = "/mappings/test-field-analyzed-mappings.json") //
|
||||
@Nullable private byte[] mappingProperty;
|
||||
}
|
||||
|
||||
@Document(indexName = "fieldname-index")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
static class GeoPointEntity {
|
||||
|
||||
@Nullable @Id @Field("id-property") private String id;
|
||||
@Nullable
|
||||
@Id
|
||||
@Field("id-property") private String id;
|
||||
|
||||
@Nullable @Field("geopoint-property") private GeoPoint geoPoint;
|
||||
@Nullable
|
||||
@Field("geopoint-property") private GeoPoint geoPoint;
|
||||
}
|
||||
|
||||
@Document(indexName = "fieldname-index")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
static class CircularEntity {
|
||||
|
||||
@Nullable @Id @Field("id-property") private String id;
|
||||
@Nullable
|
||||
@Id
|
||||
@Field("id-property") private String id;
|
||||
|
||||
@Nullable @Field(name = "circular-property", type = FieldType.Object, ignoreFields = { "circular-property" }) //
|
||||
@Nullable
|
||||
@Field(name = "circular-property", type = FieldType.Object, ignoreFields = { "circular-property" }) //
|
||||
private CircularEntity circularProperty;
|
||||
}
|
||||
|
||||
@Document(indexName = "fieldname-index")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
static class CompletionEntity {
|
||||
|
||||
@Nullable @Id @Field("id-property") private String id;
|
||||
@Nullable
|
||||
@Id
|
||||
@Field("id-property") private String id;
|
||||
|
||||
@Nullable @Field("completion-property") @CompletionField(maxInputLength = 100) //
|
||||
@Nullable
|
||||
@Field("completion-property")
|
||||
@CompletionField(maxInputLength = 100) //
|
||||
private Completion suggest;
|
||||
}
|
||||
|
||||
@Document(indexName = "fieldname-index")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
static class MultiFieldEntity {
|
||||
|
||||
@Nullable @Id @Field("id-property") private String id;
|
||||
@Nullable
|
||||
@Id
|
||||
@Field("id-property") private String id;
|
||||
|
||||
@Nullable //
|
||||
@MultiField(mainField = @Field(name = "main-field", type = FieldType.Text, analyzer = "whitespace"),
|
||||
@ -425,13 +470,17 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Document(indexName = "test-index-book-mapping-builder")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
static class Book {
|
||||
@Nullable @Id private String id;
|
||||
@Nullable
|
||||
@Id private String id;
|
||||
@Nullable private String name;
|
||||
@Nullable @Field(type = FieldType.Object) private Author author;
|
||||
@Nullable @Field(type = FieldType.Nested) private Map<Integer, Collection<String>> buckets = new HashMap<>();
|
||||
@Nullable @MultiField(mainField = @Field(type = FieldType.Text, analyzer = "whitespace"),
|
||||
@Nullable
|
||||
@Field(type = FieldType.Object) private Author author;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Nested) private Map<Integer, Collection<String>> buckets = new HashMap<>();
|
||||
@Nullable
|
||||
@MultiField(mainField = @Field(type = FieldType.Text, analyzer = "whitespace"),
|
||||
otherFields = { @InnerField(suffix = "prefix", type = FieldType.Text, analyzer = "stop",
|
||||
searchAnalyzer = "standard") }) private String description;
|
||||
|
||||
@ -481,11 +530,12 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Document(indexName = "test-index-simple-recursive-mapping-builder")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
static class SimpleRecursiveEntity {
|
||||
@Nullable @Id private String id;
|
||||
@Nullable @Field(type = FieldType.Object,
|
||||
ignoreFields = { "circularObject" }) private SimpleRecursiveEntity circularObject;
|
||||
@Nullable
|
||||
@Id private String id;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Object, ignoreFields = { "circularObject" }) private SimpleRecursiveEntity circularObject;
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
@ -506,12 +556,16 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Document(indexName = "test-copy-to-mapping-builder")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
static class CopyToEntity {
|
||||
@Nullable @Id private String id;
|
||||
@Nullable @Field(type = FieldType.Keyword, copyTo = "name") private String firstName;
|
||||
@Nullable @Field(type = FieldType.Keyword, copyTo = "name") private String lastName;
|
||||
@Nullable @Field(type = FieldType.Keyword) private String name;
|
||||
@Nullable
|
||||
@Id private String id;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Keyword, copyTo = "name") private String firstName;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Keyword, copyTo = "name") private String lastName;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Keyword) private String name;
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
@ -550,12 +604,15 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Document(indexName = "test-index-normalizer-mapping-builder")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
@Setting(settingPath = "/settings/test-normalizer.json")
|
||||
static class NormalizerEntity {
|
||||
@Nullable @Id private String id;
|
||||
@Nullable @Field(type = FieldType.Keyword, normalizer = "lower_case_normalizer") private String name;
|
||||
@Nullable @MultiField(mainField = @Field(type = FieldType.Text), otherFields = { @InnerField(suffix = "lower_case",
|
||||
@Nullable
|
||||
@Id private String id;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Keyword, normalizer = "lower_case_normalizer") private String name;
|
||||
@Nullable
|
||||
@MultiField(mainField = @Field(type = FieldType.Text), otherFields = { @InnerField(suffix = "lower_case",
|
||||
type = FieldType.Keyword, normalizer = "lower_case_normalizer") }) private String description;
|
||||
|
||||
@Nullable
|
||||
@ -610,10 +667,11 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Document(indexName = "test-index-sample-inherited-mapping-builder")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
static class SampleInheritedEntity extends AbstractInheritedEntity {
|
||||
|
||||
@Nullable @Field(type = Text, index = false, store = true, analyzer = "standard") private String message;
|
||||
@Nullable
|
||||
@Field(type = Text, index = false, store = true, analyzer = "standard") private String message;
|
||||
|
||||
@Nullable
|
||||
public String getMessage() {
|
||||
@ -656,11 +714,13 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Document(indexName = "test-index-stock-mapping-builder")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
static class StockPrice {
|
||||
@Nullable @Id private String id;
|
||||
@Nullable
|
||||
@Id private String id;
|
||||
@Nullable private String symbol;
|
||||
@Nullable @Field(type = FieldType.Double) private BigDecimal price;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Double) private BigDecimal price;
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
@ -691,8 +751,10 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
||||
}
|
||||
|
||||
static class AbstractInheritedEntity {
|
||||
@Nullable @Id private String id;
|
||||
@Nullable @Field(type = FieldType.Date, format = DateFormat.date_time, index = false) private Date createdDate;
|
||||
@Nullable
|
||||
@Id private String id;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Date, format = DateFormat.date_time, index = false) private Date createdDate;
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
@ -713,21 +775,27 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Document(indexName = "test-index-geo-mapping-builder")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
static class GeoEntity {
|
||||
@Nullable @Id private String id;
|
||||
@Nullable
|
||||
@Id private String id;
|
||||
// geo shape - Spring Data
|
||||
@Nullable private Box box;
|
||||
@Nullable private Circle circle;
|
||||
@Nullable private Polygon polygon;
|
||||
// geo point - Custom implementation + Spring Data
|
||||
@Nullable @GeoPointField private Point pointA;
|
||||
@Nullable
|
||||
@GeoPointField private Point pointA;
|
||||
@Nullable private GeoPoint pointB;
|
||||
@Nullable @GeoPointField private String pointC;
|
||||
@Nullable @GeoPointField private double[] pointD;
|
||||
@Nullable
|
||||
@GeoPointField private String pointC;
|
||||
@Nullable
|
||||
@GeoPointField private double[] pointD;
|
||||
// geo shape, until e have the classes for this, us a strng
|
||||
@Nullable @GeoShapeField private String shape1;
|
||||
@Nullable @GeoShapeField(coerce = true, ignoreMalformed = true, ignoreZValue = false,
|
||||
@Nullable
|
||||
@GeoShapeField private String shape1;
|
||||
@Nullable
|
||||
@GeoShapeField(coerce = true, ignoreMalformed = true, ignoreZValue = false,
|
||||
orientation = GeoShapeField.Orientation.clockwise) private String shape2;
|
||||
|
||||
@Nullable
|
||||
@ -821,17 +889,21 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Document(indexName = "test-index-user-mapping-builder")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
|
||||
static class User {
|
||||
@Nullable @Id private String id;
|
||||
@Nullable
|
||||
@Id private String id;
|
||||
|
||||
@Field(type = FieldType.Nested, ignoreFields = { "users" }) private Set<Group> groups = new HashSet<>();
|
||||
}
|
||||
|
||||
@Document(indexName = "test-index-group-mapping-builder")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
|
||||
static class Group {
|
||||
|
||||
@Nullable @Id String id;
|
||||
@Nullable
|
||||
@Id String id;
|
||||
|
||||
@Field(type = FieldType.Nested, ignoreFields = { "groups" }) private Set<User> users = new HashSet<>();
|
||||
}
|
||||
@ -848,11 +920,14 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Document(indexName = "completion")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
|
||||
static class CompletionDocument {
|
||||
@Nullable @Id private String id;
|
||||
@Nullable @CompletionField(contexts = { @CompletionContext(name = "location",
|
||||
type = CompletionContext.ContextMappingType.GEO, path = "proppath") }) private Completion suggest;
|
||||
@Nullable
|
||||
@Id private String id;
|
||||
@Nullable
|
||||
@CompletionField(contexts = { @CompletionContext(name = "location", type = CompletionContext.ContextMappingType.GEO,
|
||||
path = "proppath") }) private Completion suggest;
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
@ -873,9 +948,10 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Document(indexName = "test-index-entity-with-seq-no-primary-term-mapping-builder")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
static class EntityWithSeqNoPrimaryTerm {
|
||||
@Nullable @Field(type = Object) private SeqNoPrimaryTerm seqNoPrimaryTerm;
|
||||
@Nullable
|
||||
@Field(type = Object) private SeqNoPrimaryTerm seqNoPrimaryTerm;
|
||||
|
||||
@Nullable
|
||||
public SeqNoPrimaryTerm getSeqNoPrimaryTerm() {
|
||||
@ -888,10 +964,14 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
||||
}
|
||||
|
||||
static class RankFeatureEntity {
|
||||
@Nullable @Id private String id;
|
||||
@Nullable @Field(type = FieldType.Rank_Feature) private Integer pageRank;
|
||||
@Nullable @Field(type = FieldType.Rank_Feature, positiveScoreImpact = false) private Integer urlLength;
|
||||
@Nullable @Field(type = FieldType.Rank_Features) private Map<String, Integer> topics;
|
||||
@Nullable
|
||||
@Id private String id;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Rank_Feature) private Integer pageRank;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Rank_Feature, positiveScoreImpact = false) private Integer urlLength;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Rank_Features) private Map<String, Integer> topics;
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
@ -930,18 +1010,25 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Document(indexName = "termvectors-test")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
static class TermVectorFieldEntity {
|
||||
@Nullable @Id private String id;
|
||||
@Nullable @Field(type = FieldType.Text, termVector = TermVector.no) private String no;
|
||||
@Nullable @Field(type = FieldType.Text, termVector = TermVector.yes) private String yes;
|
||||
@Nullable @Field(type = FieldType.Text, termVector = TermVector.with_positions) private String with_positions;
|
||||
@Nullable @Field(type = FieldType.Text, termVector = TermVector.with_offsets) private String with_offsets;
|
||||
@Nullable @Field(type = FieldType.Text,
|
||||
termVector = TermVector.with_positions_offsets) private String with_positions_offsets;
|
||||
@Nullable @Field(type = FieldType.Text,
|
||||
@Nullable
|
||||
@Id private String id;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Text, termVector = TermVector.no) private String no;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Text, termVector = TermVector.yes) private String yes;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Text, termVector = TermVector.with_positions) private String with_positions;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Text, termVector = TermVector.with_offsets) private String with_offsets;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Text, termVector = TermVector.with_positions_offsets) private String with_positions_offsets;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Text,
|
||||
termVector = TermVector.with_positions_payloads) private String with_positions_payloads;
|
||||
@Nullable @Field(type = FieldType.Text,
|
||||
@Nullable
|
||||
@Field(type = FieldType.Text,
|
||||
termVector = TermVector.with_positions_offsets_payloads) private String with_positions_offsets_payloads;
|
||||
|
||||
@Nullable
|
||||
@ -1017,10 +1104,12 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Document(indexName = "wildcard-test")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
static class WildcardEntity {
|
||||
@Nullable @Field(type = Wildcard) private String wildcardWithoutParams;
|
||||
@Nullable @Field(type = Wildcard, nullValue = "WILD", ignoreAbove = 42) private String wildcardWithParams;
|
||||
@Nullable
|
||||
@Field(type = Wildcard) private String wildcardWithoutParams;
|
||||
@Nullable
|
||||
@Field(type = Wildcard, nullValue = "WILD", ignoreAbove = 42) private String wildcardWithParams;
|
||||
|
||||
@Nullable
|
||||
public String getWildcardWithoutParams() {
|
||||
@ -1041,11 +1130,13 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Document(indexName = "disabled-entity-mapping")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
@Mapping(enabled = false)
|
||||
static class DisabledMappingEntity {
|
||||
@Nullable @Id private String id;
|
||||
@Nullable @Field(type = Text) private String text;
|
||||
@Nullable
|
||||
@Id private String id;
|
||||
@Nullable
|
||||
@Field(type = Text) private String text;
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
@ -1068,9 +1159,13 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
||||
|
||||
@Document(indexName = "disabled-property-mapping")
|
||||
static class DisabledMappingProperty {
|
||||
@Nullable @Id private String id;
|
||||
@Nullable @Field(type = Text) private String text;
|
||||
@Nullable @Mapping(enabled = false) @Field(type = Object) private Object object;
|
||||
@Nullable
|
||||
@Id private String id;
|
||||
@Nullable
|
||||
@Field(type = Text) private String text;
|
||||
@Nullable
|
||||
@Mapping(enabled = false)
|
||||
@Field(type = Object) private Object object;
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
@ -1100,10 +1195,12 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Document(indexName = "densevector-test")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
static class DenseVectorEntity {
|
||||
@Nullable @Id private String id;
|
||||
@Nullable @Field(type = Dense_Vector, dims = 3) private float[] dense_vector;
|
||||
@Nullable
|
||||
@Id private String id;
|
||||
@Nullable
|
||||
@Field(type = Dense_Vector, dims = 3) private float[] dense_vector;
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
@ -1124,15 +1221,19 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Document(indexName = "dynamic-mapping-annotation")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
@DynamicMapping(DynamicMappingValue.False)
|
||||
static class DynamicMappingAnnotationEntity {
|
||||
|
||||
@Nullable @DynamicMapping(DynamicMappingValue.Strict) @Field(type = FieldType.Object) private Author author;
|
||||
@Nullable @DynamicMapping(DynamicMappingValue.False) @Field(
|
||||
type = FieldType.Object) private Map<String, Object> objectMap;
|
||||
@Nullable @DynamicMapping(DynamicMappingValue.False) @Field(
|
||||
type = FieldType.Nested) private List<Map<String, Object>> nestedObjectMap;
|
||||
@Nullable
|
||||
@DynamicMapping(DynamicMappingValue.Strict)
|
||||
@Field(type = FieldType.Object) private Author author;
|
||||
@Nullable
|
||||
@DynamicMapping(DynamicMappingValue.False)
|
||||
@Field(type = FieldType.Object) private Map<String, Object> objectMap;
|
||||
@Nullable
|
||||
@DynamicMapping(DynamicMappingValue.False)
|
||||
@Field(type = FieldType.Nested) private List<Map<String, Object>> nestedObjectMap;
|
||||
|
||||
@Nullable
|
||||
public Author getAuthor() {
|
||||
@ -1144,57 +1245,140 @@ public class MappingBuilderIntegrationTests extends MappingContextBaseTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Document(indexName = "dynamic-mapping", dynamic = Dynamic.FALSE)
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
static class DynamicMappingEntity {
|
||||
|
||||
@Nullable @Field(type = FieldType.Object) //
|
||||
@Nullable
|
||||
@Field(type = FieldType.Object) //
|
||||
private Map<String, Object> objectInherit;
|
||||
@Nullable @Field(type = FieldType.Object, dynamic = Dynamic.FALSE) //
|
||||
@Nullable
|
||||
@Field(type = FieldType.Object, dynamic = Dynamic.FALSE) //
|
||||
private Map<String, Object> objectFalse;
|
||||
@Nullable @Field(type = FieldType.Object, dynamic = Dynamic.TRUE) //
|
||||
@Nullable
|
||||
@Field(type = FieldType.Object, dynamic = Dynamic.TRUE) //
|
||||
private Map<String, Object> objectTrue;
|
||||
@Nullable @Field(type = FieldType.Object, dynamic = Dynamic.STRICT) //
|
||||
@Nullable
|
||||
@Field(type = FieldType.Object, dynamic = Dynamic.STRICT) //
|
||||
private Map<String, Object> objectStrict;
|
||||
@Nullable @Field(type = FieldType.Object, dynamic = Dynamic.RUNTIME) //
|
||||
@Nullable
|
||||
@Field(type = FieldType.Object, dynamic = Dynamic.RUNTIME) //
|
||||
private Map<String, Object> objectRuntime;
|
||||
@Nullable @Field(type = FieldType.Nested) //
|
||||
@Nullable
|
||||
@Field(type = FieldType.Nested) //
|
||||
private List<Map<String, Object>> nestedObjectInherit;
|
||||
@Nullable @Field(type = FieldType.Nested, dynamic = Dynamic.FALSE) //
|
||||
@Nullable
|
||||
@Field(type = FieldType.Nested, dynamic = Dynamic.FALSE) //
|
||||
private List<Map<String, Object>> nestedObjectFalse;
|
||||
@Nullable @Field(type = FieldType.Nested, dynamic = Dynamic.TRUE) //
|
||||
@Nullable
|
||||
@Field(type = FieldType.Nested, dynamic = Dynamic.TRUE) //
|
||||
private List<Map<String, Object>> nestedObjectTrue;
|
||||
@Nullable @Field(type = FieldType.Nested, dynamic = Dynamic.STRICT) //
|
||||
@Nullable
|
||||
@Field(type = FieldType.Nested, dynamic = Dynamic.STRICT) //
|
||||
private List<Map<String, Object>> nestedObjectStrict;
|
||||
@Nullable @Field(type = FieldType.Nested, dynamic = Dynamic.RUNTIME) //
|
||||
@Nullable
|
||||
@Field(type = FieldType.Nested, dynamic = Dynamic.RUNTIME) //
|
||||
private List<Map<String, Object>> nestedObjectRuntime;
|
||||
}
|
||||
|
||||
@Document(indexName = "dynamic-detection-mapping-true")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
@Mapping(dateDetection = Mapping.Detection.TRUE, numericDetection = Mapping.Detection.TRUE,
|
||||
dynamicDateFormats = { "MM/dd/yyyy" })
|
||||
private static class DynamicDetectionMapping {
|
||||
@Id @Nullable private String id;
|
||||
@Id
|
||||
@Nullable private String id;
|
||||
}
|
||||
|
||||
@Document(indexName = "runtime-fields")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
@Mapping(runtimeFieldsPath = "/mappings/runtime-fields.json")
|
||||
private static class RuntimeFieldEntity {
|
||||
@Id @Nullable private String id;
|
||||
@Field(type = Date, format = DateFormat.epoch_millis, name = "@timestamp") @Nullable private Instant timestamp;
|
||||
@Id
|
||||
@Nullable private String id;
|
||||
@Field(type = Date, format = DateFormat.epoch_millis, name = "@timestamp")
|
||||
@Nullable private Instant timestamp;
|
||||
}
|
||||
|
||||
@Document(indexName = "fields-excluded-from-source")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
private static class ExcludedFieldEntity {
|
||||
@Id @Nullable private String id;
|
||||
@Nullable @Field(name = "excluded-date", type = Date, format = DateFormat.date,
|
||||
@Id
|
||||
@Nullable private String id;
|
||||
@Nullable
|
||||
@Field(name = "excluded-date", type = Date, format = DateFormat.date,
|
||||
excludeFromSource = true) private LocalDate excludedDate;
|
||||
@Nullable @Field(type = Nested) private NestedExcludedFieldEntity nestedEntity;
|
||||
@Nullable
|
||||
@Field(type = Nested) private NestedExcludedFieldEntity nestedEntity;
|
||||
}
|
||||
|
||||
private static class NestedExcludedFieldEntity {
|
||||
@Nullable @Field(name = "excluded-text", type = Text, excludeFromSource = true) private String excludedText;
|
||||
@Nullable
|
||||
@Field(name = "excluded-text", type = Text, excludeFromSource = true) private String excludedText;
|
||||
}
|
||||
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
private static class EntityWithAllTypes {
|
||||
@Nullable
|
||||
@Field(type = FieldType.Auto) String autoField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Text) String textField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Keyword) String keywordField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Long) String longField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Integer) String integerField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Short) String shortField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Byte) String byteField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Double) String doubleField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Float) String floatField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Half_Float) String halfFloatField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Scaled_Float) String scaledFloatField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Date) String dateField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Date_Nanos) String dateNanosField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Boolean) String booleanField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Binary) String binaryField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Integer_Range) String integerRangeField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Float_Range) String floatRangeField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Long_Range) String longRangeField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Double_Range) String doubleRangeField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Date_Range) String dateRangeField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Ip_Range) String ipRangeField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Object) String objectField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Nested) String nestedField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Ip) String ipField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.TokenCount, analyzer = "standard") String tokenCountField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Percolator) String percolatorField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Flattened) String flattenedField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Search_As_You_Type) String searchAsYouTypeField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Rank_Feature) String rankFeatureField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Rank_Features) String rankFeaturesField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Wildcard) String wildcardField;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Dense_Vector, dims = 1) String denseVectorField;
|
||||
}
|
||||
// endregion
|
||||
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.Transient;
|
||||
import org.springframework.data.elasticsearch.annotations.*;
|
||||
import org.springframework.data.elasticsearch.core.MappingContextBaseTests;
|
||||
import org.springframework.data.elasticsearch.core.Range;
|
||||
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
|
||||
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
|
||||
import org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm;
|
||||
@ -721,6 +722,29 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
|
||||
assertEquals(expected, mapping, false);
|
||||
}
|
||||
|
||||
@Test // #2102
|
||||
@DisplayName("should write date formats for date range fields")
|
||||
void shouldWriteDateFormatsForDateRangeFields() throws JSONException {
|
||||
|
||||
String expected = "{\n" + //
|
||||
" \"properties\": {\n" + //
|
||||
" \"_class\": {\n" + //
|
||||
" \"type\": \"keyword\",\n" + //
|
||||
" \"index\": false,\n" + //
|
||||
" \"doc_values\": false\n" + //
|
||||
" },\n" + //
|
||||
" \"field2\": {\n" + //
|
||||
" \"type\": \"date_range\",\n" + //
|
||||
" \"format\": \"date\"\n" + //
|
||||
" }\n" + //
|
||||
" }\n" + //
|
||||
"}\n"; //
|
||||
|
||||
String mapping = getMappingBuilder().buildPropertyMapping(DateRangeEntity.class);
|
||||
|
||||
assertEquals(expected, mapping, false);
|
||||
}
|
||||
|
||||
@Test // #1454
|
||||
@DisplayName("should write type hints when context is configured to do so")
|
||||
void shouldWriteTypeHintsWhenContextIsConfiguredToDoSo() throws JSONException {
|
||||
@ -1911,6 +1935,31 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
|
||||
}
|
||||
}
|
||||
|
||||
private static class DateRangeEntity {
|
||||
@Nullable
|
||||
@Id private String id;
|
||||
@Nullable
|
||||
@Field(type = Date_Range, format = DateFormat.date) private Range<LocalDateTime> field2;
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(@Nullable String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Range<LocalDateTime> getField2() {
|
||||
return field2;
|
||||
}
|
||||
|
||||
public void setField2(@Nullable Range<LocalDateTime> field2) {
|
||||
this.field2 = field2;
|
||||
}
|
||||
}
|
||||
|
||||
@Document(indexName = "magazine")
|
||||
private static class Magazine {
|
||||
@Id @Nullable private String id;
|
||||
|
@ -265,23 +265,30 @@ public class SimpleElasticsearchPersistentPropertyUnitTests {
|
||||
|
||||
// region entities
|
||||
static class FieldNameProperty {
|
||||
@Nullable @Field(name = "by-name") String fieldProperty;
|
||||
@Nullable
|
||||
@Field(name = "by-name") String fieldProperty;
|
||||
}
|
||||
|
||||
static class FieldValueProperty {
|
||||
@Nullable @Field(value = "by-value") String fieldProperty;
|
||||
@Nullable
|
||||
@Field(value = "by-value") String fieldProperty;
|
||||
}
|
||||
|
||||
static class MultiFieldProperty {
|
||||
@Nullable @MultiField(mainField = @Field("mainfield"),
|
||||
@Nullable
|
||||
@MultiField(mainField = @Field("mainfield"),
|
||||
otherFields = { @InnerField(suffix = "suff", type = FieldType.Keyword) }) String mainfieldProperty;
|
||||
}
|
||||
|
||||
static class DatesProperty {
|
||||
@Nullable @Field(type = FieldType.Date, format = {}, pattern = "dd.MM.uuuu") LocalDate localDate;
|
||||
@Nullable @Field(type = FieldType.Date, format = DateFormat.basic_date_time) LocalDateTime localDateTime;
|
||||
@Nullable @Field(type = FieldType.Date, format = DateFormat.basic_date_time) Date legacyDate;
|
||||
@Nullable @Field(type = FieldType.Date, format = {}, pattern = "dd.MM.uuuu") List<LocalDate> localDateList;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Date, format = {}, pattern = "dd.MM.uuuu") LocalDate localDate;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Date, format = DateFormat.basic_date_time) LocalDateTime localDateTime;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Date, format = DateFormat.basic_date_time) Date legacyDate;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Date, format = {}, pattern = "dd.MM.uuuu") List<LocalDate> localDateList;
|
||||
}
|
||||
|
||||
static class SeqNoPrimaryTermProperty {
|
||||
@ -344,8 +351,10 @@ public class SimpleElasticsearchPersistentPropertyUnitTests {
|
||||
|
||||
private static class EntityWithCustomValueConverters {
|
||||
@Id private String id;
|
||||
@Nullable @ValueConverter(ClassBasedValueConverter.class) private String fieldWithClassBasedConverter;
|
||||
@Nullable @ValueConverter(EnumBasedValueConverter.class) private String fieldWithEnumBasedConverter;
|
||||
@Nullable
|
||||
@ValueConverter(ClassBasedValueConverter.class) private String fieldWithClassBasedConverter;
|
||||
@Nullable
|
||||
@ValueConverter(EnumBasedValueConverter.class) private String fieldWithEnumBasedConverter;
|
||||
}
|
||||
|
||||
private static class ClassBasedValueConverter implements PropertyValueConverter {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -17,6 +17,7 @@ package org.springframework.data.elasticsearch.core.suggest;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -39,8 +40,8 @@ import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.query.IndexQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.core.suggest.response.CompletionSuggestion;
|
||||
import org.springframework.data.elasticsearch.core.suggest.response.Suggest;
|
||||
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchRestTemplateConfiguration;
|
||||
@ -86,13 +87,11 @@ public class ReactiveElasticsearchTemplateSuggestIntegrationTests {
|
||||
@DisplayName("should find suggestions for given prefix completion")
|
||||
void shouldFindSuggestionsForGivenPrefixCompletion() {
|
||||
|
||||
loadCompletionObjectEntities();
|
||||
|
||||
NativeSearchQuery query = new NativeSearchQueryBuilder().withSuggestBuilder(new SuggestBuilder()
|
||||
.addSuggestion("test-suggest", SuggestBuilders.completionSuggestion("suggest").prefix("m", Fuzziness.AUTO)))
|
||||
.build();
|
||||
|
||||
operations.suggest(query, CompletionEntity.class) //
|
||||
loadCompletionObjectEntities() //
|
||||
.flatMap(unused -> {
|
||||
Query query = getSuggestQuery("test-suggest", "suggest", "m");
|
||||
return operations.suggest(query, CompletionEntity.class);
|
||||
}) //
|
||||
.as(StepVerifier::create) //
|
||||
.assertNext(suggest -> {
|
||||
Suggest.Suggestion<? extends Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option>> suggestion = suggest
|
||||
@ -105,13 +104,21 @@ public class ReactiveElasticsearchTemplateSuggestIntegrationTests {
|
||||
assertThat(options).hasSize(2);
|
||||
assertThat(options.get(0).getText()).isIn("Marchand", "Mohsin");
|
||||
assertThat(options.get(1).getText()).isIn("Marchand", "Mohsin");
|
||||
|
||||
}) //
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
protected Query getSuggestQuery(String suggestionName, String fieldName, String prefix) {
|
||||
return new NativeSearchQueryBuilder() //
|
||||
.withSuggestBuilder(new SuggestBuilder() //
|
||||
.addSuggestion(suggestionName, //
|
||||
SuggestBuilders.completionSuggestion(fieldName) //
|
||||
.prefix(prefix, Fuzziness.AUTO))) //
|
||||
.build(); //
|
||||
}
|
||||
|
||||
// region helper functions
|
||||
private void loadCompletionObjectEntities() {
|
||||
private Mono<CompletionEntity> loadCompletionObjectEntities() {
|
||||
|
||||
CompletionEntity rizwan_idrees = new CompletionEntityBuilder("1").name("Rizwan Idrees")
|
||||
.suggest(new String[] { "Rizwan Idrees" }).build();
|
||||
@ -124,7 +131,7 @@ public class ReactiveElasticsearchTemplateSuggestIntegrationTests {
|
||||
List<CompletionEntity> entities = new ArrayList<>(
|
||||
Arrays.asList(rizwan_idrees, franck_marchand, mohsin_husen, artur_konczak));
|
||||
IndexCoordinates index = IndexCoordinates.of(indexNameProvider.indexName());
|
||||
operations.saveAll(entities, index).blockLast();
|
||||
return operations.saveAll(entities, index).last();
|
||||
}
|
||||
// endregion
|
||||
|
||||
@ -132,11 +139,13 @@ public class ReactiveElasticsearchTemplateSuggestIntegrationTests {
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
static class CompletionEntity {
|
||||
|
||||
@Nullable @Id private String id;
|
||||
@Nullable
|
||||
@Id private String id;
|
||||
|
||||
@Nullable private String name;
|
||||
|
||||
@Nullable @CompletionField(maxInputLength = 100) private Completion suggest;
|
||||
@Nullable
|
||||
@CompletionField(maxInputLength = 100) private Completion suggest;
|
||||
|
||||
private CompletionEntity() {}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user