1
0
mirror of synced 2026-07-05 09:40:00 +00:00

Compare commits

...

31 Commits

Author SHA1 Message Date
Jens Schauder 950ca0fc2a Release version 5.3.4 (2024.0.4).
See #2966
2024-09-13 11:36:55 +02:00
Jens Schauder 95a86f558b Prepare 5.3.4 (2024.0.4).
See #2966
2024-09-13 11:36:35 +02:00
Peter-Josef Meisch 8117e5a174 Remove Blockhound
Original Pull Request #2978
Closes #2977

(cherry picked from commit d06c122fd5)
2024-09-04 18:17:42 +02:00
HAN SEUNGWOO 3a9a959918 Set refresh on DeleteByQueryRequest by DeleteQuery.
Original Pull Request #2976
Closes #2973

(cherry picked from commit b1b232d354)
2024-09-03 20:27:14 +02:00
Peter-Josef Meisch a179dd0643 Add excludeFromSource handling to multifield.
Original Pull Request #2975
Closes #2971

(cherry picked from commit 555b570246)
2024-08-31 21:25:40 +02:00
Peter-Josef Meisch 310ea07c6f Update versions.adoc 2024-08-19 20:27:42 +02:00
Jens Schauder 34a277cd7d After release cleanups.
See #2939
2024-08-16 10:08:54 +02:00
Jens Schauder 878dc029ec Prepare next development iteration.
See #2939
2024-08-16 10:08:53 +02:00
Jens Schauder c931812c6a Release version 5.3.3 (2024.0.3).
See #2939
2024-08-16 10:05:57 +02:00
Jens Schauder c514e020b8 Prepare 5.3.3 (2024.0.3).
See #2939
2024-08-16 10:05:39 +02:00
Mark Paluch 31a4ad715f Upgrade to Maven Wrapper 3.9.8.
See #2959
2024-08-08 10:23:16 +02:00
Mark Paluch 03efe1b910 Update CI properties.
See #2939
2024-08-08 10:19:20 +02:00
Peter-Josef Meisch 9d5d2efb40 Update versions.adoc 2024-08-07 18:41:12 +02:00
Eric Haag b7266961d9 Migrate build to Spring Develocity Conventions extension.
* Migrate build to Spring Develocity Conventions extension.

* Adopt Develocity environment variables.

Closes #2944
2024-08-01 14:54:23 +02:00
Mark Paluch d96cd02572 Bundle Javadoc with Antora documentation site.
Closes #2948.
2024-07-31 14:53:25 +02:00
Jens Schauder ceb0225850 After release cleanups.
See #2932
2024-07-12 19:12:15 +02:00
Jens Schauder 5c59f73e00 Prepare next development iteration.
See #2932
2024-07-12 19:12:14 +02:00
Jens Schauder 44b1c9e848 Release version 5.3.2 (2024.0.2).
See #2932
2024-07-12 19:09:19 +02:00
Jens Schauder 1770f98a74 Prepare 5.3.2 (2024.0.2).
See #2932
2024-07-12 19:09:01 +02:00
Peter-Josef Meisch bad0a80313 Enable use of search_after with field_collapse.
Original Pull Request #2937
Closes #2935

(cherry picked from commit dd156b9e29)
2024-07-06 11:37:51 +02:00
Peter-Josef Meisch 92dd6e8599 Update migration-guide-5.2-5.3.adoc 2024-07-04 20:58:41 +02:00
Mark Paluch c793be8ab4 Switch to Broadcom docker proxy.
Closes #2934
2024-06-20 11:21:08 +02:00
Mark Paluch 07ae79f9ce After release cleanups.
See #2913
2024-06-14 10:48:00 +02:00
Mark Paluch 47c84b84af Prepare next development iteration.
See #2913
2024-06-14 10:47:59 +02:00
Mark Paluch 5ba1e5dc77 Release version 5.3.1 (2024.0.1).
See #2913
2024-06-14 10:45:40 +02:00
Mark Paluch 5ddcd55942 Prepare 5.3.1 (2024.0.1).
See #2913
2024-06-14 10:45:24 +02:00
Peter-Josef Meisch be4a77ad21 Update nav.adoc, add loink to migration guide 5.2 to 5.3 2024-05-27 19:39:26 +02:00
Peter-Josef Meisch 7fa3cb74a1 Upgrade to Elasticsearch 8.13.4.
Original Pull Request #2917
Closes #2916
2024-05-18 18:43:34 +00:00
Peter-Josef Meisch ba9edf8ec8 Fix max dim value for dense vector.
Closes #2911

