1
0
mirror of synced 2026-09-03 14:39:38 +00:00

Compare commits

..

25 Commits

Author SHA1 Message Date
Mark Paluch cf8d803877 Release version 6.1 M1 (2026.0.0).
See #3198
2026-02-13 10:21:30 +01:00
Mark Paluch a994520a38 Prepare 6.1 M1 (2026.0.0).
See #3198
2026-02-13 10:21:04 +01:00
Peter-Josef Meisch 2952389f26 Polishing
Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com>
2026-02-10 20:14:53 +01:00
Peter-Josef Meisch 548841cc22 Enable IndexCoordinates a parameter in repository search queries.
Closes #2506

Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com>
Co-authored-by: Urs Keller <urs.keller@lightspeedhq.com>
2026-02-09 21:30:39 +01:00
Peter-Josef Meisch 2278d075a7 Use jackson3 version from the parent bom.
Closes #3239

Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com>
2026-02-09 21:18:58 +01:00
Peter-Josef Meisch 9a66c77b06 Upgrade to Elasticsearch 9.2.5.
Closes #3235

Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com>
2026-02-08 18:17:16 +01:00
Peter-Josef Meisch ead1926d13 Fix setting script id in UpdateQuery request.
Closes #3231

Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com>
2026-02-01 13:37:42 +01:00
Mark Paluch c5043d6544 Update CI Properties.
See #3198
2026-01-28 10:40:10 +01:00
Peter-Josef Meisch bccaa5c0ab Upgrade to Elasticsearch 9.2.4.
Closes #3227

Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com>
2026-01-23 20:50:35 +01:00
Mark Paluch 0a65d36040 Add Readme templates.
See spring-projects/spring-data-build#2758
2026-01-12 15:27:32 +01:00
Peter-Josef Meisch f5e9558f3c Remove deprecated code.
Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com>
2026-01-06 14:29:48 +01:00
Mark Paluch c118bb3b84 Extend license header copyright years to present.
See #3220
2026-01-05 08:45:17 +01:00
dependabot[bot] a7871385bf Bump org.apache.logging.log4j:log4j-core from 2.25.1 to 2.25.3 (#3215)
Bumps org.apache.logging.log4j:log4j-core from 2.25.1 to 2.25.3.

---
updated-dependencies:
- dependency-name: org.apache.logging.log4j:log4j-core
  dependency-version: 2.25.3
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-24 16:58:24 +01:00
Peter-Josef Meisch cf9de2fc7d Upgrade to Elasticsearch 9.2.3.
Closes #3216

Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com>
2025-12-24 15:55:20 +01:00
Mark Paluch e0199e3ed4 Polishing.
Simplify test dependency setup, remove no longer required servlet/xbean dependencies, exclude commons-lang3 in favor of the Testcontainers variant. Remove outdated commons-codec dependency in favor of the Testcontainers variant.

See #3212
2025-12-11 09:02:33 +01:00
Mark Paluch 1e5341ad07 Add @ContextConfiguration(…) to test classes that use @SpringIntegrationTest on super classes.
Closes #3212
2025-12-11 08:59:58 +01:00
Mark Paluch 6c7c323246 Update CI Properties.
See #3198
2025-12-10 08:35:42 +01:00
Mark Paluch d623f228a0 Remove project.yml. 2025-12-10 08:34:04 +01:00
Peter-Josef Meisch 8c0e62c9d8 Upgrade to Elasticsearch 9.2.2.
Closes #3207

Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com>
2025-12-04 23:41:51 +01:00
Peter-Josef Meisch 69746441e1 Fix UpdateQuery.Builder to allow only a scriptname to be set.
Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com>
2025-12-04 23:24:35 +01:00
Peter-Josef Meisch e31b66768b Adjust aot hints for Elasticsearch 9 client.
The hints for the old httpclient are only needed when the old library is on the classpath, in case a user still uses the old RestClient. For the new Elasticsearch client there are no aot hints required.

Closes: #3203

Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com>
2025-11-25 20:09:58 +01:00
Peter-Josef Meisch e7e0ac89e2 Migrate to Jackson3.
Jackson2 ist still needed as runtime optional dependency for users that still use the old RestClient.

Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com>
2025-11-14 20:14:40 +01:00
Peter-Josef Meisch 5821a81db9 Fix documentation.
Closes #3199

Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com>
2025-11-14 19:01:33 +01:00
Mark Paluch eabfd95497 After release cleanups.
See #3186
2025-11-14 13:56:11 +01:00
Mark Paluch b712cb8829 Prepare next development iteration.
See #3186
2025-11-14 13:56:10 +01:00
61 changed files with 712 additions and 811 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
= Spring Data for Elasticsearch image:https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A["Revved up by Develocity", link="https://ge.spring.io/scans?search.rootProjectNames=Spring Data Elasticsearch"]
= Spring Data for Elasticsearch image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-elasticsearch%2Fmain&subject=Build[link=https://jenkins.spring.io/view/SpringData/job/spring-data-elasticsearch/] https://gitter.im/spring-projects/spring-data[image:https://badges.gitter.im/spring-projects/spring-data.svg[Gitter]] image:https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A["Revved up by Develocity", link="https://ge.spring.io/scans?search.rootProjectNames=Spring Data Elasticsearch"]
The primary goal of the https://projects.spring.io/spring-data[Spring Data] project is to make it easier to build Spring-powered applications that use new data access technologies such as non-relational databases, map-reduce frameworks, and cloud based data services.
@@ -1,30 +0,0 @@
name: Build Release
runs:
using: composite
steps:
- name: Write settings.xml
if: ${{ env.RELEASE_TRAIN_SETTINGS_XML != '' }}
shell: bash
run: |
echo "::notice::Writing provided settings-xml input to release-train-settings.xml"
echo "${RELEASE_TRAIN_SETTINGS_XML}" > release-train-settings.xml
- name: Prepare Maven command
shell: bash
run: |
MAVEN_LOGGING_OPTS=()
if [[ "${RUNNER_DEBUG}" == "1" || "${ACTIONS_STEP_DEBUG}" == "true" ]]; then
echo "::notice::transfer-progress enabled"
else
MAVEN_LOGGING_OPTS+=(--no-transfer-progress)
fi
echo "RELEASE_TRAIN_MVN=./mvnw --batch-mode ${MAVEN_LOGGING_OPTS[*]} --settings release-train-settings.xml clean deploy -DaltDeploymentRepository=release-train::file://$(pwd)/deployment-repository -DskipTests -Dgpg.skip" >> "${GITHUB_ENV}"
- name: Build Release
shell: bash
run: ${RELEASE_TRAIN_MVN} --activate-profiles releaseTrain,release,ci
- name: Distribution build
shell: bash
run: ${RELEASE_TRAIN_MVN} --activate-profiles releaseTrain,distribute
- name: Schema build
shell: bash
run: ${RELEASE_TRAIN_MVN} --activate-profiles releaseTrain,distribute-schema
@@ -1,6 +0,0 @@
artifactory:
artifacts:
- pattern: "/**/spring-data-*-docs.zip"
properties:
zip.deployed: "false"
zip.type: "docs"
@@ -1,24 +0,0 @@
name: Test Release
runs:
using: composite
steps:
- name: Write settings.xml
if: ${{ env.RELEASE_TRAIN_SETTINGS_XML != '' }}
shell: bash
run: |
echo "::notice::Writing provided settings-xml input to release-train-settings.xml"
echo "${RELEASE_TRAIN_SETTINGS_XML}" > release-train-settings.xml
- name: Prepare Maven command
shell: bash
run: |
MAVEN_LOGGING_OPTS=()
if [[ "${RUNNER_DEBUG}" == "1" || "${ACTIONS_STEP_DEBUG}" == "true" ]]; then
echo "::notice::transfer-progress enabled"
else
MAVEN_LOGGING_OPTS+=(--no-transfer-progress)
fi
echo "RELEASE_TRAIN_MVN=./mvnw --batch-mode ${MAVEN_LOGGING_OPTS[*]} --settings release-train-settings.xml clean verify -Dgpg.skip" >> "${GITHUB_ENV}"
- name: Test Release
shell: bash
run: ${RELEASE_TRAIN_MVN} --activate-profiles releaseTrain,release,ci
-26
View File
@@ -1,26 +0,0 @@
workflow:
generator:
project:
java:
versions:
primary: 25
workflows:
release-train:
join:
watch: false
leave:
watch: false
ready:
watch: false
build:
env:
COMMERCIAL_REPO_USERNAME: secrets.COMMERCIAL_ARTIFACTORY_USERNAME
COMMERCIAL_REPO_PASSWORD: secrets.COMMERCIAL_ARTIFACTORY_PASSWORD
COMMERCIAL_RELEASE_REPO_URL: vars.COMMERCIAL_RELEASE_REPO_URL
RELEASE_TRAIN_SETTINGS_XML: vars.RELEASE_TRAIN_SETTINGS_XML
test:
env:
COMMERCIAL_REPO_USERNAME: secrets.COMMERCIAL_ARTIFACTORY_USERNAME
COMMERCIAL_REPO_PASSWORD: secrets.COMMERCIAL_ARTIFACTORY_PASSWORD
COMMERCIAL_RELEASE_REPO_URL: vars.COMMERCIAL_RELEASE_REPO_URL
RELEASE_TRAIN_SETTINGS_XML: vars.RELEASE_TRAIN_SETTINGS_XML
-33
View File
@@ -1,33 +0,0 @@
name: CI Build
on:
workflow_dispatch:
push:
branches: [ 6.0.x, 'issue/6.0.x/**' ]
permissions: read-all
jobs:
build-java:
strategy:
matrix:
java-version: [ base, main ]
name: Build project
runs-on: ${{ vars.ACTION_RUNNER || 'ubuntu-latest' }}
steps:
- uses: actions/checkout@v6
- name: Setup Java and Maven
uses: spring-projects/spring-data-build/actions/setup-maven@4.0.x
with:
java-version: ${{ matrix.java-version }}
develocity-access-key: '${{ secrets.DEVELOCITY_ACCESS_KEY }}'
- name: Build
uses: spring-projects/spring-data-build/actions/maven-build@4.0.x
env:
OSS_ARTIFACTORY_USR: '${{ secrets.ARTIFACTORY_USERNAME }}'
OSS_ARTIFACTORY_PSW: '${{ secrets.ARTIFACTORY_PASSWORD }}'
COMMERCIAL_ARTIFACTORY_USR: '${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}'
COMMERCIAL_ARTIFACTORY_PSW: '${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}'
TESTCONTAINERS_REUSE_ENABLE: true
with:
settings-xml: '${{ vars.SETTINGS_XML }}'
-1
View File
@@ -4,7 +4,6 @@ name: "CodeQL Advanced"
on:
push:
branches: [ 6.0.x ]
pull_request:
workflow_dispatch:
schedule:
-45
View File
@@ -1,45 +0,0 @@
# GitHub Actions to automate GitHub issues for Spring Data Project Management
name: Spring Data GitHub Issues
on:
issues:
types: [opened, edited, reopened]
issue_comment:
types: [created]
pull_request_target:
types: [opened, edited, reopened]
permissions:
contents: read
issues: write
pull-requests: write
jobs:
Inbox:
runs-on: ubuntu-latest
if: vars.PROJECT_CARDS_ENABLED == 'true' && github.repository_owner == 'spring-projects' && (github.event.action == 'opened' || github.event.action == 'reopened') && github.event.pull_request == null && !contains(join(github.event.issue.labels.*.name, ', '), 'dependency-upgrade') && !contains(github.event.issue.title, 'Release ')
steps:
- name: Create or Update Issue Card
uses: actions/add-to-project@v1.0.2
with:
project-url: https://github.com/orgs/spring-projects/projects/25
github-token: ${{ secrets.GH_ISSUES_TOKEN_SPRING_DATA }}
Pull-Request:
runs-on: ubuntu-latest
if: vars.PROJECT_CARDS_ENABLED == 'true' && github.repository_owner == 'spring-projects' && (github.event.action == 'opened' || github.event.action == 'reopened') && github.event.pull_request != null
steps:
- name: Create or Update Pull Request Card
uses: actions/add-to-project@v1.0.2
with:
project-url: https://github.com/orgs/spring-projects/projects/25
github-token: ${{ secrets.GH_ISSUES_TOKEN_SPRING_DATA }}
Feedback-Provided:
runs-on: ubuntu-latest
if: vars.PROJECT_CARDS_ENABLED == 'true' && github.repository_owner == 'spring-projects' && github.event_name == 'issue_comment' && github.event.action == 'created' && github.actor != 'spring-projects-issues' && github.event.pull_request == null && github.event.issue.state == 'open' && contains(toJSON(github.event.issue.labels), 'waiting-for-feedback')
steps:
- name: Update Project Card
uses: actions/add-to-project@v1.0.2
with:
project-url: https://github.com/orgs/spring-projects/projects/25
github-token: ${{ secrets.GH_ISSUES_TOKEN_SPRING_DATA }}
-92
View File
@@ -1,92 +0,0 @@
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
name: "Release Train Build"
run-name: "${{ inputs.callback-ref }} Build"
"on":
workflow_dispatch:
inputs:
callback:
description: "Repository to which a callback should be made upon completion"
required: true
type: "string"
callback-ref:
description: "Ref in the callback repository to which a callback should be made upon completion"
required: true
type: "string"
release-train-maven-repository-url:
description: "URL of a Maven repository to be used to resolve artifacts of projects earlier in the train"
required: true
type: "string"
permissions:
contents: "read"
concurrency:
group: "${{ github.workflow }}-${{ github.ref }}"
jobs:
build-release:
name: "Build Release"
runs-on: "ubuntu22-2-8"
steps:
- name: "Prevent Re-runs"
id: "prevent-re-runs"
run: |-
if [ "$GITHUB_RUN_ATTEMPT" -gt 1 ]; then
echo "Re-runs are prohibited. Use the 'Release Train  Retry' workflow to retry build failures"
exit 1
fi
- name: "Set up Java"
id: "set-up-java"
uses: "actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95" # v5.6.0
with:
distribution: "liberica"
java-version: "25"
- name: "Check Out Code"
id: "check-out-code"
uses: "actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0" # v7.0.0
- name: "Build Release"
id: "build-release"
uses: "./.github/actions/release-train-build"
env:
COMMERCIAL_RELEASE_REPO_URL: "${{ vars.COMMERCIAL_RELEASE_REPO_URL }}"
COMMERCIAL_REPO_PASSWORD: "${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}"
COMMERCIAL_REPO_USERNAME: "${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}"
RELEASE_TRAIN_MAVEN_REPOSITORY_PASSWORD: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_PASSWORD }}"
RELEASE_TRAIN_MAVEN_REPOSITORY_URL: "${{ inputs.release-train-maven-repository-url }}"
RELEASE_TRAIN_MAVEN_REPOSITORY_USERNAME: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_USERNAME }}"
RELEASE_TRAIN_SETTINGS_XML: "${{ vars.RELEASE_TRAIN_SETTINGS_XML }}"
- name: "Upload Deployment Repository"
id: "upload-deployment-repository"
uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7.0.1
with:
name: "deployment-repository"
path: "deployment-repository/**"
- name: "Upload Deployment Spec"
id: "upload-deployment-spec"
uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7.0.1
with:
archive: "false"
if-no-files-found: "ignore"
name: "deployment-spec"
path: ".github/actions/release-train-build/deployment-spec.yml"
- name: "Save Build System Caches"
id: "save-build-system-caches"
uses: "actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9" # v6.1.0
with:
key: "release-train-${{ inputs.callback-ref }}-${{ github.ref_name }}"
path: "~/.m2/repository"
- name: "Send Callback"
id: "send-callback"
if: "${{ !cancelled() }}"
env:
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
run: |-
gh workflow run callback \
--repo ${{ inputs.callback }} \
--ref ${{ inputs.callback-ref }} \
--field commit-hash=${{ steps.check-out-code.outputs.commit }} \
--field deployment-repository-artifact-identifier=${{ steps.upload-deployment-repository.outputs.artifact-id }} \
--field deployment-spec-artifact-identifier=${{ steps.upload-deployment-spec.outputs.artifact-id }} \
--field release-branch=${{ github.ref_name }} \
--field release-repository=${{ github.repository }} \
--field result=${{ job.status == 'success' && 'built' || 'build-failed' }} \
--field workflow-run-url=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
-43
View File
@@ -1,43 +0,0 @@
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
name: "Release Train Join"
run-name: "${{ inputs.release-train }} Join"
"on":
workflow_dispatch:
inputs:
deployment-destination:
description: "Destination to which the release should be deployed"
options:
- "Maven Central"
- "Spring Enterprise"
required: true
type: "choice"
release-train:
description: "Release train"
required: true
type: "string"
release-train-repository:
default: "spring-io/release-train"
description: "Release train repository"
required: true
type: "string"
permissions:
contents: "none"
jobs:
join-release-train:
name: "Join Release Train"
runs-on: "ubuntu-latest"
steps:
- name: "Join Release Train"
id: "join-release-train"
env:
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
run: |-
gh workflow run join \
--repo ${{ inputs.release-train-repository }} \
--ref ${{ inputs.release-train }} \
--field commit-hash=${{ github.sha }} \
--field deployment-destination=${{ inputs.deployment-destination == 'Maven Central' && 'maven-central' || 'spring-enterprise' }} \
--field release-branch=${{ github.ref_name }} \
--field release-repository=${{ github.repository }}
-34
View File
@@ -1,34 +0,0 @@
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
name: "Release Train Leave"
run-name: "${{ inputs.release-train }} Leave"
"on":
workflow_dispatch:
inputs:
release-train:
description: "Release train"
required: true
type: "string"
release-train-repository:
default: "spring-io/release-train"
description: "Release train repository"
required: true
type: "string"
permissions:
contents: "none"
jobs:
leave:
name: "Leave"
runs-on: "ubuntu-latest"
steps:
- name: "Leave"
id: "leave"
env:
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
run: |-
gh workflow run leave \
--repo ${{ inputs.release-train-repository }} \
--ref ${{ inputs.release-train }} \
--field release-branch=${{ github.ref_name }} \
--field release-repository=${{ github.repository }}
-35
View File
@@ -1,35 +0,0 @@
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
name: "Release Train Ready"
run-name: "${{ inputs.release-train }} Ready"
"on":
workflow_dispatch:
inputs:
release-train:
description: "Release train"
required: true
type: "string"
release-train-repository:
default: "spring-io/release-train"
description: "Release train repository"
required: true
type: "string"
permissions:
contents: "none"
jobs:
ready:
name: "Ready"
runs-on: "ubuntu-latest"
steps:
- name: "Ready"
id: "ready"
env:
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
run: |-
gh workflow run ready \
--repo ${{ inputs.release-train-repository }} \
--ref ${{ inputs.release-train }} \
--field commit-hash=${{ github.sha }} \
--field release-branch=${{ github.ref_name }} \
--field release-repository=${{ github.repository }}
-34
View File
@@ -1,34 +0,0 @@
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
name: "Release Train Retry"
run-name: "${{ inputs.release-train }} Retry"
"on":
workflow_dispatch:
inputs:
release-train:
description: "Release train"
required: true
type: "string"
release-train-repository:
default: "spring-io/release-train"
description: "Release train repository"
required: true
type: "string"
permissions:
contents: "none"
jobs:
trigger-retry:
name: "Trigger Retry"
runs-on: "ubuntu-latest"
steps:
- name: "Trigger Retry"
id: "trigger-retry"
env:
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
run: |-
gh workflow run retry \
--repo ${{ inputs.release-train-repository }} \
--ref ${{ inputs.release-train }} \
--field release-branch=${{ github.ref_name }} \
--field release-repository=${{ github.repository }}
-85
View File
@@ -1,85 +0,0 @@
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
name: "Release Train Test"
run-name: "${{ inputs.callback-ref }} Test"
"on":
workflow_dispatch:
inputs:
callback:
description: "Repository to which a callback should be made upon completion"
required: true
type: "string"
callback-ref:
description: "Ref in the callback repository to which a callback should be made upon completion"
required: true
type: "string"
release-train-maven-repository-url:
description: "URL of a Maven repository to be used to resolve artifacts of projects earlier in the train"
required: true
type: "string"
permissions:
contents: "read"
concurrency:
group: "${{ github.workflow }}-${{ github.ref }}"
jobs:
test-release:
name: "Test Release"
runs-on: "ubuntu22-2-8"
steps:
- name: "Prevent Re-runs"
id: "prevent-re-runs"
run: |-
if [ "$GITHUB_RUN_ATTEMPT" -gt 1 ]; then
echo "Re-runs are prohibited. Use the 'Release Train  Retry' workflow to retry test failures"
exit 1
fi
- name: "Set up Java"
id: "set-up-java"
uses: "actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95" # v5.6.0
with:
distribution: "liberica"
java-version: "25"
- name: "Check Out Code"
id: "check-out-code"
uses: "actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0" # v7.0.0
- name: "Restore Build System Caches"
id: "restore-build-system-caches"
uses: "actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9" # v6.1.0
with:
key: "release-train-${{ inputs.callback-ref }}-${{ github.ref_name }}"
path: "~/.m2/repository"
- name: "Test Release"
id: "test-release"
uses: "./.github/actions/release-train-test"
env:
COMMERCIAL_RELEASE_REPO_URL: "${{ vars.COMMERCIAL_RELEASE_REPO_URL }}"
COMMERCIAL_REPO_PASSWORD: "${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}"
COMMERCIAL_REPO_USERNAME: "${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}"
RELEASE_TRAIN_MAVEN_REPOSITORY_PASSWORD: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_PASSWORD }}"
RELEASE_TRAIN_MAVEN_REPOSITORY_URL: "${{ inputs.release-train-maven-repository-url }}"
RELEASE_TRAIN_MAVEN_REPOSITORY_USERNAME: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_USERNAME }}"
RELEASE_TRAIN_SETTINGS_XML: "${{ vars.RELEASE_TRAIN_SETTINGS_XML }}"
- name: "Send Callback"
id: "send-callback"
if: "${{ !cancelled() }}"
env:
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
run: |-
gh workflow run callback \
--repo ${{ inputs.callback }} \
--ref ${{ inputs.callback-ref }} \
--field commit-hash=${{ steps.check-out-code.outputs.commit }} \
--field release-branch=${{ github.ref_name }} \
--field release-repository=${{ github.repository }} \
--field result=${{ job.status == 'success' && 'tested' || 'test-failed' }} \
--field workflow-run-url=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- name: "Upload Build System Reports"
id: "upload-build-system-reports"
if: "${{ failure() }}"
uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7.0.1
with:
name: "build-system-reports"
path: |-
**/target/surefire-reports
**/target/failsafe-reports
-31
View File
@@ -1,31 +0,0 @@
name: Snapshots
on:
workflow_dispatch:
push:
branches: [ 6.0.x, 'issue/6.0.x/**', 'release/**' ]
permissions: read-all
jobs:
build-snapshots:
name: Build and deploy snapshots
if: ${{ github.repository_owner == 'spring-projects' }}
runs-on: ${{ vars.ACTION_RUNNER || 'ubuntu-latest' }}
steps:
- uses: actions/checkout@v6
- name: Setup Java and Maven
uses: spring-projects/spring-data-build/actions/setup-maven@4.0.x
with:
develocity-access-key: '${{ secrets.DEVELOCITY_ACCESS_KEY }}'
- name: Deploy to Artifactory
uses: spring-projects/spring-data-build/actions/maven-artifactory-deploy@4.0.x
env:
OSS_ARTIFACTORY_USR: '${{ secrets.ARTIFACTORY_USERNAME }}'
OSS_ARTIFACTORY_PSW: '${{ secrets.ARTIFACTORY_PASSWORD }}'
COMMERCIAL_ARTIFACTORY_USR: '${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}'
COMMERCIAL_ARTIFACTORY_PSW: '${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}'
TESTCONTAINERS_REUSE_ENABLE: true
with:
build-name: 'spring-data-elasticsearch'
settings-xml: '${{ vars.SETTINGS_XML }}'
+2 -2
View File
@@ -1,3 +1,3 @@
#Tue Jun 02 14:59:45 CEST 2026
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.16/apache-maven-3.9.16-bin.zip
#Thu Jul 17 13:59:56 CEST 2025
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.11/apache-maven-3.9.11-bin.zip
+39 -1
View File
@@ -1,5 +1,43 @@
= Continuous Integration
image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-elasticsearch%2Fmain&subject=2020.0.0%20(main)[link=https://jenkins.spring.io/view/SpringData/job/spring-data-elasticsearch/]
image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-elasticsearch%2F4.0.x&subject=Neumann%20(4.0.x)[link=https://jenkins.spring.io/view/SpringData/job/spring-data-elasticsearch/]
image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-elasticsearch%2F3.2.x&subject=Moore%20(3.2.x)[link=https://jenkins.spring.io/view/SpringData/job/spring-data-elasticsearch/]
== Running CI tasks locally
You can run CI jobs locally using Docker and act[https://nektosact.com/].
Since this pipeline is purely Docker-based, it's easy to:
* Debug what went wrong on your local machine.
* Test out a a tweak to your `verify.sh` script before sending it out.
* Experiment against a new image before submitting your pull request.
All of these use cases are great reasons to essentially run what the CI server does on your local machine.
IMPORTANT: To do this you must have Docker installed on your machine.
1. `docker run -it --mount type=bind,source="$(pwd)",target=/spring-data-elasticsearch-github adoptopenjdk/openjdk8:latest /bin/bash`
+
This will launch the Docker image and mount your source code at `spring-data-elasticsearch-github`.
+
2. `cd spring-data-elasticsearch-github`
+
Next, run your tests from inside the container:
+
3. `./mvnw clean dependency:list test -Dsort` (or whatever profile you need to test out)
Since the container is binding to your source, you can make edits from your IDE and continue to run build jobs.
If you need to package things up, do this:
1. `docker run -it -v /var/run/docker.sock:/var/run/docker.sock --mount type=bind,source="$(pwd)",target=/spring-data-elasticsearch-github adoptopenjdk/openjdk8:latest /bin/bash`
+
This will launch the Docker image and mount your source code at `spring-data-elasticsearch-github`.
+
2. `cd spring-data-elasticsearch-github`
+
Next, try to package everything up from inside the container:
+
3. `./mvnw -Pci,snapshot -Dmaven.test.skip=true clean package`
NOTE: Docker containers can eat up disk space fast! From time to time, run `docker system prune` to clean out old images.
Vendored
+132
View File
@@ -0,0 +1,132 @@
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)
}
options {
disableConcurrentBuilds()
buildDiscarder(logRotator(numToKeepStr: '14'))
}
stages {
stage("test: baseline (main)") {
when {
beforeAgent(true)
anyOf {
branch(pattern: "main|(\\d\\.\\d\\.x)", comparator: "REGEXP")
not { triggeredBy 'UpstreamCause' }
}
}
agent {
label 'data'
}
options { timeout(time: 30, unit: 'MINUTES') }
environment {
ARTIFACTORY = credentials("${p['artifactory.credentials']}")
DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}")
}
steps {
script {
docker.withRegistry(p['docker.proxy.registry'], p['docker.proxy.credentials']) {
docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) {
sh "PROFILE=none JENKINS_USER_NAME=${p['jenkins.user.name']} ci/verify.sh"
sh "JENKINS_USER_NAME=${p['jenkins.user.name']} ci/clean.sh"
}
}
}
}
}
stage("Test other configurations") {
when {
beforeAgent(true)
allOf {
branch(pattern: "main|(\\d\\.\\d\\.x)", comparator: "REGEXP")
not { triggeredBy 'UpstreamCause' }
}
}
parallel {
stage("test: baseline (next)") {
agent {
label 'data'
}
options { timeout(time: 30, unit: 'MINUTES') }
environment {
ARTIFACTORY = credentials("${p['artifactory.credentials']}")
DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}")
}
steps {
script {
docker.withRegistry(p['docker.proxy.registry'], p['docker.proxy.credentials']) {
docker.image(p['docker.java.next.image']).inside(p['docker.java.inside.docker']) {
sh "PROFILE=none JENKINS_USER_NAME=${p['jenkins.user.name']} ci/verify.sh"
sh "JENKINS_USER_NAME=${p['jenkins.user.name']} ci/clean.sh"
}
}
}
}
}
}
}
stage('Release to artifactory') {
when {
beforeAgent(true)
anyOf {
branch(pattern: "main|(\\d\\.\\d\\.x)", comparator: "REGEXP")
not { triggeredBy 'UpstreamCause' }
}
}
agent {
label 'data'
}
options { timeout(time: 20, unit: 'MINUTES') }
environment {
ARTIFACTORY = credentials("${p['artifactory.credentials']}")
DEVELOCITY_ACCESS_KEY = credentials("${p['develocity.access-key']}")
}
steps {
script {
docker.withRegistry(p['docker.proxy.registry'], p['docker.proxy.credentials']) {
docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) {
sh 'MAVEN_OPTS="-Duser.name=' + "${p['jenkins.user.name']}" + ' -Duser.home=/tmp/jenkins-home" ' +
"./mvnw -s settings.xml -Pci,artifactory " +
"-Ddevelocity.storage.directory=/tmp/jenkins-home/.develocity-root " +
"-Dartifactory.server=${p['artifactory.url']} " +
"-Dartifactory.username=${ARTIFACTORY_USR} " +
"-Dartifactory.password=${ARTIFACTORY_PSW} " +
"-Dartifactory.staging-repository=${p['artifactory.repository.snapshot']} " +
"-Dartifactory.build-name=spring-data-elasticsearch " +
"-Dartifactory.build-number=spring-data-elasticsearch-${BRANCH_NAME}-build-${BUILD_NUMBER} " +
"-Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-elasticsearch " +
"-Dmaven.test.skip=true clean deploy -U -B"
}
}
}
}
}
}
post {
changed {
script {
emailext(
subject: "[${currentBuild.fullDisplayName}] ${currentBuild.currentResult}",
mimeType: 'text/html',
recipientProviders: [[$class: 'CulpritsRecipientProvider'], [$class: 'RequesterRecipientProvider']],
body: "<a href=\"${env.BUILD_URL}\">${currentBuild.fullDisplayName} is reported as ${currentBuild.currentResult}</a>")
}
}
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
= Spring Data for Elasticsearch image:https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A["Revved up by Develocity", link="https://ge.spring.io/scans?search.rootProjectNames=Spring Data Elasticsearch"]
= Spring Data for Elasticsearch image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-elasticsearch%2Fmain&subject=Build[link=https://jenkins.spring.io/view/SpringData/job/spring-data-elasticsearch/] https://gitter.im/spring-projects/spring-data[image:https://badges.gitter.im/spring-projects/spring-data.svg[Gitter]] image:https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A["Revved up by Develocity", link="https://ge.spring.io/scans?search.rootProjectNames=Spring Data Elasticsearch"]
The primary goal of the https://projects.spring.io/spring-data[Spring Data] project is to make it easier to build Spring-powered applications that use new data access technologies such as non-relational databases, map-reduce frameworks, and cloud based data services.
Executable
+8
View File
@@ -0,0 +1,8 @@
#!/bin/bash -x
set -euo pipefail
export JENKINS_USER=${JENKINS_USER_NAME}
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 -Ddevelocity.storage.directory=/tmp/jenkins-home/.develocity-root
+33
View File
@@ -0,0 +1,33 @@
# Java versions
java.main.tag=25.0.1_8-jdk-noble
java.next.tag=25.0.1_8-jdk-noble
# Docker container images - standard
docker.java.main.image=library/eclipse-temurin:${java.main.tag}
docker.java.next.image=library/eclipse-temurin:${java.next.tag}
# Supported versions of MongoDB
docker.mongodb.6.0.version=6.0.23
docker.mongodb.7.0.version=7.0.20
docker.mongodb.8.0.version=8.0.9
# Supported versions of Redis
docker.redis.6.version=6.2.13
docker.redis.7.version=7.2.4
docker.redis.8.version=8.2.2
docker.valkey.8.version=8.1.1
# Docker environment settings
docker.java.inside.basic=-v $HOME:/tmp/jenkins-home --ulimit nofile=32000:32000
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 --ulimit nofile=32000:32000
# Credentials
docker.registry=
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.url=https://repo.spring.io
artifactory.repository.snapshot=libs-snapshot-local
develocity.access-key=gradle_enterprise_secret_access_key
jenkins.user.name=spring-builds+jenkins
Executable
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash -x
set -euo pipefail
mkdir -p /tmp/jenkins-home/.m2/spring-data-elasticsearch
export JENKINS_USER=${JENKINS_USER_NAME}
MAVEN_OPTS="-Duser.name=${JENKINS_USER} -Duser.home=/tmp/jenkins-home" \
./mvnw -s settings.xml \
-P${PROFILE} clean dependency:list verify -Dsort -U -B -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-elasticsearch -Ddevelocity.storage.directory=/tmp/jenkins-home/.develocity-root
+22 -18
View File
@@ -5,12 +5,12 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>6.0.7</version>
<version>6.1.0-M1</version>
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>4.0.7</version>
<version>4.1.0-M1</version>
</parent>
<name>Spring Data Elasticsearch</name>
@@ -18,14 +18,14 @@
<url>https://github.com/spring-projects/spring-data-elasticsearch</url>
<properties>
<springdata.commons>4.0.7</springdata.commons>
<springdata.commons>4.1.0-M1</springdata.commons>
<!-- version of the ElasticsearchClient -->
<elasticsearch-java>9.2.9</elasticsearch-java>
<elasticsearch-rest-client>9.2.8</elasticsearch-rest-client>
<elasticsearch-java>9.2.5</elasticsearch-java>
<elasticsearch-rest-client>9.2.5</elasticsearch-rest-client>
<hoverfly>0.20.2</hoverfly>
<log4j>2.25.1</log4j>
<log4j>2.25.3</log4j>
<jsonassert>1.5.3</jsonassert>
<wiremock>3.9.2</wiremock>
@@ -82,7 +82,8 @@
<scm>
<url>https://github.com/spring-projects/spring-data-elasticsearch</url>
<connection>scm:git:git://github.com/spring-projects/spring-data-elasticsearch.git</connection>
<developerConnection>scm:git:ssh://git@github.com/spring-projects/spring-data-elasticsearch.git</developerConnection>
<developerConnection>scm:git:ssh://git@github.com/spring-projects/spring-data-elasticsearch.git
</developerConnection>
</scm>
<issueManagement>
@@ -178,17 +179,30 @@
</dependency>
<!-- Jackson JSON Mapper -->
<dependency>
<groupId>tools.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>tools.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- Version 2 to use with the legacy RestClient -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<!-- CDI -->
<dependency>
<groupId>javax.interceptor</groupId>
<artifactId>javax.interceptor-api</artifactId>
@@ -357,18 +371,8 @@
<filtering>false</filtering>
<excludes>
<exclude>**/versions.properties</exclude>
<exclude>notice.txt</exclude>
<exclude>license.txt</exclude>
</excludes>
</resource>
<resource>
<directory>${project.root}/src/main/resources</directory>
<targetPath>META-INF</targetPath>
<includes>
<include>notice.txt</include>
<include>license.txt</include>
</includes>
</resource>
</resources>
<plugins>
+29
View File
@@ -0,0 +1,29 @@
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
https://maven.apache.org/xsd/settings-1.0.0.xsd">
<servers>
<server>
<id>spring-plugins-release</id>
<username>${env.ARTIFACTORY_USR}</username>
<password>${env.ARTIFACTORY_PSW}</password>
</server>
<server>
<id>spring-libs-snapshot</id>
<username>${env.ARTIFACTORY_USR}</username>
<password>${env.ARTIFACTORY_PSW}</password>
</server>
<server>
<id>spring-libs-milestone</id>
<username>${env.ARTIFACTORY_USR}</username>
<password>${env.ARTIFACTORY_PSW}</password>
</server>
<server>
<id>spring-libs-release</id>
<username>${env.ARTIFACTORY_USR}</username>
<password>${env.ARTIFACTORY_PSW}</password>
</server>
</servers>
</settings>
+7 -3
View File
@@ -6,8 +6,12 @@ nav:
ext:
collector:
- run:
command: ./mvnw -B validate process-resources dependency:unpack -am -Pantora-process-resources
command: ./mvnw validate process-resources -am -Pantora-process-resources
local: true
scan:
- dir: target/classes/
- dir: target/antora/
dir: target/classes/
- run:
command: ./mvnw package -Pdistribute
local: true
scan:
dir: target/antora
@@ -1,12 +1,18 @@
[[new-features]]
= What's new
[[new-features.6-1-0]]
== New in Spring Data Elasticsearch 6.1
* Upgrade to Elasticsearch 9.2.5
* Add support to use `IndexCoordinates` as repository query parameter
[[new-features.6-0-0]]
== New in Spring Data Elasticsearch 6.0
* Upgrade to Spring 7
* Switch to jspecify nullability annotations
* Upgrade to Elasticsearch 9.2.2
* Upgrade to Elasticsearch 9.2.1
* Use the new Elasticsearch Rest5Client as default
* Add support for SpEL expressions in the `settingPath` parameter of the `@Setting` annotation
@@ -6,7 +6,8 @@ The following table shows the Elasticsearch and Spring versions that are used by
[cols="^,^,^,^",options="header"]
|===
| Spring Data Release Train | Spring Data Elasticsearch | Elasticsearch | Spring Framework
| 2025.1 | 6.0.x | 9.2.8 | 7.0.x
| 2026.0 | 6.1.x | 9.2.5 | 7.0.x
| 2025.1 | 6.0.x | 9.2.2 | 7.0.x
| 2025.0 | 5.5.x | 8.18.1 | 6.2.x
| 2024.1 | 5.4.xfootnote:oom[Out of maintenance] | 8.15.5 | 6.1.x
| 2024.0 | 5.3.xfootnote:oom[] | 8.13.4 | 6.1.x
@@ -13,7 +13,7 @@ asciidoc:
include-xml-namespaces: false
spring-data-commons-docs-url: '${documentation.baseurl}/spring-data/commons/reference/${springdata.commons.short}'
spring-data-commons-javadoc-base: '{spring-data-commons-docs-url}/api/java'
springdocsurl: '${documentation.spring-reference-url}/{springversionshort}'
springdocsurl: '${documentation.baseurl}/spring-framework/reference/{springversionshort}'
spring-framework-docs: '{springdocsurl}'
springjavadocurl: '${documentation.spring-javadoc-url}'
spring-framework-javadoc: '{springjavadocurl}'
@@ -17,12 +17,14 @@ package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.JsonpMapper;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.json.jackson.Jackson3JsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.TransportOptions;
import co.elastic.clients.transport.rest5_client.Rest5ClientOptions;
import co.elastic.clients.transport.rest5_client.low_level.RequestOptions;
import co.elastic.clients.transport.rest5_client.low_level.Rest5Client;
import tools.jackson.databind.cfg.JsonNodeFeature;
import tools.jackson.databind.json.JsonMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
@@ -32,10 +34,6 @@ import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.util.Assert;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
/**
* Base class for a @{@link org.springframework.context.annotation.Configuration} class to set up the Elasticsearch
* connection using the Elasticsearch Client. This class exposes different parts of the setup as Spring beans. Deriving
@@ -128,10 +126,11 @@ public abstract class ElasticsearchConfiguration extends ElasticsearchConfigurat
// we need to create our own objectMapper that keeps null values in order to provide the storeNullValue
// functionality. The one Elasticsearch would provide removes the nulls. We remove unwanted nulls before they get
// into this mapper, so we can safely keep them here.
var objectMapper = (new ObjectMapper())
.configure(SerializationFeature.INDENT_OUTPUT, false)
.setSerializationInclusion(JsonInclude.Include.ALWAYS);
return new JacksonJsonpMapper(objectMapper);
JsonMapper jsonMapper = JsonMapper.builder()
.enable(JsonNodeFeature.WRITE_NULL_PROPERTIES)
.enable(JsonNodeFeature.READ_NULL_PROPERTIES)
.build();
return new Jackson3JsonpMapper(jsonMapper);
}
/**
@@ -47,7 +47,7 @@ import com.fasterxml.jackson.databind.SerializationFeature;
* @since 4.4
* @deprecated since 6.0, use {@link ElasticsearchConfiguration}
*/
@Deprecated(since = "6.0", forRemoval=true)
@Deprecated(since = "6.0", forRemoval = true)
public abstract class ElasticsearchLegacyRestClientConfiguration extends ElasticsearchConfigurationSupport {
/**
@@ -16,12 +16,14 @@
package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.json.JsonpMapper;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.json.jackson.Jackson3JsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.TransportOptions;
import co.elastic.clients.transport.rest5_client.Rest5ClientOptions;
import co.elastic.clients.transport.rest5_client.low_level.RequestOptions;
import co.elastic.clients.transport.rest5_client.low_level.Rest5Client;
import tools.jackson.databind.cfg.JsonNodeFeature;
import tools.jackson.databind.json.JsonMapper;
import org.elasticsearch.client.RestClient;
import org.springframework.context.annotation.Bean;
@@ -32,10 +34,6 @@ import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperatio
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.util.Assert;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
/**
* Base class for a @{@link org.springframework.context.annotation.Configuration} class to set up the Elasticsearch
* connection using the {@link ReactiveElasticsearchClient}. This class exposes different parts of the setup as Spring
@@ -127,10 +125,11 @@ public abstract class ReactiveElasticsearchConfiguration extends ElasticsearchCo
// we need to create our own objectMapper that keeps null values in order to provide the storeNullValue
// functionality. The one Elasticsearch would provide removes the nulls. We remove unwanted nulls before they get
// into this mapper, so we can safely keep them here.
var objectMapper = (new ObjectMapper())
.configure(SerializationFeature.INDENT_OUTPUT, false)
.setSerializationInclusion(JsonInclude.Include.ALWAYS);
return new JacksonJsonpMapper(objectMapper);
JsonMapper jsonMapper = JsonMapper.builder()
.enable(JsonNodeFeature.WRITE_NULL_PROPERTIES)
.enable(JsonNodeFeature.READ_NULL_PROPERTIES)
.build();
return new Jackson3JsonpMapper(jsonMapper);
}
/**
@@ -15,9 +15,15 @@
*/
package org.springframework.data.elasticsearch.client.elc.aot;
import co.elastic.clients.elasticsearch._types.mapping.RuntimeFieldType;
import co.elastic.clients.elasticsearch._types.mapping.TypeMapping;
import co.elastic.clients.elasticsearch.indices.IndexSettings;
import co.elastic.clients.elasticsearch.indices.PutMappingRequest;
import org.jspecify.annotations.Nullable;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeReference;
import org.springframework.util.ClassUtils;
/**
@@ -32,14 +38,10 @@ public class ElasticsearchClientRuntimeHints implements RuntimeHintsRegistrar {
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
hints.reflection()
.registerTypeIfPresent(classLoader, "co.elastic.clients.elasticsearch.indices.IndexSettings",
builder -> builder.withField("_DESERIALIZER"))
.registerTypeIfPresent(classLoader, "co.elastic.clients.elasticsearch.indices.PutMappingRequest",
builder -> builder.withField("_DESERIALIZER"))
.registerTypeIfPresent(classLoader, "co.elastic.clients.elasticsearch._types.mapping.RuntimeFieldType",
builder -> builder.withField("_DESERIALIZER"))
.registerTypeIfPresent(classLoader, "co.elastic.clients.elasticsearch._types.mapping.TypeMapping",
builder -> builder.withField("_DESERIALIZER"));
.registerType(TypeReference.of(IndexSettings.class), builder -> builder.withField("_DESERIALIZER"))
.registerType(TypeReference.of(PutMappingRequest.class), builder -> builder.withField("_DESERIALIZER"))
.registerType(TypeReference.of(RuntimeFieldType.class), builder -> builder.withField("_DESERIALIZER"))
.registerType(TypeReference.of(TypeMapping.class), builder -> builder.withField("_DESERIALIZER"));
if (ClassUtils.isPresent("org.apache.http.impl.auth.BasicScheme",
ElasticsearchClientRuntimeHints.class.getClassLoader())) {
@@ -94,18 +94,16 @@ public final class Rest5Clients {
throw new RuntimeException(e);
}
}
httpAsyncClientBuilder.addExecInterceptorFirst("es-rest5-client",
(request, entityProducer, scope, chain, asyncExecCallback) -> {
clientConfiguration.getHeadersSupplier().get().forEach((header, values) -> {
// The accept and content-type headers may already be put on the request, despite this being the
// first interceptor.
if ("Accept".equalsIgnoreCase(header) || "Content-Type".equalsIgnoreCase(header)) {
request.removeHeaders(header);
}
values.forEach(value -> request.addHeader(header, value));
});
chain.proceed(request, entityProducer, scope, asyncExecCallback);
});
httpAsyncClientBuilder.addRequestInterceptorFirst((request, entity, context) -> {
clientConfiguration.getHeadersSupplier().get().forEach((header, values) -> {
// The accept and content-type headers are already put on the request, despite this being the first
// interceptor.
if ("Accept".equalsIgnoreCase(header) || " Content-Type".equalsIgnoreCase(header)) {
request.removeHeaders(header);
}
values.forEach(value -> request.addHeader(header, value));
});
});
// add httpclient configurator callbacks provided by the configuration
for (ClientConfiguration.ClientConfigurationCallback<?> clientConfigurer : clientConfiguration
@@ -245,10 +245,7 @@ abstract public class AbstractReactiveElasticsearchTemplate
public void onNext(List<T> entityList) {
onNextHasBeenCalled.set(true);
saveAll(entityList, index)
.map(entity -> {
sink.tryEmitNext(entity);
return entity;
})
.map(sink::tryEmitNext)
.doOnComplete(() -> {
if (!upstreamComplete.get()) {
if (subscription == null) {
@@ -258,14 +255,7 @@ abstract public class AbstractReactiveElasticsearchTemplate
} else {
sink.tryEmitComplete();
}
})
.subscribe(v -> {
}, error -> {
if (subscription != null) {
subscription.cancel();
}
sink.tryEmitError(error);
});
}).subscribe();
}
@Override
@@ -15,7 +15,8 @@
*/
package org.springframework.data.elasticsearch.core.document;
import java.io.IOException;
import tools.jackson.core.JacksonException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Function;
@@ -87,7 +88,7 @@ public interface Document extends StringObjectMap<Document> {
clear();
try {
putAll(MapDocument.OBJECT_MAPPER.readerFor(Map.class).readValue(json));
} catch (IOException e) {
} catch (JacksonException e) {
throw new ConversionException("Cannot parse JSON", e);
}
return this;
@@ -15,6 +15,9 @@
*/
package org.springframework.data.elasticsearch.core.document;
import tools.jackson.core.JacksonException;
import tools.jackson.databind.ObjectMapper;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -25,9 +28,6 @@ import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.support.DefaultStringObjectMap;
import org.springframework.data.mapping.MappingException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* {@link Document} implementation backed by a {@link LinkedHashMap}.
*
@@ -344,7 +344,7 @@ class MapDocument implements Document {
public String toJson() {
try {
return OBJECT_MAPPER.writeValueAsString(this);
} catch (JsonProcessingException e) {
} catch (JacksonException e) {
throw new MappingException("Cannot render document to JSON", e);
}
}
@@ -1,26 +1,25 @@
package org.springframework.data.elasticsearch.core.geo;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import tools.jackson.core.JacksonException;
import tools.jackson.core.JsonGenerator;
import tools.jackson.core.JsonParser;
import tools.jackson.databind.DeserializationContext;
import tools.jackson.databind.SerializationContext;
import tools.jackson.databind.ValueDeserializer;
import tools.jackson.databind.ValueSerializer;
import org.springframework.data.geo.Point;
class PointSerializer extends JsonSerializer<Point> {
class PointSerializer extends ValueSerializer<Point> {
@Override
public void serialize(Point value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeObject(GeoPoint.fromPoint(value));
public void serialize(Point value, JsonGenerator gen, SerializationContext serializers) throws JacksonException {
gen.writePOJO(GeoPoint.fromPoint(value));
}
}
class PointDeserializer extends JsonDeserializer<Point> {
class PointDeserializer extends ValueDeserializer<Point> {
@Override
public Point deserialize(JsonParser p, DeserializationContext context) throws IOException {
public Point deserialize(JsonParser p, DeserializationContext context) throws JacksonException {
return GeoPoint.toPoint(p.readValueAs(GeoPoint.class));
}
}
@@ -15,14 +15,14 @@
*/
package org.springframework.data.elasticsearch.core.index;
import tools.jackson.databind.node.ObjectNode;
import java.io.IOException;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.annotations.GeoShapeField;
import org.springframework.util.Assert;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* @author Peter-Josef Meisch
*/
@@ -18,6 +18,13 @@ package org.springframework.data.elasticsearch.core.index;
import static org.springframework.data.elasticsearch.core.index.MappingParameters.*;
import static org.springframework.util.StringUtils.*;
import tools.jackson.databind.JsonNode;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.node.ArrayNode;
import tools.jackson.databind.node.ObjectNode;
import tools.jackson.databind.node.StringNode;
import tools.jackson.databind.util.RawValue;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.nio.charset.Charset;
@@ -48,13 +55,6 @@ import org.springframework.data.mapping.PropertyHandler;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.fasterxml.jackson.databind.util.RawValue;
/**
* @author Rizwan Idrees
* @author Mohsin Husen
@@ -184,7 +184,7 @@ public class MappingBuilder {
if (!excludeFromSource.isEmpty()) {
ObjectNode sourceNode = objectNode.putObject(SOURCE);
ArrayNode excludes = sourceNode.putArray(SOURCE_EXCLUDES);
excludeFromSource.stream().map(TextNode::new).forEach(excludes::add);
excludeFromSource.stream().map(StringNode::new).forEach(excludes::add);
}
return objectMapper.writer().writeValueAsString(objectNode);
@@ -238,7 +238,7 @@ public class MappingBuilder {
if (mappingAnnotation.dynamicDateFormats().length > 0) {
objectNode.putArray(DYNAMIC_DATE_FORMATS).addAll(Arrays.stream(mappingAnnotation.dynamicDateFormats())
.map(TextNode::valueOf).collect(Collectors.toList()));
.map(StringNode::valueOf).collect(Collectors.toList()));
}
if (runtimeFields != null) {
@@ -546,7 +546,7 @@ public class MappingBuilder {
if (children.length > 1) {
relationsNode.putArray(parent)
.addAll(Arrays.stream(children).map(TextNode::valueOf).collect(Collectors.toList()));
.addAll(Arrays.stream(children).map(StringNode::valueOf).collect(Collectors.toList()));
} else if (children.length == 1) {
relationsNode.put(parent, children[0]);
}
@@ -15,6 +15,9 @@
*/
package org.springframework.data.elasticsearch.core.index;
import tools.jackson.databind.node.ObjectNode;
import tools.jackson.databind.node.StringNode;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
@@ -28,9 +31,6 @@ import org.springframework.data.elasticsearch.annotations.*;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
/**
* A class to hold the mapping parameters that might be set on
* {@link org.springframework.data.elasticsearch.annotations.Field } or
@@ -285,7 +285,7 @@ public final class MappingParameters {
if (copyTo != null && copyTo.length > 0) {
objectNode.putArray(FIELD_PARAM_COPY_TO)
.addAll(Arrays.stream(copyTo).map(TextNode::valueOf).collect(Collectors.toList()));
.addAll(Arrays.stream(copyTo).map(StringNode::valueOf).collect(Collectors.toList()));
}
if (ignoreAbove != null) {
@@ -90,7 +90,8 @@ public abstract class AbstractElasticsearchRepositoryQuery implements Repository
Query query = createQuery(parameters);
IndexCoordinates index = elasticsearchOperations.getIndexCoordinatesFor(clazz);
IndexCoordinates index = parameterAccessor
.getIndexCoordinates(elasticsearchOperations.getIndexCoordinatesFor(clazz));
Object result = null;
@@ -110,7 +110,7 @@ abstract class AbstractReactiveElasticsearchRepositoryQuery implements Repositor
evaluationContextProvider);
String indexName = queryMethod.getEntityInformation().getIndexName();
IndexCoordinates index = IndexCoordinates.of(indexName);
IndexCoordinates index = parameterAccessor.getIndexCoordinates(IndexCoordinates.of(indexName));
ReactiveElasticsearchQueryExecution execution = getExecution(parameterAccessor,
new ResultProcessingConverter(processor));
@@ -17,6 +17,7 @@ package org.springframework.data.elasticsearch.repository.query;
import org.springframework.core.MethodParameter;
import org.springframework.data.core.TypeInformation;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.RuntimeField;
import org.springframework.data.elasticsearch.core.query.ScriptedField;
import org.springframework.data.repository.query.Parameter;
@@ -42,7 +43,8 @@ public class ElasticsearchParameter extends Parameter {
@Override
public boolean isSpecialParameter() {
return super.isSpecialParameter() || isScriptedFieldParameter() || isRuntimeFieldParameter();
return super.isSpecialParameter() || isScriptedFieldParameter() || isRuntimeFieldParameter()
|| isIndexCoordinatesParameter();
}
public Boolean isScriptedFieldParameter() {
@@ -52,4 +54,8 @@ public class ElasticsearchParameter extends Parameter {
public Boolean isRuntimeFieldParameter() {
return RuntimeField.class.isAssignableFrom(getType());
}
public Boolean isIndexCoordinatesParameter() {
return IndexCoordinates.class.isAssignableFrom(getType());
}
}
@@ -15,6 +15,7 @@
*/
package org.springframework.data.elasticsearch.repository.query;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.repository.query.ParameterAccessor;
/**
@@ -29,4 +30,11 @@ public interface ElasticsearchParameterAccessor extends ParameterAccessor {
* @return
*/
Object[] getValues();
/**
* If there is a parameter of type IndexCoordinates, this parameter value is returned, otherwise the defaults value
*
* @param defaults default value
*/
IndexCoordinates getIndexCoordinates(IndexCoordinates defaults);
}
@@ -29,10 +29,11 @@ import org.springframework.data.repository.query.ParametersSource;
* @since 3.2
*/
public class ElasticsearchParameters extends Parameters<ElasticsearchParameters, ElasticsearchParameter> {
private final List<ElasticsearchParameter> scriptedFields = new ArrayList<>();
private final List<ElasticsearchParameter> runtimeFields = new ArrayList<>();
private final int indexCoordinatesIndex;
public ElasticsearchParameters(ParametersSource parametersSource) {
super(parametersSource,
@@ -53,6 +54,23 @@ public class ElasticsearchParameters extends Parameters<ElasticsearchParameters,
runtimeFields.add(parameter);
}
}
this.indexCoordinatesIndex = initIndexCoordinatesIndex();
}
private int initIndexCoordinatesIndex() {
int indexCoordinatesIndex = -1;
int index = 0;
for (ElasticsearchParameter parameter : this) {
if (parameter.isIndexCoordinatesParameter()) {
if (indexCoordinatesIndex != -1) {
throw new IllegalArgumentException(this + " can only contain at most one IndexCoordinates parameter.");
} else {
indexCoordinatesIndex = index;
}
}
index++;
}
return indexCoordinatesIndex;
}
private ElasticsearchParameter parameterFactory(MethodParameter methodParameter, TypeInformation<?> domainType) {
@@ -61,6 +79,7 @@ public class ElasticsearchParameters extends Parameters<ElasticsearchParameters,
private ElasticsearchParameters(List<ElasticsearchParameter> parameters) {
super(parameters);
this.indexCoordinatesIndex = initIndexCoordinatesIndex();
}
@Override
@@ -75,4 +94,12 @@ public class ElasticsearchParameters extends Parameters<ElasticsearchParameters,
List<ElasticsearchParameter> getRuntimeFields() {
return runtimeFields;
}
public boolean hasIndexCoordinatesParameter() {
return this.indexCoordinatesIndex != -1;
}
public int getIndexCoordinatesIndex() {
return indexCoordinatesIndex;
}
}
@@ -15,6 +15,7 @@
*/
package org.springframework.data.elasticsearch.repository.query;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.repository.query.ParametersParameterAccessor;
/**
@@ -25,6 +26,7 @@ public class ElasticsearchParametersParameterAccessor extends ParametersParamete
implements ElasticsearchParameterAccessor {
private final Object[] values;
private final ElasticsearchParameters eleasticSearchParameters;
/**
* Creates a new {@link ElasticsearchParametersParameterAccessor}.
@@ -36,10 +38,19 @@ public class ElasticsearchParametersParameterAccessor extends ParametersParamete
super(method.getParameters(), values);
this.values = values;
this.eleasticSearchParameters = method.getParameters();
}
@Override
public Object[] getValues() {
return values;
}
@Override
public IndexCoordinates getIndexCoordinates(IndexCoordinates defaults) {
if (!eleasticSearchParameters.hasIndexCoordinatesParameter()) {
return defaults;
}
return (IndexCoordinates) getValues()[eleasticSearchParameters.getIndexCoordinatesIndex()];
}
}
@@ -1,39 +0,0 @@
/*
* Copyright 2013-present 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.repository.query;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.repository.query.ValueExpressionDelegate;
/**
* ElasticsearchPartQuery
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Kevin Leturc
* @author Mark Paluch
* @author Rasmus Faber-Espensen
* @author Peter-Josef Meisch
* @author Haibo Liu
* @deprecated since 5.5, use {@link RepositoryPartQuery} instead
*/
@Deprecated(forRemoval = true)
public class ElasticsearchPartQuery extends RepositoryPartQuery {
public ElasticsearchPartQuery(ElasticsearchQueryMethod method, ElasticsearchOperations elasticsearchOperations,
ValueExpressionDelegate valueExpressionDelegate) {
super(method, elasticsearchOperations, valueExpressionDelegate);
}
}
@@ -373,6 +373,11 @@ public class ElasticsearchQueryMethod extends QueryMethod {
return fieldNames.toArray(new String[0]);
}
@Override
public ElasticsearchParameters getParameters() {
return (ElasticsearchParameters) super.getParameters();
}
// region Copied from QueryMethod base class
/*
* Copied from the QueryMethod class adding support for collections of SearchHit instances. No static method here.
@@ -1,38 +0,0 @@
/*
* Copyright 2013-present 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.repository.query;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.repository.query.ValueExpressionDelegate;
/**
* ElasticsearchStringQuery
*
* @author Rizwan Idrees
* @author Mohsin Husen
* @author Mark Paluch
* @author Taylor Ono
* @author Peter-Josef Meisch
* @author Haibo Liu
* @deprecated since 5.5, use {@link RepositoryStringQuery}
*/
@Deprecated(since = "5.5", forRemoval = true)
public class ElasticsearchStringQuery extends RepositoryStringQuery {
public ElasticsearchStringQuery(ElasticsearchQueryMethod queryMethod, ElasticsearchOperations elasticsearchOperations,
String queryString, ValueExpressionDelegate valueExpressionDelegate) {
super(queryMethod, elasticsearchOperations, queryString, valueExpressionDelegate);
}
}
@@ -136,11 +136,6 @@ public class ReactiveElasticsearchQueryMethod extends ElasticsearchQueryMethod {
return true;
}
@Override
public ElasticsearchParameters getParameters() {
return (ElasticsearchParameters) super.getParameters();
}
@Override
protected boolean isAllowedGenericType(ParameterizedType methodGenericReturnType) {
return super.isAllowedGenericType(methodGenericReturnType)
@@ -1,40 +0,0 @@
/*
* Copyright 2019-present 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.repository.query;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.repository.query.ValueExpressionDelegate;
/**
* @author Christoph Strobl
* @author Taylor Ono
* @author Haibo Liu
* @since 3.2
* @deprecated since 5.5, use {@link ReactiveRepositoryStringQuery}
*/
@Deprecated(since = "5.5", forRemoval = true)
public class ReactiveElasticsearchStringQuery extends ReactiveRepositoryStringQuery {
public ReactiveElasticsearchStringQuery(ReactiveElasticsearchQueryMethod queryMethod,
ReactiveElasticsearchOperations operations, ValueExpressionDelegate valueExpressionDelegate) {
super(queryMethod, operations, valueExpressionDelegate);
}
public ReactiveElasticsearchStringQuery(String query, ReactiveElasticsearchQueryMethod queryMethod,
ReactiveElasticsearchOperations operations, ValueExpressionDelegate valueExpressionDelegate) {
super(query, queryMethod, operations, valueExpressionDelegate);
}
}
@@ -15,7 +15,9 @@
*/
package org.springframework.data.elasticsearch.support;
import java.io.IOException;
import tools.jackson.core.JacksonException;
import tools.jackson.databind.ObjectMapper;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -25,9 +27,6 @@ import java.util.function.BiConsumer;
import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* @author Peter-Josef Meisch
*/
@@ -48,7 +47,7 @@ public class DefaultStringObjectMap<T extends StringObjectMap<T>> implements Str
public String toJson() {
try {
return OBJECT_MAPPER.writeValueAsString(this);
} catch (JsonProcessingException e) {
} catch (JacksonException e) {
throw new IllegalArgumentException("Cannot render document to JSON", e);
}
}
@@ -61,7 +60,7 @@ public class DefaultStringObjectMap<T extends StringObjectMap<T>> implements Str
delegate.clear();
try {
delegate.putAll(OBJECT_MAPPER.readerFor(Map.class).readValue(json));
} catch (IOException e) {
} catch (JacksonException e) {
throw new IllegalArgumentException("Cannot parse JSON", e);
}
return (T) this;
+17 -3
View File
@@ -1,4 +1,3 @@
Apache License
Version 2.0, January 2004
https://www.apache.org/licenses/
@@ -179,7 +178,7 @@
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
@@ -187,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -200,3 +199,18 @@
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.
=======================================================================
To the extent any open source subcomponents are licensed under the EPL and/or other
similar licenses that require the source code and/or modifications to
source code to be made available (as would be noted above), you may obtain a
copy of the source code corresponding to the binaries for such open source
components and modifications thereto, if any, (the "Source Files"), by
downloading the Source Files from https://www.springsource.org/download,
or by sending a request, with your name and address to: VMware, Inc., 3401 Hillview
Avenue, Palo Alto, CA 94304, United States of America or email info@vmware.com. All
such requests should clearly specify: OPEN SOURCE FILES REQUEST, Attention General
Counsel. VMware shall mail a copy of the Source Files to you on a CD or equivalent
physical medium. This offer to obtain a copy of the Source Files is valid for three
years from the date you acquired this Software product.
+2 -6
View File
@@ -1,5 +1,5 @@
Spring Data Elasticsearch 6.0.7 (2025.1.7)
Copyright (c) [2013-2026] Pivotal Software, Inc.
Spring Data Elasticsearch 6.1 M1 (2026.0.0)
Copyright (c) [2013-2022] Pivotal Software, Inc.
This product is licensed to you under the Apache License, Version 2.0 (the "License").
You may not use this product except in compliance with the License.
@@ -17,7 +17,3 @@ conditions of the subcomponent's license, as noted in the LICENSE file.
@@ -16,6 +16,7 @@
package org.springframework.data.elasticsearch.client;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static io.specto.hoverfly.junit.dsl.HoverflyDsl.*;
import static io.specto.hoverfly.junit.verification.HoverflyVerifications.*;
@@ -31,7 +32,6 @@ import io.specto.hoverfly.junit5.api.HoverflyConfig;
import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.stream.Stream;
@@ -108,8 +108,6 @@ public class RestClientsTest {
AtomicInteger connectionConfigurerCount = new AtomicInteger(0);
AtomicInteger connectionManagerConfigurerCount = new AtomicInteger(0);
AtomicInteger requestConfigurerCount = new AtomicInteger(0);
ThreadLocal<String> threadLocal = ThreadLocal.withInitial(() -> null);
AtomicReference<String> supplierThreadName = new AtomicReference<>();
ClientConfigurationBuilder configurationBuilder = new ClientConfigurationBuilder();
configurationBuilder //
@@ -117,9 +115,7 @@ public class RestClientsTest {
.withBasicAuth("user", "password") //
.withDefaultHeaders(defaultHeaders) //
.withHeaders(() -> {
supplierThreadName.set(Thread.currentThread().getName());
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("thread", threadLocal.get());
httpHeaders.add("supplied", "val0");
httpHeaders.add("supplied", "val" + supplierCount.getAndIncrement());
return httpHeaders;
@@ -183,7 +179,6 @@ public class RestClientsTest {
// do several calls to check that the headerSupplier provided values are set
int startValue = clientUnderTest.usesInitialRequest() ? 2 : 1;
for (int i = startValue; i <= startValue + 2; i++) {
threadLocal.set("local");
clientUnderTest.ping();
verify(headRequestedFor(urlEqualTo("/")) //
@@ -194,15 +189,10 @@ public class RestClientsTest {
.withHeader("supplied", new EqualToPattern("val0")) //
// on the first call Elasticsearch does the version check and thus already increments the counter
.withHeader("supplied", new EqualToPattern("val" + i)) //
.withHeader("supplied", including("val0", "val" + i)) //
.withHeader("thread", new EqualToPattern("local")));
.withHeader("supplied", including("val0", "val" + i)));
;
}
// the headers supplier must run on the calling thread, so a ThreadLocal set above is visible (#3300).
// this guards against future regressions that move the supplier back onto the async I/O thread.
assertThat(supplierThreadName.get()).isEqualTo(Thread.currentThread().getName());
assertThat(restClientConfigurerCount).hasValue(clientUnderTestFactory.getExpectedRestClientConfigurerCalls());
assertThat(httpClientConfigurerCount).hasValue(1);
assertThat(connectionConfigurerCount).hasValue(clientUnderTestFactory.getExpectedConnectionConfigurerCalls());
@@ -1220,28 +1220,6 @@ public abstract class ReactiveElasticsearchIntegrationTests {
.allMatch(failureStatus -> failureStatus.status().equals(409));
}
@Test // Error propagation in reactive Flux save
@DisplayName("should propagate errors during Flux save operations")
void shouldPropagateErrorsDuringFluxSaveOperations() {
// Create a Flux that will produce an error after emitting some valid entities
Flux<SampleEntity> entitiesWithError = Flux.concat(
Flux.just(
randomEntity("valid entity 1"),
randomEntity("valid entity 2")),
Flux.error(new RuntimeException("Simulated error during entity creation")));
// The save operation should propagate the error to the subscriber.
// With the manual subscriber approach, the error propagates eagerly
// sink.tryEmitError is called before in-flight saveAll results can be emitted,
// so the caller sees 0 entities before the error.
operations.save(entitiesWithError, SampleEntity.class, 10)
.as(StepVerifier::create)
.expectNextCount(0)
.expectErrorMatches(throwable -> throwable instanceof RuntimeException &&
throwable.getMessage().equals("Simulated error during entity creation"))
.verify();
}
// endregion
// region Helper functions
@@ -0,0 +1,23 @@
package org.springframework.data.elasticsearch.repository.query.indexcoordinates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchTemplateConfiguration;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.test.context.ContextConfiguration;
@ContextConfiguration(classes = { IndexCoordinatesParameterELCIntegrationTests.Config.class })
public class IndexCoordinatesParameterELCIntegrationTests extends IndexCoordinatesParameterIntegrationTests {
@Configuration
@Import({ ElasticsearchTemplateConfiguration.class })
@EnableElasticsearchRepositories(considerNestedRepositories = true)
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("query-index-coordinates");
}
}
}
@@ -0,0 +1,98 @@
package org.springframework.data.elasticsearch.repository.query.indexcoordinates;
import org.jspecify.annotations.Nullable;
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.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
@SpringIntegrationTest
abstract class IndexCoordinatesParameterIntegrationTests {
@Autowired ElasticsearchOperations operations;
@Autowired IndexNameProvider indexNameProvider;
@Autowired RecordRepository recordRepository;
@BeforeEach
public void before() {
indexNameProvider.increment();
}
@Test
@Order(Integer.MAX_VALUE)
void cleanup() {
operations.indexOps(IndexCoordinates.of(indexNameProvider.getPrefix() + "*")).delete();
}
@Test // #2506
@DisplayName("should use indexcoordinates passes as repository query argument")
void shouldUseIndexCoordinatesPassesAsRepositoryQueryArgument() {
var record1 = new Record("1", "one");
var indexName1 = indexNameProvider.indexName();
var indexCoordinates1 = IndexCoordinates.of(indexName1);
operations.save(record1, indexCoordinates1);
var record2 = new Record("2", "two");
var indexName2 = indexName1 + "second";
var indexCoordinates2 = IndexCoordinates.of(indexName2);
operations.save(record2, indexCoordinates2);
// search for record1
var searchHits = recordRepository.findByText("one");
assert searchHits.getTotalHits() == 1;
searchHits = recordRepository.findByText("one", indexCoordinates2);
assert searchHits.getTotalHits() == 0;
// search for record2
searchHits = recordRepository.findByText("two");
assert searchHits.getTotalHits() == 0;
searchHits = recordRepository.findByText("two", indexCoordinates2);
assert searchHits.getTotalHits() == 1;
}
@Document(indexName = "#{@indexNameProvider.indexName()}")
static class Record {
@Nullable
@Id private String id;
@Nullable
@Field(type = FieldType.Keyword) private String text;
public Record(@Nullable String id, @Nullable String text) {
this.id = id;
this.text = text;
}
public @Nullable String getId() {
return id;
}
public void setId(@Nullable String id) {
this.id = id;
}
public @Nullable String getText() {
return text;
}
public void setText(@Nullable String text) {
this.text = text;
}
}
interface RecordRepository extends ElasticsearchRepository<Record, String> {
SearchHits<Record> findByText(String text);
SearchHits<Record> findByText(String text, IndexCoordinates index);
}
}
@@ -0,0 +1,24 @@
package org.springframework.data.elasticsearch.repository.query.indexcoordinates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchTemplateConfiguration;
import org.springframework.data.elasticsearch.repository.config.EnableReactiveElasticsearchRepositories;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.test.context.ContextConfiguration;
@ContextConfiguration(classes = { ReactiveIndexCoordinatesParameterELCIntegrationTests.Config.class })
public class ReactiveIndexCoordinatesParameterELCIntegrationTests
extends ReactiveIndexCoordinatesParameterIntegrationTests {
@Configuration
@Import({ ReactiveElasticsearchTemplateConfiguration.class })
@EnableReactiveElasticsearchRepositories(considerNestedRepositories = true)
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("reactive-query-index-coordinates");
}
}
}
@@ -0,0 +1,109 @@
package org.springframework.data.elasticsearch.repository.query.indexcoordinates;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;
import org.jspecify.annotations.Nullable;
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.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
import org.springframework.data.elasticsearch.repository.ReactiveElasticsearchRepository;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
@SpringIntegrationTest
abstract class ReactiveIndexCoordinatesParameterIntegrationTests {
@Autowired ReactiveElasticsearchOperations operations;
@Autowired IndexNameProvider indexNameProvider;
@Autowired RecordRepository recordRepository;
@BeforeEach
public void before() {
indexNameProvider.increment();
}
@Test
@Order(Integer.MAX_VALUE)
void cleanup() {
operations.indexOps(IndexCoordinates.of(indexNameProvider.getPrefix() + "*")).delete().block();
}
@Test // #2506
@DisplayName("should use indexcoordinates passes as repository query argument")
void shouldUseIndexCoordinatesPassesAsRepositoryQueryArgument() {
var record1 = new Record("1", "one");
var indexName1 = indexNameProvider.indexName();
var indexCoordinates1 = IndexCoordinates.of(indexName1);
operations.save(record1, indexCoordinates1).block();
var record2 = new Record("2", "two");
var indexName2 = indexName1 + "second";
var indexCoordinates2 = IndexCoordinates.of(indexName2);
operations.save(record2, indexCoordinates2).block();
// search for record1
recordRepository.findByText("one")
.as(StepVerifier::create)
.expectNextCount(1)
.verifyComplete();
recordRepository.findByText("one", indexCoordinates2)
.as(StepVerifier::create)
.expectNextCount(0)
.verifyComplete();
// search for record2
recordRepository.findByText("two")
.as(StepVerifier::create)
.expectNextCount(0)
.verifyComplete();
recordRepository.findByText("two", indexCoordinates2)
.as(StepVerifier::create)
.expectNextCount(1)
.verifyComplete();
}
@Document(indexName = "#{@indexNameProvider.indexName()}")
static class Record {
@Nullable
@Id private String id;
@Nullable
@Field(type = FieldType.Keyword) private String text;
public Record(@Nullable String id, @Nullable String text) {
this.id = id;
this.text = text;
}
public @Nullable String getId() {
return id;
}
public void setId(@Nullable String id) {
this.id = id;
}
public @Nullable String getText() {
return text;
}
public void setText(@Nullable String text) {
this.text = text;
}
}
interface RecordRepository extends ReactiveElasticsearchRepository<Record, String> {
Flux<SearchHit<Record>> findByText(String text);
Flux<SearchHit<Record>> findByText(String text, IndexCoordinates index);
}
}
@@ -15,7 +15,7 @@
#
#
sde.testcontainers.image-name=docker.elastic.co/elasticsearch/elasticsearch
sde.testcontainers.image-version=9.2.8
sde.testcontainers.image-version=9.2.5
#
#
# 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