(cherry picked from commit e997b39f68)
2024-05-18 18:26:23 +02:00
Mark Paluch e4a39ae285 After release cleanups.
See #2896
2024-05-17 12:03:29 +02:00
Mark Paluch 7392222793 Prepare next development iteration.
See #2896
2024-05-17 11:51:48 +02:00
34 changed files with 302 additions and 302 deletions
+1 -2
View File
@@ -30,7 +30,6 @@ target
build/ build/
node_modules node_modules
node node
package.json
package-lock.json package-lock.json
.mvn/.gradle-enterprise .mvn/.develocity
+3 -8
View File
@@ -1,13 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<extensions> <extensions>
<extension> <extension>
<groupId>com.gradle</groupId> <groupId>io.spring.develocity.conventions</groupId>
<artifactId>gradle-enterprise-maven-extension</artifactId> <artifactId>develocity-conventions-maven-extension</artifactId>
<version>1.19.2</version> <version>0.0.19</version>
</extension>
<extension>
<groupId>com.gradle</groupId>
<artifactId>common-custom-user-data-maven-extension</artifactId>
<version>1.12.4</version>
</extension> </extension>
</extensions> </extensions>
-31
View File
@@ -1,31 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<gradleEnterprise
xmlns="https://www.gradle.com/gradle-enterprise-maven" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://www.gradle.com/gradle-enterprise-maven https://www.gradle.com/schema/gradle-enterprise-maven.xsd">
<server>
<url>https://ge.spring.io</url>
</server>
<buildScan>
<backgroundBuildScanUpload>false</backgroundBuildScanUpload>
<captureGoalInputFiles>true</captureGoalInputFiles>
<publishIfAuthenticated>true</publishIfAuthenticated>
<obfuscation>
<ipAddresses>#{{'0.0.0.0'}}</ipAddresses>
</obfuscation>
</buildScan>
<buildCache>
<local>
<enabled>true</enabled>
</local>
<remote>
<server>
<credentials>
<username>${env.GRADLE_ENTERPRISE_CACHE_USERNAME}</username>
<password>${env.GRADLE_ENTERPRISE_CACHE_PASSWORD}</password>
</credentials>
</server>
<enabled>true</enabled>
<storeEnabled>#{env['GRADLE_ENTERPRISE_CACHE_USERNAME'] != null and env['GRADLE_ENTERPRISE_CACHE_PASSWORD'] != null}</storeEnabled>
</remote>
</buildCache>
</gradleEnterprise>
+2 -2
View File
@@ -1,3 +1,3 @@
#Thu Dec 14 08:40:44 CET 2023 #Thu Aug 08 10:23:16 CEST 2024
wrapperUrl=https\://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar wrapperUrl=https\://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.8/apache-maven-3.9.8-bin.zip
Vendored
+23 -23
View File
@@ -9,7 +9,7 @@ pipeline {
triggers { triggers {
pollSCM 'H/10 * * * *' pollSCM 'H/10 * * * *'
upstream(upstreamProjects: "spring-data-commons/main", threshold: hudson.model.Result.SUCCESS) upstream(upstreamProjects: "spring-data-commons/3.3.x", threshold: hudson.model.Result.SUCCESS)
} }
options { options {
@@ -33,15 +33,16 @@ pipeline {
environment { environment {
ARTIFACTORY = credentials("${p['artifactory.credentials']}") ARTIFACTORY = credentials("${p['artifactory.credentials']}")
DEVELOCITY_CACHE = credentials("${p['develocity.cache.credentials']}")
DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}") DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}")
} }
steps { steps {
script { script {
docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) { docker.withRegistry(p['docker.proxy.registry'], p['docker.proxy.credentials']) {
sh "PROFILE=none JENKINS_USER_NAME=${p['jenkins.user.name']} ci/verify.sh" docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) {
sh "JENKINS_USER_NAME=${p['jenkins.user.name']} ci/clean.sh" sh "PROFILE=none JENKINS_USER_NAME=${p['jenkins.user.name']} ci/verify.sh"
sh "JENKINS_USER_NAME=${p['jenkins.user.name']} ci/clean.sh"
}
} }
} }
} }
@@ -63,14 +64,15 @@ pipeline {
options { timeout(time: 30, unit: 'MINUTES') } options { timeout(time: 30, unit: 'MINUTES') }
environment { environment {
ARTIFACTORY = credentials("${p['artifactory.credentials']}") ARTIFACTORY = credentials("${p['artifactory.credentials']}")
DEVELOCITY_CACHE = credentials("${p['develocity.cache.credentials']}")
DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}") DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}")
} }
steps { steps {
script { script {
docker.image(p['docker.java.next.image']).inside(p['docker.java.inside.docker']) { docker.withRegistry(p['docker.proxy.registry'], p['docker.proxy.credentials']) {
sh "PROFILE=none JENKINS_USER_NAME=${p['jenkins.user.name']} ci/verify.sh" docker.image(p['docker.java.next.image']).inside(p['docker.java.inside.docker']) {
sh "JENKINS_USER_NAME=${p['jenkins.user.name']} ci/clean.sh" sh "PROFILE=none JENKINS_USER_NAME=${p['jenkins.user.name']} ci/verify.sh"
sh "JENKINS_USER_NAME=${p['jenkins.user.name']} ci/clean.sh"
}
} }
} }
} }
@@ -92,24 +94,22 @@ pipeline {
options { timeout(time: 20, unit: 'MINUTES') } options { timeout(time: 20, unit: 'MINUTES') }
environment { environment {
ARTIFACTORY = credentials("${p['artifactory.credentials']}") ARTIFACTORY = credentials("${p['artifactory.credentials']}")
DEVELOCITY_CACHE = credentials("${p['develocity.cache.credentials']}")
DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}") DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}")
} }
steps { steps {
script { script {
docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.basic']) { docker.withRegistry(p['docker.proxy.registry'], p['docker.proxy.credentials']) {
sh 'MAVEN_OPTS="-Duser.name=' + "${p['jenkins.user.name']}" + ' -Duser.home=/tmp/jenkins-home" ' + docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.basic']) {
"DEVELOCITY_CACHE_USERNAME=${DEVELOCITY_CACHE_USR} " + sh 'MAVEN_OPTS="-Duser.name=' + "${p['jenkins.user.name']}" + ' -Duser.home=/tmp/jenkins-home" ' +
"DEVELOCITY_CACHE_PASSWORD=${DEVELOCITY_CACHE_PSW} " + "./mvnw -s settings.xml -Pci,artifactory -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-elasticsearch-non-root " +
"GRADLE_ENTERPRISE_ACCESS_KEY=${DEVELOCITY_ACCESS_KEY} " + "-Dartifactory.server=${p['artifactory.url']} " +
"./mvnw -s settings.xml -Pci,artifactory -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-elasticsearch-non-root " + "-Dartifactory.username=${ARTIFACTORY_USR} " +
"-Dartifactory.server=${p['artifactory.url']} " + "-Dartifactory.password=${ARTIFACTORY_PSW} " +
"-Dartifactory.username=${ARTIFACTORY_USR} " + "-Dartifactory.staging-repository=${p['artifactory.repository.snapshot']} " +
"-Dartifactory.password=${ARTIFACTORY_PSW} " + "-Dartifactory.build-name=spring-data-elasticsearch " +
"-Dartifactory.staging-repository=${p['artifactory.repository.snapshot']} " + "-Dartifactory.build-number=spring-data-elasticsearch-${BRANCH_NAME}-build-${BUILD_NUMBER} " +
"-Dartifactory.build-name=spring-data-elasticsearch " + "-Dmaven.test.skip=true clean deploy -U -B"
"-Dartifactory.build-number=spring-data-elasticsearch-${BRANCH_NAME}-build-${BUILD_NUMBER} " + }
"-Dmaven.test.skip=true clean deploy -U -B"
} }
} }
} }
-5
View File
@@ -2,12 +2,7 @@
set -euo pipefail set -euo pipefail
export DEVELOCITY_CACHE_USERNAME=${DEVELOCITY_CACHE_USR}
export DEVELOCITY_CACHE_PASSWORD=${DEVELOCITY_CACHE_PSW}
export JENKINS_USER=${JENKINS_USER_NAME} export JENKINS_USER=${JENKINS_USER_NAME}
# The environment variable to configure access key is still GRADLE_ENTERPRISE_ACCESS_KEY
export GRADLE_ENTERPRISE_ACCESS_KEY=${DEVELOCITY_ACCESS_KEY}
MAVEN_OPTS="-Duser.name=${JENKINS_USER} -Duser.home=/tmp/jenkins-home" \ MAVEN_OPTS="-Duser.name=${JENKINS_USER} -Duser.home=/tmp/jenkins-home" \
./mvnw -s settings.xml clean -Dscan=false -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-elasticsearch ./mvnw -s settings.xml clean -Dscan=false -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-elasticsearch
+7 -5
View File
@@ -1,10 +1,10 @@
# Java versions # Java versions
java.main.tag=17.0.9_9-jdk-focal java.main.tag=17.0.12_7-jdk-focal
java.next.tag=21.0.1_12-jdk-jammy java.next.tag=22.0.2_9-jdk-jammy
# Docker container images - standard # Docker container images - standard
docker.java.main.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.main.tag} docker.java.main.image=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.next.image=library/eclipse-temurin:${java.next.tag}
# Supported versions of MongoDB # Supported versions of MongoDB
docker.mongodb.4.4.version=4.4.25 docker.mongodb.4.4.version=4.4.25
@@ -14,6 +14,7 @@ docker.mongodb.7.0.version=7.0.2
# Supported versions of Redis # Supported versions of Redis
docker.redis.6.version=6.2.13 docker.redis.6.version=6.2.13
docker.redis.7.version=7.2.4
# Supported versions of Cassandra # Supported versions of Cassandra
docker.cassandra.3.version=3.11.16 docker.cassandra.3.version=3.11.16
@@ -25,9 +26,10 @@ docker.java.inside.docker=-u root -v /var/run/docker.sock:/var/run/docker.sock -
# Credentials # Credentials
docker.registry= docker.registry=
docker.credentials=hub.docker.com-springbuildmaster docker.credentials=hub.docker.com-springbuildmaster
docker.proxy.registry=https://docker-hub.usw1.packages.broadcom.com
docker.proxy.credentials=usw1_packages_broadcom_com-jenkins-token
artifactory.credentials=02bd1690-b54f-4c9f-819d-a77cb7a9822c artifactory.credentials=02bd1690-b54f-4c9f-819d-a77cb7a9822c
artifactory.url=https://repo.spring.io artifactory.url=https://repo.spring.io
artifactory.repository.snapshot=libs-snapshot-local artifactory.repository.snapshot=libs-snapshot-local
develocity.cache.credentials=gradle_enterprise_cache_user
develocity.access-key=gradle_enterprise_secret_access_key develocity.access-key=gradle_enterprise_secret_access_key
jenkins.user.name=spring-builds+jenkins jenkins.user.name=spring-builds+jenkins
-6
View File
@@ -4,14 +4,8 @@ set -euo pipefail
mkdir -p /tmp/jenkins-home/.m2/spring-data-elasticsearch mkdir -p /tmp/jenkins-home/.m2/spring-data-elasticsearch
chown -R 1001:1001 . chown -R 1001:1001 .
export DEVELOCITY_CACHE_USERNAME=${DEVELOCITY_CACHE_USR}
export DEVELOCITY_CACHE_PASSWORD=${DEVELOCITY_CACHE_PSW}
export JENKINS_USER=${JENKINS_USER_NAME} export JENKINS_USER=${JENKINS_USER_NAME}
# The environment variable to configure access key is still GRADLE_ENTERPRISE_ACCESS_KEY
export GRADLE_ENTERPRISE_ACCESS_KEY=${DEVELOCITY_ACCESS_KEY}
MAVEN_OPTS="-Duser.name=${JENKINS_USER} -Duser.home=/tmp/jenkins-home" \ MAVEN_OPTS="-Duser.name=${JENKINS_USER} -Duser.home=/tmp/jenkins-home" \
./mvnw -s settings.xml \ ./mvnw -s settings.xml \
-P${PROFILE} clean dependency:list verify -Dsort -U -B -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-elasticsearch -P${PROFILE} clean dependency:list verify -Dsort -U -B -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-elasticsearch
+10
View File
@@ -0,0 +1,10 @@
{
"dependencies": {
"antora": "3.2.0-alpha.6",
"@antora/atlas-extension": "1.0.0-alpha.2",
"@antora/collector-extension": "1.0.0-alpha.7",
"@asciidoctor/tabs": "1.0.0-beta.6",
"@springio/antora-extensions": "1.13.0",
"@springio/asciidoctor-extensions": "1.0.0-alpha.11"
}
}
+5 -32
View File
@@ -5,12 +5,12 @@
<groupId>org.springframework.data</groupId> <groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId> <artifactId>spring-data-elasticsearch</artifactId>
<version>5.3.0</version> <version>5.3.4</version>
<parent> <parent>
<groupId>org.springframework.data.build</groupId> <groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId> <artifactId>spring-data-parent</artifactId>
<version>3.3.0</version> <version>3.3.4</version>
</parent> </parent>
<name>Spring Data Elasticsearch</name> <name>Spring Data Elasticsearch</name>
@@ -18,12 +18,11 @@
<url>https://github.com/spring-projects/spring-data-elasticsearch</url> <url>https://github.com/spring-projects/spring-data-elasticsearch</url>
<properties> <properties>
<springdata.commons>3.3.0</springdata.commons> <springdata.commons>3.3.4</springdata.commons>
<!-- version of the ElasticsearchClient --> <!-- version of the ElasticsearchClient -->
<elasticsearch-java>8.13.2</elasticsearch-java> <elasticsearch-java>8.13.4</elasticsearch-java>
<blockhound-junit>1.0.8.RELEASE</blockhound-junit>
<hoverfly>0.14.4</hoverfly> <hoverfly>0.14.4</hoverfly>
<log4j>2.18.0</log4j> <log4j>2.18.0</log4j>
<jsonassert>1.5.1</jsonassert> <jsonassert>1.5.1</jsonassert>
@@ -248,13 +247,6 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>io.projectreactor.tools</groupId>
<artifactId>blockhound-junit-platform</artifactId>
<version>${blockhound-junit}</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.skyscreamer</groupId> <groupId>org.skyscreamer</groupId>
<artifactId>jsonassert</artifactId> <artifactId>jsonassert</artifactId>
@@ -443,25 +435,6 @@
</build> </build>
</profile> </profile>
<profile>
<id>jdk13+</id>
<!-- on jDK13+, Blockhound needs this JVM flag set -->
<activation>
<jdk>[13,)</jdk>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-XX:+AllowRedefinitionToAddDeleteMethods</argLine>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile> <profile>
<id>antora-process-resources</id> <id>antora-process-resources</id>
<build> <build>
@@ -479,7 +452,7 @@
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
<groupId>io.spring.maven.antora</groupId> <groupId>org.antora</groupId>
<artifactId>antora-maven-plugin</artifactId> <artifactId>antora-maven-plugin</artifactId>
</plugin> </plugin>
</plugins> </plugins>
+3 -5
View File
@@ -3,8 +3,7 @@
# The purpose of this Antora playbook is to build the docs in the current branch. # The purpose of this Antora playbook is to build the docs in the current branch.
antora: antora:
extensions: extensions:
- '@antora/collector-extension' - require: '@springio/antora-extensions'
- require: '@springio/antora-extensions/root-component-extension'
root_component_name: 'data-elasticsearch' root_component_name: 'data-elasticsearch'
site: site:
title: Spring Data Elasticsearch title: Spring Data Elasticsearch
@@ -22,13 +21,12 @@ content:
start_path: src/main/antora start_path: src/main/antora
asciidoc: asciidoc:
attributes: attributes:
page-pagination: ''
hide-uri-scheme: '@' hide-uri-scheme: '@'
tabs-sync-option: '@' tabs-sync-option: '@'
chomp: 'all'
extensions: extensions:
- '@asciidoctor/tabs' - '@asciidoctor/tabs'
- '@springio/asciidoctor-extensions' - '@springio/asciidoctor-extensions'
- '@springio/asciidoctor-extensions/javadoc-extension'
sourcemap: true sourcemap: true
urls: urls:
latest_version_segment: '' latest_version_segment: ''
@@ -38,5 +36,5 @@ runtime:
format: pretty format: pretty
ui: ui:
bundle: bundle:
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.3.5/ui-bundle.zip url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.16/ui-bundle.zip
snapshot: true snapshot: true
+5
View File
@@ -10,3 +10,8 @@ ext:
local: true local: true
scan: scan:
dir: target/classes/ dir: target/classes/
- run:
command: ./mvnw package -Pdistribute
local: true
scan:
dir: target/antora
+3 -2
View File
@@ -9,7 +9,7 @@
*** xref:migration-guides/migration-guide-4.4-5.0.adoc[] *** xref:migration-guides/migration-guide-4.4-5.0.adoc[]
*** xref:migration-guides/migration-guide-5.0-5.1.adoc[] *** xref:migration-guides/migration-guide-5.0-5.1.adoc[]
*** xref:migration-guides/migration-guide-5.1-5.2.adoc[] *** xref:migration-guides/migration-guide-5.1-5.2.adoc[]
*** xref:migration-guides/migration-guide-5.2-5.3.adoc[]
* xref:elasticsearch.adoc[] * xref:elasticsearch.adoc[]
** xref:elasticsearch/clients.adoc[] ** xref:elasticsearch/clients.adoc[]
@@ -39,4 +39,5 @@
** xref:repositories/query-keywords-reference.adoc[] ** xref:repositories/query-keywords-reference.adoc[]
** xref:repositories/query-return-types-reference.adoc[] ** xref:repositories/query-return-types-reference.adoc[]
* https://github.com/spring-projects/spring-data-commons/wiki[Wiki] * xref:attachment$api/java/index.html[Javadoc,role=link-external,window=_blank]
* https://github.com/spring-projects/spring-data-commons/wiki[Wiki,role=link-external,window=_blank]
@@ -31,7 +31,7 @@ public class MyClientConfig extends ElasticsearchConfiguration {
<.> for a detailed description of the builder methods see xref:elasticsearch/clients.adoc#elasticsearch.clients.configuration[Client Configuration] <.> for a detailed description of the builder methods see xref:elasticsearch/clients.adoc#elasticsearch.clients.configuration[Client Configuration]
==== ====
The `ElasticsearchConfiguration` class allows further configuration by overriding for example the `jsonpMapper()` or `transportOptions()` methods. The javadoc:org.springframework.data.elasticsearch.client.elc.ElasticsearchConfiguration[]] class allows further configuration by overriding for example the `jsonpMapper()` or `transportOptions()` methods.
The following beans can then be injected in other Spring components: The following beans can then be injected in other Spring components:
@@ -52,13 +52,13 @@ RestClient restClient; <.>
JsonpMapper jsonpMapper; <.> JsonpMapper jsonpMapper; <.>
---- ----
<.> an implementation of `ElasticsearchOperations` <.> an implementation of javadoc:org.springframework.data.elasticsearch.core.ElasticsearchOperations[]
<.> the `co.elastic.clients.elasticsearch.ElasticsearchClient` that is used. <.> the `co.elastic.clients.elasticsearch.ElasticsearchClient` that is used.
<.> the low level `RestClient` from the Elasticsearch libraries <.> the low level `RestClient` from the Elasticsearch libraries
<.> the `JsonpMapper` user by the Elasticsearch `Transport` <.> the `JsonpMapper` user by the Elasticsearch `Transport`
==== ====
Basically one should just use the `ElasticsearchOperations` to interact with the Elasticsearch cluster. Basically one should just use the javadoc:org.springframework.data.elasticsearch.core.ElasticsearchOperations[] to interact with the Elasticsearch cluster.
When using repositories, this instance is used under the hood as well. When using repositories, this instance is used under the hood as well.
[[elasticsearch.clients.reactiverestclient]] [[elasticsearch.clients.reactiverestclient]]
@@ -86,7 +86,7 @@ public class MyClientConfig extends ReactiveElasticsearchConfiguration {
<.> for a detailed description of the builder methods see xref:elasticsearch/clients.adoc#elasticsearch.clients.configuration[Client Configuration] <.> for a detailed description of the builder methods see xref:elasticsearch/clients.adoc#elasticsearch.clients.configuration[Client Configuration]
==== ====
The `ReactiveElasticsearchConfiguration` class allows further configuration by overriding for example the `jsonpMapper()` or `transportOptions()` methods. The javadoc:org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchConfiguration[] class allows further configuration by overriding for example the `jsonpMapper()` or `transportOptions()` methods.
The following beans can then be injected in other Spring components: The following beans can then be injected in other Spring components:
@@ -108,20 +108,20 @@ JsonpMapper jsonpMapper; <.>
the following can be injected: the following can be injected:
<.> an implementation of `ReactiveElasticsearchOperations` <.> an implementation of javadoc:org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations[]
<.> the `org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient` that is used. <.> the `org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient` that is used.
This is a reactive implementation based on the Elasticsearch client implementation. This is a reactive implementation based on the Elasticsearch client implementation.
<.> the low level `RestClient` from the Elasticsearch libraries <.> the low level `RestClient` from the Elasticsearch libraries
<.> the `JsonpMapper` user by the Elasticsearch `Transport` <.> the `JsonpMapper` user by the Elasticsearch `Transport`
==== ====
Basically one should just use the `ReactiveElasticsearchOperations` to interact with the Elasticsearch cluster. Basically one should just use the javadoc:org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations[] to interact with the Elasticsearch cluster.
When using repositories, this instance is used under the hood as well. When using repositories, this instance is used under the hood as well.
[[elasticsearch.clients.configuration]] [[elasticsearch.clients.configuration]]
== Client Configuration == Client Configuration
Client behaviour can be changed via the `ClientConfiguration` that allows to set options for SSL, connect and socket timeouts, headers and other parameters. Client behaviour can be changed via the javadoc:org.springframework.data.elasticsearch.client.ClientConfiguration[] that allows to set options for SSL, connect and socket timeouts, headers and other parameters.
.Client Configuration .Client Configuration
==== ====
@@ -178,7 +178,7 @@ If this is used in the reactive setup, the supplier function *must not* block!
[[elasticsearch.clients.configuration.callbacks]] [[elasticsearch.clients.configuration.callbacks]]
=== Client configuration callbacks === Client configuration callbacks
The `ClientConfiguration` class offers the most common parameters to configure the client. The javadoc:org.springframework.data.elasticsearch.client.ClientConfiguration[] class offers the most common parameters to configure the client.
In the case this is not enough, the user can add callback functions by using the `withClientConfigurer(ClientConfigurationCallback<?>)` method. In the case this is not enough, the user can add callback functions by using the `withClientConfigurer(ClientConfigurationCallback<?>)` method.
The following callbacks are provided: The following callbacks are provided:
@@ -1,6 +1,11 @@
[[new-features]] [[new-features]]
= What's new = What's new
[[new-features.5-3-1]]
== New in Spring Data Elasticsearch 5.3.1
* Upgrade to Elasticsearch 8.13.4.
[[new-features.5-3-0]] [[new-features.5-3-0]]
== New in Spring Data Elasticsearch 5.3 == New in Spring Data Elasticsearch 5.3
@@ -3,10 +3,10 @@
Spring Data Elasticsearch uses several interfaces to define the operations that can be called against an Elasticsearch index (for a description of the reactive interfaces see xref:elasticsearch/reactive-template.adoc[]). Spring Data Elasticsearch uses several interfaces to define the operations that can be called against an Elasticsearch index (for a description of the reactive interfaces see xref:elasticsearch/reactive-template.adoc[]).
* `IndexOperations` defines actions on index level like creating or deleting an index. * javadoc:org.springframework.data.elasticsearch.core.IndexOperations[] defines actions on index level like creating or deleting an index.
* `DocumentOperations` defines actions to store, update and retrieve entities based on their id. * javadoc:org.springframework.data.elasticsearch.core.DocumentOperations[] defines actions to store, update and retrieve entities based on their id.
* `SearchOperations` define the actions to search for multiple entities using queries * javadoc:org.springframework.data.elasticsearch.core.SearchOperations[] define the actions to search for multiple entities using queries
* `ElasticsearchOperations` combines the `DocumentOperations` and `SearchOperations` interfaces. * javadoc:org.springframework.data.elasticsearch.core.ElasticsearchOperations[] combines the `DocumentOperations` and `SearchOperations` interfaces.
These interfaces correspond to the structuring of the https://www.elastic.co/guide/en/elasticsearch/reference/current/rest-apis.html[Elasticsearch API]. These interfaces correspond to the structuring of the https://www.elastic.co/guide/en/elasticsearch/reference/current/rest-apis.html[Elasticsearch API].
@@ -6,10 +6,10 @@ The following table shows the Elasticsearch and Spring versions that are used by
[cols="^,^,^,^",options="header"] [cols="^,^,^,^",options="header"]
|=== |===
| Spring Data Release Train | Spring Data Elasticsearch | Elasticsearch | Spring Framework | Spring Data Release Train | Spring Data Elasticsearch | Elasticsearch | Spring Framework
| 2024.0 (?) | 5.3.x | 8.13.2 | ? | 2024.0 | 5.3.3 | 8.13.4 | 6.1.x
| 2023.1 (Vaughan) | 5.2.x | 8.11.1 | 6.1.x | 2023.1 (Vaughan) | 5.2.x | 8.11.1 | 6.1.x
| 2023.0 (Ullmann) | 5.1.x | 8.7.1 | 6.0.x | 2023.0 (Ullmann) | 5.1.xfootnote:oom[Out of maintenance] | 8.7.1 | 6.0.x
| 2022.0 (Turing) | 5.0.xfootnote:oom[Out of maintenance] | 8.5.3 | 6.0.x | 2022.0 (Turing) | 5.0.xfootnote:oom[] | 8.5.3 | 6.0.x
| 2021.2 (Raj) | 4.4.xfootnote:oom[] | 7.17.3 | 5.3.x | 2021.2 (Raj) | 4.4.xfootnote:oom[] | 7.17.3 | 5.3.x
| 2021.1 (Q) | 4.3.xfootnote:oom[] | 7.15.2 | 5.3.x | 2021.1 (Q) | 4.3.xfootnote:oom[] | 7.15.2 | 5.3.x
| 2021.0 (Pascal) | 4.2.xfootnote:oom[] | 7.12.0 | 5.3.x | 2021.0 (Pascal) | 4.2.xfootnote:oom[] | 7.12.0 | 5.3.x
@@ -5,7 +5,10 @@ This section describes breaking changes from version 5.2.x to 5.3.x and how remo
[[elasticsearch-migration-guide-5.2-5.3.breaking-changes]] [[elasticsearch-migration-guide-5.2-5.3.breaking-changes]]
== Breaking Changes == Breaking Changes
During the parameter replacement in `@Query` annotated repository methods previous versions wrote the String _"null"_ into the query that was sent to Elasticsearch
when the actual parameter value was `null`. As Elasticsearch does not store `null` values, this behaviour could lead to problems, for example whent the fields to be
searched contains the string `"null"`. In Version 5.3 a `null` value in a parameter will cause a `ConversionException` to be thrown. If you are using `"null"` as the
`null_value` defined in a field mapping, then pass that string into the query instead of a Java `null`.
[[elasticsearch-migration-guide-5.2-5.3.deprecations]] [[elasticsearch-migration-guide-5.2-5.3.deprecations]]
== Deprecations == Deprecations
@@ -16,6 +16,7 @@
package org.springframework.data.elasticsearch.client.elc; package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.elasticsearch._types.KnnQuery; import co.elastic.clients.elasticsearch._types.KnnQuery;
import co.elastic.clients.elasticsearch._types.KnnSearch;
import co.elastic.clients.elasticsearch._types.SortOptions; import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregation; import co.elastic.clients.elasticsearch._types.aggregations.Aggregation;
import co.elastic.clients.elasticsearch._types.query_dsl.Query; import co.elastic.clients.elasticsearch._types.query_dsl.Query;
@@ -54,6 +55,7 @@ public class NativeQuery extends BaseQuery {
private Map<String, JsonData> searchExtensions = Collections.emptyMap(); private Map<String, JsonData> searchExtensions = Collections.emptyMap();
@Nullable private KnnQuery knnQuery; @Nullable private KnnQuery knnQuery;
@Nullable private List<KnnSearch> knnSearches = Collections.emptyList();
public NativeQuery(NativeQueryBuilder builder) { public NativeQuery(NativeQueryBuilder builder) {
super(builder); super(builder);
@@ -71,6 +73,7 @@ public class NativeQuery extends BaseQuery {
} }
this.springDataQuery = builder.getSpringDataQuery(); this.springDataQuery = builder.getSpringDataQuery();
this.knnQuery = builder.getKnnQuery(); this.knnQuery = builder.getKnnQuery();
this.knnSearches = builder.getKnnSearches();
} }
public NativeQuery(@Nullable Query query) { public NativeQuery(@Nullable Query query) {
@@ -129,6 +132,14 @@ public class NativeQuery extends BaseQuery {
return knnQuery; return knnQuery;
} }
/**
* @since 5.3.1
*/
@Nullable
public List<KnnSearch> getKnnSearches() {
return knnSearches;
}
@Nullable @Nullable
public org.springframework.data.elasticsearch.core.query.Query getSpringDataQuery() { public org.springframework.data.elasticsearch.core.query.Query getSpringDataQuery() {
return springDataQuery; return springDataQuery;
@@ -16,6 +16,7 @@
package org.springframework.data.elasticsearch.client.elc; package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.elasticsearch._types.KnnQuery; import co.elastic.clients.elasticsearch._types.KnnQuery;
import co.elastic.clients.elasticsearch._types.KnnSearch;
import co.elastic.clients.elasticsearch._types.SortOptions; import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregation; import co.elastic.clients.elasticsearch._types.aggregations.Aggregation;
import co.elastic.clients.elasticsearch._types.query_dsl.Query; import co.elastic.clients.elasticsearch._types.query_dsl.Query;
@@ -26,6 +27,7 @@ import co.elastic.clients.util.ObjectBuilder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -52,6 +54,7 @@ public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQuer
@Nullable private org.springframework.data.elasticsearch.core.query.Query springDataQuery; @Nullable private org.springframework.data.elasticsearch.core.query.Query springDataQuery;
@Nullable private KnnQuery knnQuery; @Nullable private KnnQuery knnQuery;
@Nullable private List<KnnSearch> knnSearches = Collections.emptyList();
public NativeQueryBuilder() {} public NativeQueryBuilder() {}
@@ -92,6 +95,14 @@ public class NativeQueryBuilder extends BaseQueryBuilder<NativeQuery, NativeQuer
return knnQuery; return knnQuery;
} }
/**
* @since 5.3.1
*/
@Nullable
public List<KnnSearch> getKnnSearches() {
return knnSearches;
}
@Nullable @Nullable
public org.springframework.data.elasticsearch.core.query.Query getSpringDataQuery() { public org.springframework.data.elasticsearch.core.query.Query getSpringDataQuery() {
return springDataQuery; return springDataQuery;
@@ -395,7 +395,28 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
Function<PitSearchAfter, Publisher<? extends ResponseBody<EntityAsMap>>> resourceClosure = psa -> { Function<PitSearchAfter, Publisher<? extends ResponseBody<EntityAsMap>>> resourceClosure = psa -> {
baseQuery.setPointInTime(new Query.PointInTime(psa.getPit(), pitKeepAlive)); baseQuery.setPointInTime(new Query.PointInTime(psa.getPit(), pitKeepAlive));
baseQuery.addSort(Sort.by("_shard_doc"));
// only add _shard_doc if there is not a field_collapse and a sort with the same name
boolean addShardDoc = true;
if (query instanceof NativeQuery nativeQuery && nativeQuery.getFieldCollapse() != null) {
var field = nativeQuery.getFieldCollapse().field();
if (nativeQuery.getSortOptions().stream()
.anyMatch(sortOptions -> sortOptions.isField() && sortOptions.field().field().equals(field))) {
addShardDoc = false;
}
if (query.getSort() != null
&& query.getSort().stream().anyMatch(order -> order.getProperty().equals(field))) {
addShardDoc = false;
}
}
if (addShardDoc) {
baseQuery.addSort(Sort.by("_shard_doc"));
}
SearchRequest firstSearchRequest = requestConverter.searchRequest(baseQuery, routingResolver.getRouting(), SearchRequest firstSearchRequest = requestConverter.searchRequest(baseQuery, routingResolver.getRouting(),
clazz, index, false, true); clazz, index, false, true);
@@ -1021,6 +1021,9 @@ class RequestConverter extends AbstractQueryProcessor {
.collect(Collectors.toList())); .collect(Collectors.toList()));
} }
} }
if (query.getRefresh() != null) {
dqb.refresh(query.getRefresh());
}
dqb.allowNoIndices(query.getAllowNoIndices()) dqb.allowNoIndices(query.getAllowNoIndices())
.conflicts(conflicts(query.getConflicts())) .conflicts(conflicts(query.getConflicts()))
.ignoreUnavailable(query.getIgnoreUnavailable()) .ignoreUnavailable(query.getIgnoreUnavailable())
@@ -1477,8 +1480,8 @@ class RequestConverter extends AbstractQueryProcessor {
if (query instanceof NativeQuery nativeQuery) { if (query instanceof NativeQuery nativeQuery) {
prepareNativeSearch(nativeQuery, builder); prepareNativeSearch(nativeQuery, builder);
} }
// query.getSort() must be checked after prepareNativeSearch as this already might hav a sort set that must have // query.getSort() must be checked after prepareNativeSearch as this already might have a sort set
// higher priority // that must have higher priority
if (query.getSort() != null) { if (query.getSort() != null) {
List<SortOptions> sortOptions = getSortOptions(query.getSort(), persistentEntity); List<SortOptions> sortOptions = getSortOptions(query.getSort(), persistentEntity);
@@ -1500,7 +1503,15 @@ class RequestConverter extends AbstractQueryProcessor {
} }
if (!isEmpty(query.getSearchAfter())) { if (!isEmpty(query.getSearchAfter())) {
builder.searchAfter(query.getSearchAfter().stream().map(TypeUtils::toFieldValue).toList()); var fieldValues = query.getSearchAfter().stream().map(TypeUtils::toFieldValue).toList();
// when there is a field collapse on a native query, and we have a search_after, then the search_after
// must only have one entry
if (query instanceof NativeQuery nativeQuery && nativeQuery.getFieldCollapse() != null) {
builder.searchAfter(fieldValues.get(0));
} else {
builder.searchAfter(fieldValues);
}
} }
query.getRescorerQueries().forEach(rescorerQuery -> builder.rescore(getRescore(rescorerQuery))); query.getRescorerQueries().forEach(rescorerQuery -> builder.rescore(getRescore(rescorerQuery)));
@@ -1719,7 +1730,18 @@ class RequestConverter extends AbstractQueryProcessor {
; ;
if (query.getKnnQuery() != null) { if (query.getKnnQuery() != null) {
builder.knn(query.getKnnQuery()); var kq = query.getKnnQuery();
builder.knn(ksb -> ksb
.field(kq.field())
.queryVector(kq.queryVector())
.numCandidates(kq.numCandidates())
.filter(kq.filter())
.similarity(kq.similarity()));
}
if (!isEmpty(query.getKnnSearches())) {
builder.knn(query.getKnnSearches());
} }
if (!isEmpty(query.getAggregations())) { if (!isEmpty(query.getAggregations())) {
@@ -1740,7 +1762,18 @@ class RequestConverter extends AbstractQueryProcessor {
.sort(query.getSortOptions()); .sort(query.getSortOptions());
if (query.getKnnQuery() != null) { if (query.getKnnQuery() != null) {
builder.knn(query.getKnnQuery()); var kq = query.getKnnQuery();
builder.knn(ksb -> ksb
.field(kq.field())
.queryVector(kq.queryVector())
.numCandidates(kq.numCandidates())
.filter(kq.filter())
.similarity(kq.similarity()));
}
if (!isEmpty(query.getKnnSearches())) {
builder.knn(query.getKnnSearches());
} }
if (!isEmpty(query.getAggregations())) { if (!isEmpty(query.getAggregations())) {
@@ -345,8 +345,10 @@ public class MappingBuilder {
: nestedPropertyPrefix + '.' + property.getFieldName(); : nestedPropertyPrefix + '.' + property.getFieldName();
Field fieldAnnotation = property.findAnnotation(Field.class); Field fieldAnnotation = property.findAnnotation(Field.class);
MultiField multiFieldAnnotation = property.findAnnotation(MultiField.class);
if (fieldAnnotation != null && fieldAnnotation.excludeFromSource()) { if ((fieldAnnotation != null && fieldAnnotation.excludeFromSource()) ||
multiFieldAnnotation != null && multiFieldAnnotation.mainField().excludeFromSource()) {
excludeFromSource.add(nestedPropertyPath); excludeFromSource.add(nestedPropertyPath);
} }
@@ -377,8 +379,6 @@ public class MappingBuilder {
} }
} }
MultiField multiField = property.findAnnotation(MultiField.class);
if (isCompletionProperty) { if (isCompletionProperty) {
CompletionField completionField = property.findAnnotation(CompletionField.class); CompletionField completionField = property.findAnnotation(CompletionField.class);
applyCompletionFieldMapping(propertiesNode, property, completionField); applyCompletionFieldMapping(propertiesNode, property, completionField);
@@ -386,8 +386,8 @@ public class MappingBuilder {
if (isRootObject && fieldAnnotation != null && property.isIdProperty()) { if (isRootObject && fieldAnnotation != null && property.isIdProperty()) {
applyDefaultIdFieldMapping(propertiesNode, property); applyDefaultIdFieldMapping(propertiesNode, property);
} else if (multiField != null) { } else if (multiFieldAnnotation != null) {
addMultiFieldMapping(propertiesNode, property, multiField, isNestedOrObjectProperty, dynamicMapping); addMultiFieldMapping(propertiesNode, property, multiFieldAnnotation, isNestedOrObjectProperty, dynamicMapping);
} else if (fieldAnnotation != null) { } else if (fieldAnnotation != null) {
addSingleFieldMapping(propertiesNode, property, fieldAnnotation, isNestedOrObjectProperty, dynamicMapping); addSingleFieldMapping(propertiesNode, property, fieldAnnotation, isNestedOrObjectProperty, dynamicMapping);
} }
@@ -171,8 +171,8 @@ public final class MappingParameters {
positiveScoreImpact = field.positiveScoreImpact(); positiveScoreImpact = field.positiveScoreImpact();
dims = field.dims(); dims = field.dims();
if (type == FieldType.Dense_Vector) { if (type == FieldType.Dense_Vector) {
Assert.isTrue(dims >= 1 && dims <= 2048, Assert.isTrue(dims >= 1 && dims <= 4096,
"Invalid required parameter! Dense_Vector value \"dims\" must be between 1 and 2048."); "Invalid required parameter! Dense_Vector value \"dims\" must be between 1 and 4096.");
} }
Assert.isTrue(field.enabled() || type == FieldType.Object, "enabled false is only allowed for field type object"); Assert.isTrue(field.enabled() || type == FieldType.Object, "enabled false is only allowed for field type object");
enabled = field.enabled(); enabled = field.enabled();
@@ -214,8 +214,8 @@ public final class MappingParameters {
positiveScoreImpact = field.positiveScoreImpact(); positiveScoreImpact = field.positiveScoreImpact();
dims = field.dims(); dims = field.dims();
if (type == FieldType.Dense_Vector) { if (type == FieldType.Dense_Vector) {
Assert.isTrue(dims >= 1 && dims <= 2048, Assert.isTrue(dims >= 1 && dims <= 4096,
"Invalid required parameter! Dense_Vector value \"dims\" must be between 1 and 2048."); "Invalid required parameter! Dense_Vector value \"dims\" must be between 1 and 4096.");
} }
enabled = true; enabled = true;
eagerGlobalOrdinals = field.eagerGlobalOrdinals(); eagerGlobalOrdinals = field.eagerGlobalOrdinals();
+5 -1
View File
@@ -1,4 +1,4 @@
Spring Data Elasticsearch 5.3 GA (2024.0.0) Spring Data Elasticsearch 5.3.4 (2024.0.4)
Copyright (c) [2013-2022] Pivotal Software, Inc. Copyright (c) [2013-2022] Pivotal Software, Inc.
This product is licensed to you under the Apache License, Version 2.0 (the "License"). This product is licensed to you under the Apache License, Version 2.0 (the "License").
@@ -22,3 +22,7 @@ conditions of the subcomponent's license, as noted in the LICENSE file.
@@ -1,48 +0,0 @@
/*
* Copyright 2021-2024 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.blockhound;
import reactor.blockhound.BlockHound;
import reactor.blockhound.BlockingOperationError;
import reactor.blockhound.integration.BlockHoundIntegration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author Peter-Josef Meisch
*/
public class BlockHoundIntegrationCustomizer implements BlockHoundIntegration {
private static final Log LOGGER = LogFactory.getLog(BlockHoundIntegrationCustomizer.class);
@Override
public void applyTo(BlockHound.Builder builder) {
// Elasticsearch classes reading from the classpath on initialization, needed for parsing Elasticsearch responses
builder //
.allowBlockingCallsInside("org.elasticsearch.Build", "<clinit>") //
.allowBlockingCallsInside("org.elasticsearch.common.xcontent.XContentBuilder", "<clinit>") // pre 7.16
.allowBlockingCallsInside("org.elasticsearch.common.XContentBuilder", "<clinit>") // from 7.16 on
.allowBlockingCallsInside("org.elasticsearch.xcontent.json.JsonXContent", "contentBuilder") // from 7.16 on
.allowBlockingCallsInside("jakarta.json.spi.JsonProvider", "provider") //
;
builder.blockingMethodCallback(it -> {
LOGGER.error("BlockHound error", new Error(it.toString()));
throw new BlockingOperationError(it);
});
}
}
@@ -1,47 +0,0 @@
/*
* Copyright 2021-2024 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.blockhound;
import static org.assertj.core.api.Assertions.*;
import reactor.blockhound.BlockingOperationError;
import reactor.core.publisher.Mono;
import java.time.Duration;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
/**
* @author Peter-Josef Meisch
*/
public class BlockHoundTests {
@Test // #1822
@DisplayName("should fail if BlockHound is not installed")
void shouldFailIfBlockHoundIsNotInstalled() {
assertThatThrownBy(() -> {
Mono.delay(Duration.ofMillis(1)).doOnNext(it -> {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}).block(); // should throw an exception about Thread.sleep
}).hasCauseInstanceOf(BlockingOperationError.class);
}
}
@@ -30,12 +30,16 @@ import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter; import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates; import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext; 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.DeleteQuery;
import org.springframework.data.elasticsearch.core.query.DocValueField; import org.springframework.data.elasticsearch.core.query.DocValueField;
import org.springframework.data.elasticsearch.core.query.StringQuery; import org.springframework.data.elasticsearch.core.query.StringQuery;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
/** /**
* @author Peter-Josef Meisch * @author Peter-Josef Meisch
* @author Han Seungwoo
*/ */
class RequestConverterTest { class RequestConverterTest {
@@ -72,6 +76,19 @@ class RequestConverterTest {
assertThat(fieldAndFormats.get(1).format()).isEqualTo("format2"); assertThat(fieldAndFormats.get(1).format()).isEqualTo("format2");
} }
@Test // #2973
@DisplayName("should set refresh based on deleteRequest")
void refreshSetByDeleteRequest() {
var query = new CriteriaQuery(new Criteria("text").contains("test"));
var deleteQuery = DeleteQuery.builder(query).withRefresh(true).build();
var deleteByQueryRequest = requestConverter.documentDeleteByQueryRequest(deleteQuery, null, SampleEntity.class,
IndexCoordinates.of("foo"),
null);
assertThat(deleteByQueryRequest.refresh()).isTrue();
}
@Document(indexName = "does-not-matter") @Document(indexName = "does-not-matter")
static class SampleEntity { static class SampleEntity {
@Nullable @Nullable
@@ -1090,38 +1090,49 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
String expected = """ String expected = """
{ {
"properties": { "properties": {
"_class": { "_class": {
"type": "keyword", "type": "keyword",
"index": false, "index": false,
"doc_values": false "doc_values": false
}, },
"excluded-date": { "excluded-date": {
"type": "date", "type": "date",
"format": "date" "format": "date"
}, },
"nestedEntity": { "nestedEntity": {
"type": "nested", "type": "nested",
"properties": { "properties": {
"_class": { "_class": {
"type": "keyword", "type": "keyword",
"index": false, "index": false,
"doc_values": false "doc_values": false
}, },
"excluded-text": { "excluded-text": {
"type": "text" "type": "text"
} }
} }
} },
}, "excluded-multifield": {
"_source": { "type": "text",
"excludes": [ "fields": {
"excluded-date", "keyword": {
"nestedEntity.excluded-text" "type": "keyword"
] }
} }
} }
"""; // },
"_source": {
"excludes": [
"excluded-date",
"nestedEntity.excluded-text",
"excluded-multifield"
]
}
}
"""; //
String mapping = getMappingBuilder().buildPropertyMapping(ExcludedFieldEntity.class); String mapping = getMappingBuilder().buildPropertyMapping(ExcludedFieldEntity.class);
@@ -2395,6 +2406,10 @@ public class MappingBuilderUnitTests extends MappingContextBaseTests {
excludeFromSource = true) private LocalDate excludedDate; excludeFromSource = true) private LocalDate excludedDate;
@Nullable @Nullable
@Field(type = Nested) private NestedExcludedFieldEntity nestedEntity; @Field(type = Nested) private NestedExcludedFieldEntity nestedEntity;
@Nullable
@MultiField(mainField = @Field(name = "excluded-multifield", type = Text, excludeFromSource = true), otherFields = {
@InnerField(suffix = "keyword", type = Keyword)
}) private String excludedMultifield;
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
@@ -70,8 +70,8 @@ public class MappingParametersTest extends MappingContextBaseTests {
} }
@Test // #1700 @Test // #1700
@DisplayName("should not allow dims length greater than 2048 for dense_vector type") @DisplayName("should not allow dims length greater than 4096 for dense_vector type")
void shouldNotAllowDimsLengthGreaterThan2048ForDenseVectorType() { void shouldNotAllowDimsLengthGreaterThan4096ForDenseVectorType() {
ElasticsearchPersistentEntity<?> failEntity = elasticsearchConverter.get().getMappingContext() ElasticsearchPersistentEntity<?> failEntity = elasticsearchConverter.get().getMappingContext()
.getRequiredPersistentEntity(DenseVectorInvalidDimsClass.class); .getRequiredPersistentEntity(DenseVectorInvalidDimsClass.class);
Annotation annotation = failEntity.getRequiredPersistentProperty("dense_vector").findAnnotation(Field.class); Annotation annotation = failEntity.getRequiredPersistentProperty("dense_vector").findAnnotation(Field.class);
@@ -90,21 +90,28 @@ public class MappingParametersTest extends MappingContextBaseTests {
} }
static class AnnotatedClass { static class AnnotatedClass {
@Nullable @Field private String field; @Nullable
@Nullable @MultiField(mainField = @Field, @Field private String field;
@Nullable
@MultiField(mainField = @Field,
otherFields = { @InnerField(suffix = "test", type = FieldType.Text) }) private String mainField; otherFields = { @InnerField(suffix = "test", type = FieldType.Text) }) private String mainField;
@Nullable @Field(type = FieldType.Text, docValues = false) private String docValuesText; @Nullable
@Nullable @Field(type = FieldType.Nested, docValues = false) private String docValuesNested; @Field(type = FieldType.Text, docValues = false) private String docValuesText;
@Nullable @Field(type = Object, enabled = true) private String enabledObject; @Nullable
@Nullable @Field(type = Object, enabled = false) private String disabledObject; @Field(type = FieldType.Nested, docValues = false) private String docValuesNested;
@Nullable
@Field(type = Object, enabled = true) private String enabledObject;
@Nullable
@Field(type = Object, enabled = false) private String disabledObject;
} }
static class InvalidEnabledFieldClass { static class InvalidEnabledFieldClass {
@Nullable @Field(type = FieldType.Text, enabled = false) private String disabledObject; @Nullable
@Field(type = FieldType.Text, enabled = false) private String disabledObject;
} }
static class DenseVectorInvalidDimsClass { static class DenseVectorInvalidDimsClass {
@Field(type = Dense_Vector, dims = 2049) private float[] dense_vector; @Field(type = Dense_Vector, dims = 4097) private float[] dense_vector;
} }
static class DenseVectorMissingDimsClass { static class DenseVectorMissingDimsClass {
@@ -132,7 +132,7 @@ public class ClusterConnection implements ExtensionContext.Store.CloseableResour
DockerImageName dockerImageName = getDockerImageName(testcontainersProperties); DockerImageName dockerImageName = getDockerImageName(testcontainersProperties);
ElasticsearchContainer elasticsearchContainer = new SpringDataElasticsearchContainer(dockerImageName) ElasticsearchContainer elasticsearchContainer = new SpringDataElasticsearchContainer(dockerImageName)
.withEnv(testcontainersProperties).withStartupTimeout(Duration.ofMinutes(2)); .withEnv(testcontainersProperties).withStartupTimeout(Duration.ofMinutes(2)).withReuse(true);
elasticsearchContainer.start(); elasticsearchContainer.start();
return ClusterConnectionInfo.builder() // return ClusterConnectionInfo.builder() //
@@ -192,16 +192,7 @@ public class ClusterConnection implements ExtensionContext.Store.CloseableResour
@Override @Override
public void close() { public void close() {
if (clusterConnectionInfo != null && clusterConnectionInfo.getElasticsearchContainer() != null) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Stopping container");
}
clusterConnectionInfo.getElasticsearchContainer().stop();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("closed");
}
} }
private static class SpringDataElasticsearchContainer extends ElasticsearchContainer { private static class SpringDataElasticsearchContainer extends ElasticsearchContainer {
@@ -15,14 +15,22 @@
*/ */
package org.springframework.data.elasticsearch.repository.support; package org.springframework.data.elasticsearch.repository.support;
import co.elastic.clients.elasticsearch.core.search.FieldCollapse;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
import org.springframework.data.elasticsearch.client.elc.Queries;
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchTemplateConfiguration; import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchTemplateConfiguration;
import org.springframework.data.elasticsearch.repositories.custommethod.QueryParameter; import org.springframework.data.elasticsearch.repositories.custommethod.QueryParameter;
import org.springframework.data.elasticsearch.repository.config.EnableReactiveElasticsearchRepositories; import org.springframework.data.elasticsearch.repository.config.EnableReactiveElasticsearchRepositories;
import org.springframework.data.elasticsearch.utils.IndexNameProvider; import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import reactor.test.StepVerifier;
/** /**
* @author Peter-Josef Meisch * @author Peter-Josef Meisch
@@ -51,4 +59,33 @@ public class SimpleReactiveElasticsearchRepositoryELCIntegrationTests
} }
} }
/**
* search_after is used by the reactive search operation, it normally always adds _shard_doc as a tiebreaker sort
* parameter. This must not be done when a collapse field is used as sort field, as in that case the collapse field
* must be the only sort field.
*/
@Test // #2935
@DisplayName("should use collapse_field for search_after in pit search")
void shouldUseCollapseFieldForSearchAfterI() {
var entity = new SampleEntity();
entity.setId("42");
entity.setMessage("m");
entity.setKeyword("kw");
repository.save(entity).block();
var query = NativeQuery.builder()
.withQuery(Queries.matchAllQueryAsQuery())
.withPageable(Pageable.unpaged())
.withFieldCollapse(FieldCollapse.of(fcb -> fcb
.field("keyword")))
.withSort(Sort.by("keyword"))
.build();
operations.search(query, SampleEntity.class)
.as(StepVerifier::create)
.expectNextCount(1)
.verifyComplete();
}
} }
@@ -1 +0,0 @@
org.springframework.data.elasticsearch.blockhound.BlockHoundIntegrationCustomizer
@@ -15,7 +15,7 @@
# #
# #
sde.testcontainers.image-name=docker.elastic.co/elasticsearch/elasticsearch sde.testcontainers.image-name=docker.elastic.co/elasticsearch/elasticsearch
sde.testcontainers.image-version=8.13.2 sde.testcontainers.image-version=8.13.4
# #
# #
# needed as we do a DELETE /* at the end of the tests, will be required from 8.0 on, produces a warning since 7.13 # needed as we do a DELETE /* at the end of the tests, will be required from 8.0 on, produces a warning since 7.13