1
0
mirror of synced 2026-05-23 12:43:17 +00:00

Compare commits

..

18 Commits

Author SHA1 Message Date
Christoph Strobl 53ac53d146 Release version 6.0.2 (2025.1.2).
See #3214
2026-01-16 10:36:18 +01:00
Christoph Strobl 6c656dff17 Prepare 6.0.2 (2025.1.2).
See #3214
2026-01-16 10:35:37 +01:00
Mark Paluch cfa303c6a3 Add Readme templates.
See spring-projects/spring-data-build#2758
2026-01-12 15:27:26 +01:00
Mark Paluch 353c463aa8 Extend license header copyright years to present.
See #3221
2026-01-05 08:45:42 +01:00
Peter-Josef Meisch cb67bfb534 Upgrade to Elasticsearch 9.2.3.
Closes #3217

Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com>
2025-12-24 16:21:03 +01:00
Mark Paluch 49cce254ce After release cleanups.
See #3197
2025-12-12 12:22:56 +01:00
Mark Paluch 597409c4c2 Prepare next development iteration.
See #3197
2025-12-12 12:22:55 +01:00
Mark Paluch 66144d10f8 Release version 6.0.1 (2025.1.1).
See #3197
2025-12-12 12:20:35 +01:00
Mark Paluch e6aefc8180 Prepare 6.0.1 (2025.1.1).
See #3197
2025-12-12 12:20:16 +01:00
Mark Paluch a7bd311106 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:48 +01:00
Mark Paluch 003d75f022 Add @ContextConfiguration(…) to test classes that use @SpringIntegrationTest on super classes.
Closes #3212
2025-12-11 09:02:48 +01:00
Mark Paluch 535b407085 Update CI Properties.
See #3197
2025-12-10 08:35:04 +01:00
Peter-Josef Meisch 44919d4cbe Upgrade Elasticsearch to 9.2.2.
Close #3208

Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com>
2025-12-05 00:01:14 +01:00
Peter-Josef Meisch 6260f278ba Fix UpdateQuery.Builder to allow only a scriptname to be set.
Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com>
(cherry picked from commit 69746441e1)
2025-12-04 23:25:34 +01:00
Peter-Josef Meisch b3bd77aa46 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>
(cherry picked from commit e31b66768b)
2025-11-25 20:11:04 +01:00
Peter-Josef Meisch bea651bc95 Fix documentation.
Closes #3199

Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com>
(cherry picked from commit 5821a81db9)
2025-11-14 19:02:47 +01:00
Mark Paluch 175614cd94 After release cleanups.
See #3186
2025-11-14 13:56:30 +01:00
Mark Paluch c4c73709c8 Prepare next development iteration.
See #3186
2025-11-14 13:56:29 +01:00
256 changed files with 1464 additions and 4743 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.
-27
View File
@@ -1,27 +0,0 @@
name: CI Build
on:
workflow_dispatch:
push:
branches: [ main, 'issue/**' ]
permissions: read-all
jobs:
build-java:
strategy:
matrix:
java-version: [ base, main ]
name: Build project
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Setup Java and Maven
uses: spring-projects/spring-data-build/actions/setup-maven@main
with:
java-version: ${{ matrix.java-version }}
develocity-access-key: '${{ secrets.DEVELOCITY_ACCESS_KEY }}'
- name: Build
uses: spring-projects/spring-data-build/actions/maven-build@main
env:
TESTCONTAINERS_REUSE_ENABLE: true
+45
View File
@@ -0,0 +1,45 @@
# 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: 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: 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: 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 }}
-28
View File
@@ -1,28 +0,0 @@
name: Snapshots
on:
workflow_dispatch:
push:
branches: [ main, 'issue/**' ]
permissions: read-all
jobs:
build-snapshots:
name: Build and deploy snapshots
if: ${{ github.repository_owner == 'spring-projects' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Setup Java and Maven
uses: spring-projects/spring-data-build/actions/setup-maven@main
with:
develocity-access-key: '${{ secrets.DEVELOCITY_ACCESS_KEY }}'
- name: Deploy to Artifactory
uses: spring-projects/spring-data-build/actions/maven-artifactory-deploy@main
env:
TESTCONTAINERS_REUSE_ENABLE: true
with:
build-name: 'spring-data-elasticsearch'
username: '${{ secrets.ARTIFACTORY_USERNAME }}'
password: '${{ secrets.ARTIFACTORY_PASSWORD }}'
+1 -1
View File
@@ -3,6 +3,6 @@
<extension>
<groupId>io.spring.develocity.conventions</groupId>
<artifactId>develocity-conventions-maven-extension</artifactId>
<version>0.0.25</version>
<version>0.0.22</version>
</extension>
</extensions>
+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/4.0.x", 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-jdk-noble
java.next.tag=25-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
+10 -41
View File
@@ -5,12 +5,12 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>6.1.0-SNAPSHOT</version>
<version>6.0.2</version>
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>4.1.0-SNAPSHOT</version>
<version>4.0.2</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.1.0-SNAPSHOT</springdata.commons>
<springdata.commons>4.0.2</springdata.commons>
<!-- version of the ElasticsearchClient -->
<elasticsearch-java>9.4.1</elasticsearch-java>
<elasticsearch-rest-client>9.4.1</elasticsearch-rest-client>
<elasticsearch-java>9.2.3</elasticsearch-java>
<elasticsearch-rest-client>9.2.3</elasticsearch-rest-client>
<hoverfly>0.20.2</hoverfly>
<log4j>2.25.4</log4j>
<log4j>2.25.1</log4j>
<jsonassert>1.5.3</jsonassert>
<wiremock>3.9.2</wiremock>
@@ -179,30 +179,17 @@
</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>
@@ -355,13 +342,7 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-observation-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</dependencies>
<build>
<resources>
@@ -499,20 +480,8 @@
</profiles>
<repositories>
<repository>
<id>spring-snapshot</id>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>
<repository>
<id>spring-milestone</id>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
</project>
+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,20 +1,12 @@
[[new-features]]
= What's new
[[new-features.6-1-0]]
== New in Spring Data Elasticsearch 6.1
* Upgrade to Elasticsearch 9.4.1
* Add support to use `IndexCoordinates` as repository query parameter
* Add support for includeNamedQueriesScore in Query
* Add support for Micrometer observation.
[[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.1
* Upgrade to Elasticsearch 9.2.2
* Use the new Elasticsearch Rest5Client as default
* Add support for SpEL expressions in the `settingPath` parameter of the `@Setting` annotation
@@ -6,10 +6,9 @@ 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
| 2026.0 | 6.1.x | 9.4.1 | 7.0.x
| 2025.1 | 6.0.x | 9.2.2 | 7.0.x
| 2025.0 | 5.5.xfootnote:oom[Out of maintenance] | 8.18.1 | 6.2.x
| 2024.1 | 5.4.xfootnote:oom[] | 8.15.5 | 6.1.x
| 2025.1 | 6.0.x | 9.2.3 | 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
| 2023.1 (Vaughan) | 5.2.xfootnote:oom[] | 8.11.1 | 6.1.x
| 2023.0 (Ullmann) | 5.1.xfootnote:oom[] | 8.7.1 | 6.0.x
@@ -1,13 +1,4 @@
[[elasticsearch.projections]]
= Projections
[[elasticsearch.projections.limitations]]
== Spring Data Elasticsearch Projection Limitations
This chapter is pulled in from the Spring Data Commons documentation, but does not apply to Spring Data Elasticsearch.
IMPORTANT: Interface-based projections are not supported in Spring Data Elasticsearch repository query methods.
To limit the fields returned from Elasticsearch, use the xref:elasticsearch/repositories/elasticsearch-repositories.adoc#elasticsearch.repositories.annotations.sourcefilters[`@SourceFilters`] annotation on your repository methods instead.
include::{commons}@data-commons::page$repositories/projections.adoc[leveloffset=+1]
@@ -15,11 +15,10 @@
*/
package org.springframework.data.elasticsearch;
import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.dao.DataRetrievalFailureException;
import java.util.Map;
/**
* @author Peter-Josef Meisch
* @author Illia Ulianov
@@ -43,6 +42,6 @@ public class BulkFailureException extends DataRetrievalFailureException {
* @author Illia Ulianov
* @since 5.2
*/
public record FailureDetails(Integer status, @Nullable String errorMessage) {
public record FailureDetails(Integer status, String errorMessage) {
}
}
@@ -28,7 +28,7 @@ import org.jspecify.annotations.Nullable;
public class ElasticsearchErrorCause {
@Nullable private final String type;
@Nullable private final String reason;
private final String reason;
@Nullable private final String stackTrace;
@@ -38,7 +38,7 @@ public class ElasticsearchErrorCause {
private final List<ElasticsearchErrorCause> suppressed;
public ElasticsearchErrorCause(@Nullable String type, @Nullable String reason, @Nullable String stackTrace,
public ElasticsearchErrorCause(@Nullable String type, String reason, @Nullable String stackTrace,
@Nullable ElasticsearchErrorCause causedBy, List<ElasticsearchErrorCause> rootCause,
List<ElasticsearchErrorCause> suppressed) {
this.type = type;
@@ -54,7 +54,7 @@ public class ElasticsearchErrorCause {
return type;
}
public @Nullable String getReason() {
public String getReason() {
return reason;
}
@@ -1,18 +1,2 @@
/*
* Copyright 2022-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.
*/
@org.jspecify.annotations.NullMarked
package org.springframework.data.elasticsearch.annotations;
@@ -1,18 +1,2 @@
/*
* Copyright 2022-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.
*/
@org.jspecify.annotations.NullMarked
package org.springframework.data.elasticsearch.aot;
@@ -1,70 +0,0 @@
/*
* Copyright 2026-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.client.elc;
import io.micrometer.common.KeyValues;
/**
* Default {@link ElasticsearchObservationConvention} implementation.
*
* @author maryantocinn
* @since 6.1
*/
public class DefaultElasticsearchObservationConvention implements ElasticsearchObservationConvention {
public static final DefaultElasticsearchObservationConvention INSTANCE = new DefaultElasticsearchObservationConvention();
@Override
public String getName() {
return ElasticsearchObservation.ELASTICSEARCH_COMMAND_OBSERVATION.getName();
}
@Override
public String getContextualName(ElasticsearchObservationContext context) {
String indexName = context.getIndexName();
if (indexName != null) {
return context.getOperationName().getValue() + " " + indexName;
}
return context.getOperationName().getValue();
}
@Override
public KeyValues getLowCardinalityKeyValues(ElasticsearchObservationContext context) {
KeyValues keyValues = KeyValues.of(
ElasticsearchObservation.LowCardinalityKeyNames.OPERATION.withValue(context.getOperationName().getValue()));
String indexName = context.getIndexName();
if (indexName != null) {
keyValues = keyValues.and(ElasticsearchObservation.LowCardinalityKeyNames.COLLECTION.withValue(indexName));
}
return keyValues;
}
@Override
public KeyValues getHighCardinalityKeyValues(ElasticsearchObservationContext context) {
Integer batchSize = context.getBatchSize();
if (batchSize != null) {
return KeyValues.of(
ElasticsearchObservation.HighCardinalityKeyNames.BATCH_SIZE.withValue(String.valueOf(batchSize)));
}
return KeyValues.empty();
}
}
@@ -24,14 +24,6 @@ import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.NestedIdentity;
import co.elastic.clients.json.JsonData;
import co.elastic.clients.json.JsonpMapper;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.Nullable;
@@ -44,6 +36,13 @@ import org.springframework.data.elasticsearch.core.document.SearchDocumentAdapte
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
import org.springframework.util.Assert;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Utility class to adapt different Elasticsearch responses to a
* {@link org.springframework.data.elasticsearch.core.document.Document}
@@ -55,187 +54,188 @@ import org.springframework.util.Assert;
*/
final class DocumentAdapters {
private static final Log LOGGER = LogFactory.getLog(DocumentAdapters.class);
private static final Log LOGGER = LogFactory.getLog(DocumentAdapters.class);
private DocumentAdapters() {}
private DocumentAdapters() {
}
/**
* Creates a {@link SearchDocument} from a {@link Hit} returned by the Elasticsearch client.
*
* @param hit the hit object
* @param jsonpMapper to map JsonData objects
* @return the created {@link SearchDocument}
*/
public static SearchDocument from(Hit<?> hit, JsonpMapper jsonpMapper) {
/**
* Creates a {@link SearchDocument} from a {@link Hit} returned by the Elasticsearch client.
*
* @param hit the hit object
* @param jsonpMapper to map JsonData objects
* @return the created {@link SearchDocument}
*/
public static SearchDocument from(Hit<?> hit, JsonpMapper jsonpMapper) {
Assert.notNull(hit, "hit must not be null");
Assert.notNull(hit, "hit must not be null");
Map<String, List<String>> highlightFields = hit.highlight();
Map<String, List<String>> highlightFields = hit.highlight();
Map<String, SearchDocumentResponse> innerHits = new LinkedHashMap<>();
hit.innerHits().forEach((name, innerHitsResult) -> {
// noinspection ReturnOfNull
innerHits.put(name, SearchDocumentResponseBuilder.from(innerHitsResult.hits(), null, null, null, 0, null, null,
searchDocument -> null, jsonpMapper));
});
Map<String, SearchDocumentResponse> innerHits = new LinkedHashMap<>();
hit.innerHits().forEach((name, innerHitsResult) -> {
// noinspection ReturnOfNull
innerHits.put(name, SearchDocumentResponseBuilder.from(innerHitsResult.hits(), null, null, null, 0, null, null,
searchDocument -> null, jsonpMapper));
});
NestedMetaData nestedMetaData = from(hit.nested());
NestedMetaData nestedMetaData = from(hit.nested());
Explanation explanation = from(hit.explanation());
Explanation explanation = from(hit.explanation());
Map<String, Double> matchedQueries = hit.matchedQueries();
Map<String, Double> matchedQueries = hit.matchedQueries();
Function<Map<String, JsonData>, EntityAsMap> fromFields = fields -> {
StringBuilder sb = new StringBuilder("{");
final boolean[] firstField = { true };
hit.fields().forEach((key, jsonData) -> {
if (!firstField[0]) {
sb.append(',');
}
sb.append('"').append(key).append("\":") //
.append(jsonData.toJson(jsonpMapper).toString());
firstField[0] = false;
});
sb.append('}');
return new EntityAsMap().fromJson(sb.toString());
};
Function<Map<String, JsonData>, EntityAsMap> fromFields = fields -> {
StringBuilder sb = new StringBuilder("{");
final boolean[] firstField = {true};
hit.fields().forEach((key, jsonData) -> {
if (!firstField[0]) {
sb.append(',');
}
sb.append('"').append(key).append("\":") //
.append(jsonData.toJson(jsonpMapper).toString());
firstField[0] = false;
});
sb.append('}');
return new EntityAsMap().fromJson(sb.toString());
};
EntityAsMap hitFieldsAsMap = fromFields.apply(hit.fields());
EntityAsMap hitFieldsAsMap = fromFields.apply(hit.fields());
Map<String, List<@Nullable Object>> documentFields = new LinkedHashMap<>();
hitFieldsAsMap.forEach((key, value) -> {
if (value instanceof List) {
// noinspection unchecked
documentFields.put(key, (List<Object>) value);
} else {
documentFields.put(key, Collections.singletonList(value));
}
});
Map<String, List<Object>> documentFields = new LinkedHashMap<>();
hitFieldsAsMap.forEach((key, value) -> {
if (value instanceof List) {
// noinspection unchecked
documentFields.put(key, (List<Object>) value);
} else {
documentFields.put(key, Collections.singletonList(value));
}
});
Document document;
Object source = hit.source();
if (source == null) {
document = Document.from(hitFieldsAsMap);
} else {
if (source instanceof EntityAsMap entityAsMap) {
document = Document.from(entityAsMap);
} else if (source instanceof JsonData jsonData) {
document = Document.from(jsonData.to(EntityAsMap.class));
} else {
Document document;
Object source = hit.source();
if (source == null) {
document = Document.from(hitFieldsAsMap);
} else {
if (source instanceof EntityAsMap entityAsMap) {
document = Document.from(entityAsMap);
} else if (source instanceof JsonData jsonData) {
document = Document.from(jsonData.to(EntityAsMap.class));
} else {
if (LOGGER.isWarnEnabled()) {
LOGGER.warn(String.format("Cannot map from type " + source.getClass().getName()));
}
document = Document.create();
}
}
document.setIndex(hit.index());
document.setId(hit.id());
if (LOGGER.isWarnEnabled()) {
LOGGER.warn(String.format("Cannot map from type " + source.getClass().getName()));
}
document = Document.create();
}
}
document.setIndex(hit.index());
document.setId(hit.id());
if (hit.version() != null) {
document.setVersion(hit.version());
}
document.setSeqNo(hit.seqNo() != null && hit.seqNo() >= 0 ? hit.seqNo() : -2); // -2 was the default value in the
// old client
document.setPrimaryTerm(hit.primaryTerm() != null && hit.primaryTerm() > 0 ? hit.primaryTerm() : 0);
if (hit.version() != null) {
document.setVersion(hit.version());
}
document.setSeqNo(hit.seqNo() != null && hit.seqNo() >= 0 ? hit.seqNo() : -2); // -2 was the default value in the
// old client
document.setPrimaryTerm(hit.primaryTerm() != null && hit.primaryTerm() > 0 ? hit.primaryTerm() : 0);
float score = hit.score() != null ? hit.score().floatValue() : Float.NaN;
return new SearchDocumentAdapter(document, score, hit.sort().stream().map(TypeUtils::toObject).toArray(),
documentFields, highlightFields, innerHits, nestedMetaData, explanation, matchedQueries, hit.routing());
}
float score = hit.score() != null ? hit.score().floatValue() : Float.NaN;
return new SearchDocumentAdapter(document, score, hit.sort().stream().map(TypeUtils::toObject).toArray(),
documentFields, highlightFields, innerHits, nestedMetaData, explanation, matchedQueries, hit.routing());
}
public static SearchDocument from(CompletionSuggestOption<EntityAsMap> completionSuggestOption) {
public static SearchDocument from(CompletionSuggestOption<EntityAsMap> completionSuggestOption) {
Document document = completionSuggestOption.source() != null ? Document.from(completionSuggestOption.source())
: Document.create();
document.setIndex(completionSuggestOption.index());
Document document = completionSuggestOption.source() != null ? Document.from(completionSuggestOption.source())
: Document.create();
document.setIndex(completionSuggestOption.index());
if (completionSuggestOption.id() != null) {
document.setId(completionSuggestOption.id());
}
if (completionSuggestOption.id() != null) {
document.setId(completionSuggestOption.id());
}
float score = completionSuggestOption.score() != null ? completionSuggestOption.score().floatValue() : Float.NaN;
return new SearchDocumentAdapter(document, score, new Object[] {}, Collections.emptyMap(), Collections.emptyMap(),
Collections.emptyMap(), null, null, null, completionSuggestOption.routing());
}
float score = completionSuggestOption.score() != null ? completionSuggestOption.score().floatValue() : Float.NaN;
return new SearchDocumentAdapter(document, score, new Object[]{}, Collections.emptyMap(), Collections.emptyMap(),
Collections.emptyMap(), null, null, null, completionSuggestOption.routing());
}
@Nullable
private static Explanation from(co.elastic.clients.elasticsearch.core.explain.@Nullable Explanation explanation) {
@Nullable
private static Explanation from(co.elastic.clients.elasticsearch.core.explain.@Nullable Explanation explanation) {
if (explanation == null) {
return null;
}
List<Explanation> details = explanation.details().stream().map(DocumentAdapters::from).collect(Collectors.toList());
return new Explanation(true, (double) explanation.value(), explanation.description(), details);
}
if (explanation == null) {
return null;
}
List<Explanation> details = explanation.details().stream().map(DocumentAdapters::from).collect(Collectors.toList());
return new Explanation(true, (double) explanation.value(), explanation.description(), details);
}
private static Explanation from(ExplanationDetail explanationDetail) {
private static Explanation from(ExplanationDetail explanationDetail) {
List<Explanation> details = explanationDetail.details().stream().map(DocumentAdapters::from)
.collect(Collectors.toList());
return new Explanation(null, (double) explanationDetail.value(), explanationDetail.description(), details);
}
List<Explanation> details = explanationDetail.details().stream().map(DocumentAdapters::from)
.collect(Collectors.toList());
return new Explanation(null, (double) explanationDetail.value(), explanationDetail.description(), details);
}
@Nullable
private static NestedMetaData from(@Nullable NestedIdentity nestedIdentity) {
@Nullable
private static NestedMetaData from(@Nullable NestedIdentity nestedIdentity) {
if (nestedIdentity == null) {
return null;
}
if (nestedIdentity == null) {
return null;
}
NestedMetaData child = from(nestedIdentity.nested());
return NestedMetaData.of(nestedIdentity.field(), nestedIdentity.offset(), child);
}
NestedMetaData child = from(nestedIdentity.nested());
return NestedMetaData.of(nestedIdentity.field(), nestedIdentity.offset(), child);
}
/**
* Creates a {@link Document} from a {@link GetResponse} where the found document is contained as {@link EntityAsMap}.
*
* @param getResponse the response instance
* @return the Document
*/
@Nullable
public static Document from(GetResult<EntityAsMap> getResponse) {
/**
* Creates a {@link Document} from a {@link GetResponse} where the found document is contained as {@link EntityAsMap}.
*
* @param getResponse the response instance
* @return the Document
*/
@Nullable
public static Document from(GetResult<EntityAsMap> getResponse) {
Assert.notNull(getResponse, "getResponse must not be null");
Assert.notNull(getResponse, "getResponse must not be null");
if (!getResponse.found()) {
return null;
}
if (!getResponse.found()) {
return null;
}
Document document = getResponse.source() != null ? Document.from(getResponse.source()) : Document.create();
document.setIndex(getResponse.index());
document.setId(getResponse.id());
Document document = getResponse.source() != null ? Document.from(getResponse.source()) : Document.create();
document.setIndex(getResponse.index());
document.setId(getResponse.id());
if (getResponse.version() != null) {
document.setVersion(getResponse.version());
}
if (getResponse.version() != null) {
document.setVersion(getResponse.version());
}
if (getResponse.seqNo() != null) {
document.setSeqNo(getResponse.seqNo());
}
if (getResponse.seqNo() != null) {
document.setSeqNo(getResponse.seqNo());
}
if (getResponse.primaryTerm() != null) {
document.setPrimaryTerm(getResponse.primaryTerm());
}
if (getResponse.primaryTerm() != null) {
document.setPrimaryTerm(getResponse.primaryTerm());
}
return document;
}
return document;
}
/**
* Creates a list of {@link MultiGetItem}s from a {@link MgetResponse} where the data is contained as
* {@link EntityAsMap} instances.
*
* @param mgetResponse the response instance
* @return list of multiget items
*/
public static List<MultiGetItem<Document>> from(MgetResponse<EntityAsMap> mgetResponse) {
/**
* Creates a list of {@link MultiGetItem}s from a {@link MgetResponse} where the data is contained as
* {@link EntityAsMap} instances.
*
* @param mgetResponse the response instance
* @return list of multiget items
*/
public static List<MultiGetItem<Document>> from(MgetResponse<EntityAsMap> mgetResponse) {
Assert.notNull(mgetResponse, "mgetResponse must not be null");
Assert.notNull(mgetResponse, "mgetResponse must not be null");
return mgetResponse.docs().stream() //
.map(itemResponse -> MultiGetItem.of( //
itemResponse.isFailure() ? null : from(itemResponse.result()), //
ResponseConverter.getFailure(itemResponse)))
.collect(Collectors.toList());
}
return mgetResponse.docs().stream() //
.map(itemResponse -> MultiGetItem.of( //
itemResponse.isFailure() ? null : from(itemResponse.result()), //
ResponseConverter.getFailure(itemResponse)))
.collect(Collectors.toList());
}
}
@@ -17,14 +17,12 @@ 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.Jackson3JsonpMapper;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
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;
@@ -34,6 +32,10 @@ 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
@@ -126,11 +128,10 @@ 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.
JsonMapper jsonMapper = JsonMapper.builder()
.enable(JsonNodeFeature.WRITE_NULL_PROPERTIES)
.enable(JsonNodeFeature.READ_NULL_PROPERTIES)
.build();
return new Jackson3JsonpMapper(jsonMapper);
var objectMapper = (new ObjectMapper())
.configure(SerializationFeature.INDENT_OUTPUT, false)
.setSerializationInclusion(JsonInclude.Include.ALWAYS);
return new JacksonJsonpMapper(objectMapper);
}
/**
@@ -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 {
/**
@@ -1,99 +0,0 @@
/*
* Copyright 2026-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.client.elc;
import io.micrometer.common.docs.KeyName;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationConvention;
import io.micrometer.observation.docs.ObservationDocumentation;
/**
* {@link ObservationDocumentation} for Spring Data Elasticsearch template operations.
*
* @author maryantocinn
* @since 6.1
*/
public enum ElasticsearchObservation implements ObservationDocumentation {
/**
* Timer created around a Spring Data Elasticsearch template operation.
*/
ELASTICSEARCH_COMMAND_OBSERVATION {
@Override
public String getName() {
return "spring.data.elasticsearch.command";
}
@Override
public Class<? extends ObservationConvention<? extends Observation.Context>> getDefaultConvention() {
return DefaultElasticsearchObservationConvention.class;
}
@Override
public KeyName[] getLowCardinalityKeyNames() {
return LowCardinalityKeyNames.values();
}
@Override
public KeyName[] getHighCardinalityKeyNames() {
return HighCardinalityKeyNames.values();
}
};
/**
* Low cardinality key names for Spring Data Elasticsearch observations. These become metric dimensions and MUST be
* present on every observation to satisfy backends like Prometheus that require consistent tag key sets.
*/
enum LowCardinalityKeyNames implements KeyName {
/**
* The Spring Data operation being performed (e.g., save, search, delete, bulk).
*/
OPERATION {
@Override
public String asString() {
return "spring.data.operation";
}
},
/**
* The target collection (index) name. Only present when the operation targets a specific index.
*/
COLLECTION {
@Override
public String asString() {
return "spring.data.collection";
}
}
}
/**
* High cardinality key names for Spring Data Elasticsearch observations. These appear only on traces/spans, not on
* metrics, because their values are unbounded or optional per operation.
*/
enum HighCardinalityKeyNames implements KeyName {
/**
* The number of operations included in a batch (bulk) request. Only present for bulk operations.
*/
BATCH_SIZE {
@Override
public String asString() {
return "spring.data.batch.size";
}
}
}
}
@@ -1,81 +0,0 @@
/*
* Copyright 2026-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.client.elc;
import io.micrometer.observation.Observation;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
/**
* {@link Observation.Context} for Spring Data Elasticsearch operations. One instance is created per observed operation.
* It carries contextual data that conventions use to produce observation names and key-values.
*
* @author maryantocinn
* @since 6.1
*/
public class ElasticsearchObservationContext extends Observation.Context {
private final ElasticsearchOperationName operationName;
@Nullable private final IndexCoordinates indexCoordinates;
@Nullable private Integer batchSize;
public ElasticsearchObservationContext(ElasticsearchOperationName operationName,
@Nullable IndexCoordinates indexCoordinates) {
this.operationName = operationName;
this.indexCoordinates = indexCoordinates;
}
/**
* @return the Spring Data operation being performed.
*/
public ElasticsearchOperationName getOperationName() {
return operationName;
}
/**
* @return the target index coordinates, or {@literal null} if the operation is not index-specific.
*/
@Nullable
public IndexCoordinates getIndexCoordinates() {
return indexCoordinates;
}
/**
* @return the comma-joined index name(s), or {@literal null} if no index coordinates are set.
*/
@Nullable
public String getIndexName() {
return indexCoordinates != null ? String.join(",", indexCoordinates.getIndexNames()) : null;
}
/**
* @return the batch size, or {@literal null} if not a batch operation.
*/
@Nullable
public Integer getBatchSize() {
return batchSize;
}
/**
* Set the number of operations included in a batch (bulk) request.
*
* @param batchSize the batch size, can be {@literal null}
*/
public void setBatchSize(@Nullable Integer batchSize) {
this.batchSize = batchSize;
}
}
@@ -1,34 +0,0 @@
/*
* Copyright 2026-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.client.elc;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationConvention;
/**
* {@link ObservationConvention} for Spring Data Elasticsearch operations. Implement this interface and register it as a
* bean to customize observation names and key-values.
*
* @author maryantocinn
* @since 6.1
*/
public interface ElasticsearchObservationConvention extends ObservationConvention<ElasticsearchObservationContext> {
@Override
default boolean supportsContext(Observation.Context context) {
return context instanceof ElasticsearchObservationContext;
}
}
@@ -1,56 +0,0 @@
/*
* Copyright 2026-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.client.elc;
/**
* Enumeration of Spring Data Elasticsearch operation names used in observations.
*
* @author maryantocinn
* @since 6.1
*/
public enum ElasticsearchOperationName {
SAVE("save"), //
INDEX("index"), //
GET("get"), //
MULTI_GET("multiGet"), //
EXISTS("exists"), //
DELETE("delete"), //
DELETE_BY_QUERY("deleteByQuery"), //
BULK("bulk"), //
UPDATE("update"), //
UPDATE_BY_QUERY("updateByQuery"), //
COUNT("count"), //
SEARCH("search");
private final String value;
ElasticsearchOperationName(String value) {
this.value = value;
}
/**
* @return the operation name as a string value used in observation key values.
*/
public String getValue() {
return value;
}
@Override
public String toString() {
return value;
}
}
@@ -17,11 +17,30 @@ package org.springframework.data.elasticsearch.client.elc;
import static org.springframework.data.elasticsearch.client.elc.TypeUtils.*;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.Time;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.core.bulk.BulkResponseItem;
import co.elastic.clients.elasticsearch.core.msearch.MultiSearchResponseItem;
import co.elastic.clients.elasticsearch.core.search.ResponseBody;
import co.elastic.clients.elasticsearch.sql.ElasticsearchSqlClient;
import co.elastic.clients.elasticsearch.sql.QueryResponse;
import co.elastic.clients.json.JsonpMapper;
import co.elastic.clients.transport.Version;
import java.io.IOException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.data.elasticsearch.BulkFailureException;
import org.springframework.data.elasticsearch.client.UnsupportedBackendOperation;
import org.springframework.data.elasticsearch.core.AbstractElasticsearchTemplate;
@@ -43,30 +62,6 @@ import org.springframework.data.elasticsearch.core.script.Script;
import org.springframework.data.elasticsearch.core.sql.SqlResponse;
import org.springframework.util.Assert;
import java.io.IOException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.Time;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.core.bulk.BulkResponseItem;
import co.elastic.clients.elasticsearch.core.msearch.MultiSearchResponseItem;
import co.elastic.clients.elasticsearch.core.search.ResponseBody;
import co.elastic.clients.elasticsearch.sql.ElasticsearchSqlClient;
import co.elastic.clients.elasticsearch.sql.QueryResponse;
import co.elastic.clients.json.JsonpMapper;
import co.elastic.clients.transport.Version;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry;
/**
* Implementation of {@link org.springframework.data.elasticsearch.core.ElasticsearchOperations} using the new
* Elasticsearch client.
@@ -75,15 +70,12 @@ import io.micrometer.observation.ObservationRegistry;
* @author Hamid Rahimi
* @author Illia Ulianov
* @author Haibo Liu
* @author maryantocinn
* @since 4.4
*/
public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
private static final Log LOGGER = LogFactory.getLog(ElasticsearchTemplate.class);
@Nullable private ElasticsearchObservationConvention observationConvention;
private final ElasticsearchClient client;
private final ElasticsearchSqlClient sqlClient;
private final RequestConverter requestConverter;
@@ -121,61 +113,6 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
protected AbstractElasticsearchTemplate doCopy() {
return new ElasticsearchTemplate(client, elasticsearchConverter);
}
@Override
protected void customizeCopy(AbstractElasticsearchTemplate copy) {
if (copy instanceof ElasticsearchTemplate elasticsearchTemplate) {
elasticsearchTemplate.observationConvention = this.observationConvention;
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
super.setApplicationContext(applicationContext);
if (observationRegistry == ObservationRegistry.NOOP) {
applicationContext.getBeanProvider(ObservationRegistry.class).ifAvailable(this::setObservationRegistry);
}
if (observationConvention == null) {
applicationContext.getBeanProvider(ElasticsearchObservationConvention.class)
.ifAvailable(this::setObservationConvention);
}
}
/**
* Set a custom {@link ElasticsearchObservationConvention} to override the default convention.
*
* @param observationConvention can be {@literal null}.
* @since 6.1
*/
public void setObservationConvention(@Nullable ElasticsearchObservationConvention observationConvention) {
this.observationConvention = observationConvention;
}
private <T> T observe(ElasticsearchOperationName operationName, @Nullable IndexCoordinates index,
Supplier<T> action) {
Observation observation = createObservation(operationName, index, null);
return observation.observe(action);
}
private <T> T observe(ElasticsearchOperationName operationName, @Nullable IndexCoordinates index, int batchSize,
Supplier<T> action) {
Observation observation = createObservation(operationName, index, batchSize);
return observation.observe(action);
}
private Observation createObservation(ElasticsearchOperationName operationName, @Nullable IndexCoordinates index,
@Nullable Integer batchSize) {
ElasticsearchObservationContext context = new ElasticsearchObservationContext(operationName, index);
context.setBatchSize(batchSize);
return ElasticsearchObservation.ELASTICSEARCH_COMMAND_OBSERVATION.observation(observationConvention,
DefaultElasticsearchObservationConvention.INSTANCE, () -> context, observationRegistry);
}
// endregion
// region child templates
@@ -200,45 +137,16 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
// endregion
// region document operations
@Override
public <T> T save(T entity, IndexCoordinates index) {
return observe(ElasticsearchOperationName.SAVE, index, () -> super.save(entity, index));
}
@Override
public String index(IndexQuery query, IndexCoordinates index) {
return observe(ElasticsearchOperationName.INDEX, index, () -> super.index(query, index));
}
@Override
public boolean exists(String id, IndexCoordinates index) {
return observe(ElasticsearchOperationName.EXISTS, index, () -> super.exists(id, index));
}
@Override
public String delete(String id, IndexCoordinates index) {
return observe(ElasticsearchOperationName.DELETE, index, () -> super.delete(id, index));
}
@Override
public List<IndexedObjectInformation> bulkOperation(List<?> queries, BulkOptions bulkOptions,
IndexCoordinates index) {
return observe(ElasticsearchOperationName.BULK, index, queries.size(),
() -> super.bulkOperation(queries, bulkOptions, index));
}
@Override
@Nullable
public <T> T get(String id, Class<T> clazz, IndexCoordinates index) {
return observe(ElasticsearchOperationName.GET, index, () -> {
GetRequest getRequest = requestConverter.documentGetRequest(elasticsearchConverter.convertId(id),
routingResolver.getRouting(), index);
GetResponse<EntityAsMap> getResponse = execute(client -> client.get(getRequest, EntityAsMap.class));
GetRequest getRequest = requestConverter.documentGetRequest(elasticsearchConverter.convertId(id),
routingResolver.getRouting(), index);
GetResponse<EntityAsMap> getResponse = execute(client -> client.get(getRequest, EntityAsMap.class));
ReadDocumentCallback<T> callback = new ReadDocumentCallback<>(elasticsearchConverter, clazz, index);
return callback.doWith(DocumentAdapters.from(getResponse));
});
ReadDocumentCallback<T> callback = new ReadDocumentCallback<>(elasticsearchConverter, clazz, index);
return callback.doWith(DocumentAdapters.from(getResponse));
}
@Override
@@ -247,17 +155,15 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
Assert.notNull(query, "query must not be null");
Assert.notNull(clazz, "clazz must not be null");
return observe(ElasticsearchOperationName.MULTI_GET, index, () -> {
MgetRequest request = requestConverter.documentMgetRequest(query, clazz, index);
MgetResponse<EntityAsMap> result = execute(client -> client.mget(request, EntityAsMap.class));
MgetRequest request = requestConverter.documentMgetRequest(query, clazz, index);
MgetResponse<EntityAsMap> result = execute(client -> client.mget(request, EntityAsMap.class));
ReadDocumentCallback<T> callback = new ReadDocumentCallback<>(elasticsearchConverter, clazz, index);
ReadDocumentCallback<T> callback = new ReadDocumentCallback<>(elasticsearchConverter, clazz, index);
return DocumentAdapters.from(result).stream() //
.map(multiGetItem -> MultiGetItem.of( //
multiGetItem.isFailed() ? null : callback.doWith(multiGetItem.getItem()), multiGetItem.getFailure())) //
.collect(Collectors.toList());
});
return DocumentAdapters.from(result).stream() //
.map(multiGetItem -> MultiGetItem.of( //
multiGetItem.isFailed() ? null : callback.doWith(multiGetItem.getItem()), multiGetItem.getFailure())) //
.collect(Collectors.toList());
}
@Override
@@ -279,26 +185,22 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
public ByQueryResponse delete(DeleteQuery query, Class<?> clazz, IndexCoordinates index) {
Assert.notNull(query, "query must not be null");
return observe(ElasticsearchOperationName.DELETE_BY_QUERY, index, () -> {
DeleteByQueryRequest request = requestConverter.documentDeleteByQueryRequest(query, routingResolver.getRouting(),
clazz, index, getRefreshPolicy());
DeleteByQueryRequest request = requestConverter.documentDeleteByQueryRequest(query, routingResolver.getRouting(),
clazz, index, getRefreshPolicy());
DeleteByQueryResponse response = execute(client -> client.deleteByQuery(request));
DeleteByQueryResponse response = execute(client -> client.deleteByQuery(request));
return responseConverter.byQueryResponse(response);
});
return responseConverter.byQueryResponse(response);
}
@Override
public UpdateResponse update(UpdateQuery updateQuery, IndexCoordinates index) {
return observe(ElasticsearchOperationName.UPDATE, index, () -> {
UpdateRequest<Document, ?> request = requestConverter.documentUpdateRequest(updateQuery, index,
getRefreshPolicy(), routingResolver.getRouting());
co.elastic.clients.elasticsearch.core.UpdateResponse<Document> response = execute(
client -> client.update(request, Document.class));
return UpdateResponse.of(result(response.result()));
});
UpdateRequest<Document, ?> request = requestConverter.documentUpdateRequest(updateQuery, index, getRefreshPolicy(),
routingResolver.getRouting());
co.elastic.clients.elasticsearch.core.UpdateResponse<Document> response = execute(
client -> client.update(request, Document.class));
return UpdateResponse.of(result(response.result()));
}
@Override
@@ -307,13 +209,11 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
Assert.notNull(updateQuery, "updateQuery must not be null");
Assert.notNull(index, "index must not be null");
return observe(ElasticsearchOperationName.UPDATE_BY_QUERY, index, () -> {
UpdateByQueryRequest request = requestConverter.documentUpdateByQueryRequest(updateQuery, index,
getRefreshPolicy());
UpdateByQueryRequest request = requestConverter.documentUpdateByQueryRequest(updateQuery, index,
getRefreshPolicy());
UpdateByQueryResponse byQueryResponse = execute(client -> client.updateByQuery(request));
return responseConverter.byQueryResponse(byQueryResponse);
});
UpdateByQueryResponse byQueryResponse = execute(client -> client.updateByQuery(request));
return responseConverter.byQueryResponse(byQueryResponse);
}
@Override
@@ -428,14 +328,12 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
Assert.notNull(query, "query must not be null");
Assert.notNull(index, "index must not be null");
return observe(ElasticsearchOperationName.COUNT, index, () -> {
SearchRequest searchRequest = requestConverter.searchRequest(query, routingResolver.getRouting(), clazz, index,
true);
SearchRequest searchRequest = requestConverter.searchRequest(query, routingResolver.getRouting(), clazz, index,
true);
SearchResponse<EntityAsMap> searchResponse = execute(client -> client.search(searchRequest, EntityAsMap.class));
SearchResponse<EntityAsMap> searchResponse = execute(client -> client.search(searchRequest, EntityAsMap.class));
return searchResponse.hits().total().value();
});
return searchResponse.hits().total().value();
}
@Override
@@ -445,13 +343,11 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
Assert.notNull(clazz, "clazz must not be null");
Assert.notNull(index, "index must not be null");
return observe(ElasticsearchOperationName.SEARCH, index, () -> {
if (query instanceof SearchTemplateQuery searchTemplateQuery) {
return doSearch(searchTemplateQuery, clazz, index);
} else {
return doSearch(query, clazz, index);
}
});
if (query instanceof SearchTemplateQuery searchTemplateQuery) {
return doSearch(searchTemplateQuery, clazz, index);
} else {
return doSearch(query, clazz, index);
}
}
protected <T> SearchHits<T> doSearch(Query query, Class<T> clazz, IndexCoordinates index) {
@@ -114,7 +114,7 @@ public class IndicesTemplate extends ChildTemplate<ElasticsearchTransport, Elast
}
@Override
public boolean create(Map<String, @Nullable Object> settings) {
public boolean create(Map<String, Object> settings) {
Assert.notNull(settings, "settings must not be null");
@@ -122,7 +122,7 @@ public class IndicesTemplate extends ChildTemplate<ElasticsearchTransport, Elast
}
@Override
public boolean create(Map<String, @Nullable Object> settings, Document mapping) {
public boolean create(Map<String, Object> settings, Document mapping) {
Assert.notNull(settings, "settings must not be null");
Assert.notNull(mapping, "mapping must not be null");
@@ -135,7 +135,7 @@ public class IndicesTemplate extends ChildTemplate<ElasticsearchTransport, Elast
return doCreate(getIndexCoordinates(), createSettings(), createMapping());
}
protected boolean doCreate(IndexCoordinates indexCoordinates, Map<String, @Nullable Object> settings,
protected boolean doCreate(IndexCoordinates indexCoordinates, Map<String, Object> settings,
@Nullable Document mapping) {
Set<Alias> aliases = (boundClass != null) ? getAliasesFor(boundClass) : new HashSet<>();
CreateIndexSettings indexSettings = CreateIndexSettings.builder(indexCoordinates)
@@ -233,7 +233,7 @@ public class IndicesTemplate extends ChildTemplate<ElasticsearchTransport, Elast
}
@Override
public Map<String, @Nullable Object> getMapping() {
public Map<String, Object> getMapping() {
IndexCoordinates indexCoordinates = getIndexCoordinates();
GetMappingRequest getMappingRequest = requestConverter.indicesGetMappingRequest(indexCoordinates);
@@ -16,14 +16,12 @@
package org.springframework.data.elasticsearch.client.elc;
import co.elastic.clients.json.JsonpMapper;
import co.elastic.clients.json.jackson.Jackson3JsonpMapper;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
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;
@@ -34,6 +32,10 @@ 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
@@ -125,11 +127,10 @@ 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.
JsonMapper jsonMapper = JsonMapper.builder()
.enable(JsonNodeFeature.WRITE_NULL_PROPERTIES)
.enable(JsonNodeFeature.READ_NULL_PROPERTIES)
.build();
return new Jackson3JsonpMapper(jsonMapper);
var objectMapper = (new ObjectMapper())
.configure(SerializationFeature.INDENT_OUTPUT, false)
.setSerializationInclusion(JsonInclude.Include.ALWAYS);
return new JacksonJsonpMapper(objectMapper);
}
/**
@@ -25,8 +25,7 @@ import co.elastic.clients.elasticsearch.core.search.ResponseBody;
import co.elastic.clients.json.JsonpMapper;
import co.elastic.clients.transport.Version;
import co.elastic.clients.transport.endpoints.BooleanResponse;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry;
import org.jspecify.annotations.NonNull;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.function.Tuple2;
@@ -44,8 +43,6 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.Nullable;
import org.reactivestreams.Publisher;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.BulkFailureException;
import org.springframework.data.elasticsearch.NoSuchIndexException;
@@ -56,7 +53,6 @@ import org.springframework.data.elasticsearch.core.AggregationContainer;
import org.springframework.data.elasticsearch.core.IndexedObjectInformation;
import org.springframework.data.elasticsearch.core.MultiGetItem;
import org.springframework.data.elasticsearch.core.ReactiveIndexOperations;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.cluster.ReactiveClusterOperations;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.Document;
@@ -80,15 +76,12 @@ import org.springframework.util.StringUtils;
* @author Peter-Josef Meisch
* @author Illia Ulianov
* @author Junghoon Ban
* @author maryantocinn
* @since 4.4
*/
public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearchTemplate {
private static final Log LOGGER = LogFactory.getLog(ReactiveElasticsearchTemplate.class);
@Nullable private ElasticsearchObservationConvention observationConvention;
private final ReactiveElasticsearchClient client;
private final ReactiveElasticsearchSqlClient sqlClient;
private final RequestConverter requestConverter;
@@ -109,105 +102,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
exceptionTranslator = new ElasticsearchExceptionTranslator(jsonpMapper);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
super.setApplicationContext(applicationContext);
if (observationRegistry == ObservationRegistry.NOOP) {
applicationContext.getBeanProvider(ObservationRegistry.class).ifAvailable(this::setObservationRegistry);
}
if (observationConvention == null) {
applicationContext.getBeanProvider(ElasticsearchObservationConvention.class)
.ifAvailable(this::setObservationConvention);
}
}
/**
* Set a custom {@link ElasticsearchObservationConvention} to override the default convention.
*
* @param observationConvention can be {@literal null}.
* @since 6.1
*/
public void setObservationConvention(@Nullable ElasticsearchObservationConvention observationConvention) {
this.observationConvention = observationConvention;
}
private <T> Mono<T> observeMono(ElasticsearchOperationName operationName, @Nullable IndexCoordinates index,
Mono<T> mono) {
return Mono.defer(() -> {
Observation observation = createObservation(operationName, index, null);
return mono.doOnError(observation::error) //
.doFinally(signalType -> observation.stop())
.contextWrite(context -> context.put(Observation.class, observation))
.doOnSubscribe(subscription -> observation.start());
});
}
private <T> Flux<T> observeFlux(ElasticsearchOperationName operationName, @Nullable IndexCoordinates index,
Flux<T> flux) {
return Flux.defer(() -> {
Observation observation = createObservation(operationName, index, null);
return flux.doOnError(observation::error) //
.doFinally(signalType -> observation.stop())
.contextWrite(context -> context.put(Observation.class, observation))
.doOnSubscribe(subscription -> observation.start());
});
}
private <T> Mono<T> observeMono(ElasticsearchOperationName operationName, @Nullable IndexCoordinates index,
int batchSize, Mono<T> mono) {
return Mono.defer(() -> {
Observation observation = createObservation(operationName, index, batchSize);
return mono.doOnError(observation::error) //
.doFinally(signalType -> observation.stop())
.contextWrite(context -> context.put(Observation.class, observation))
.doOnSubscribe(subscription -> observation.start());
});
}
private Observation createObservation(ElasticsearchOperationName operationName, @Nullable IndexCoordinates index,
@Nullable Integer batchSize) {
ElasticsearchObservationContext context = new ElasticsearchObservationContext(operationName, index);
context.setBatchSize(batchSize);
return ElasticsearchObservation.ELASTICSEARCH_COMMAND_OBSERVATION.observation(observationConvention,
DefaultElasticsearchObservationConvention.INSTANCE, () -> context, observationRegistry);
}
// region Document operations
@Override
public <T> Mono<T> save(T entity, IndexCoordinates index) {
return observeMono(ElasticsearchOperationName.SAVE, index, super.save(entity, index));
}
@Override
public Mono<Boolean> exists(String id, IndexCoordinates index) {
return observeMono(ElasticsearchOperationName.EXISTS, index, super.exists(id, index));
}
@Override
public Mono<String> delete(Object entity, IndexCoordinates index) {
return observeMono(ElasticsearchOperationName.DELETE, index, super.delete(entity, index));
}
@Override
public Mono<String> delete(String id, IndexCoordinates index) {
return observeMono(ElasticsearchOperationName.DELETE, index, super.delete(id, index));
}
@Override
public <T> Flux<SearchHit<T>> search(Query query, Class<?> entityType, Class<T> resultType, IndexCoordinates index) {
return observeFlux(ElasticsearchOperationName.SEARCH, index, super.search(query, entityType, resultType, index));
}
@Override
public Mono<Long> count(Query query, Class<?> entityType, IndexCoordinates index) {
return observeMono(ElasticsearchOperationName.COUNT, index, super.count(query, entityType, index));
}
@Override
protected <T> Mono<Tuple2<T, IndexResponseMetaData>> doIndex(T entity, IndexCoordinates index) {
@@ -229,7 +124,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
Assert.notNull(entitiesPublisher, "entitiesPublisher must not be null!");
return observeFlux(ElasticsearchOperationName.BULK, index, entitiesPublisher //
return entitiesPublisher //
.flatMapMany(entities -> Flux.fromIterable(entities) //
.concatMap(entity -> maybeCallbackBeforeConvert(entity, index)) //
).collectList() //
@@ -251,12 +146,12 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
response.index(), //
response.seqNo(), //
response.primaryTerm(), //
response.version()), //
converter, //
response.version()),
converter,
routingResolver);
return maybeCallbackAfterSave(updatedEntity, index);
});
}));
});
}
@Override
@@ -268,7 +163,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
ExistsRequest existsRequest = requestConverter.documentExistsRequest(id, routingResolver.getRouting(), index);
return Mono.from(execute(
((ClientCallback<Publisher<BooleanResponse>>) client -> client.exists(existsRequest))))
((ClientCallback<@NonNull Publisher<BooleanResponse>>) client -> client.exists(existsRequest))))
.map(BooleanResponse::value) //
.onErrorReturn(NoSuchIndexException.class, false);
}
@@ -277,11 +172,9 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
public Mono<ByQueryResponse> delete(DeleteQuery query, Class<?> entityType, IndexCoordinates index) {
Assert.notNull(query, "query must not be null");
return observeMono(ElasticsearchOperationName.DELETE_BY_QUERY, index, Mono.defer(() -> {
DeleteByQueryRequest request = requestConverter.documentDeleteByQueryRequest(query, routingResolver.getRouting(),
entityType, index, getRefreshPolicy());
return Mono.from(execute(client -> client.deleteByQuery(request))).map(responseConverter::byQueryResponse);
}));
DeleteByQueryRequest request = requestConverter.documentDeleteByQueryRequest(query, routingResolver.getRouting(),
entityType, index, getRefreshPolicy());
return Mono.from(execute(client -> client.deleteByQuery(request))).map(responseConverter::byQueryResponse);
}
@Override
@@ -291,15 +184,13 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
Assert.notNull(entityType, "entityType must not be null");
Assert.notNull(index, "index must not be null");
return observeMono(ElasticsearchOperationName.GET, index, Mono.defer(() -> {
GetRequest getRequest = requestConverter.documentGetRequest(id, routingResolver.getRouting(), index);
GetRequest getRequest = requestConverter.documentGetRequest(id, routingResolver.getRouting(), index);
Mono<GetResponse<EntityAsMap>> getResponse = Mono //
.from(execute(client -> client.get(getRequest, EntityAsMap.class)));
Mono<GetResponse<EntityAsMap>> getResponse = Mono
.from(execute(client -> client.get(getRequest, EntityAsMap.class)));
ReadDocumentCallback<T> callback = new ReadDocumentCallback<>(converter, entityType, index);
return getResponse.flatMap(response -> callback.toEntity(DocumentAdapters.from(response)));
}));
ReadDocumentCallback<T> callback = new ReadDocumentCallback<>(converter, entityType, index);
return getResponse.flatMap(response -> callback.toEntity(DocumentAdapters.from(response)));
}
@Override
@@ -336,15 +227,13 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
Assert.notNull(updateQuery, "UpdateQuery must not be null");
Assert.notNull(index, "Index must not be null");
return observeMono(ElasticsearchOperationName.UPDATE, index, Mono.defer(() -> {
UpdateRequest<Document, ?> request = requestConverter.documentUpdateRequest(updateQuery, index,
getRefreshPolicy(), routingResolver.getRouting());
UpdateRequest<Document, ?> request = requestConverter.documentUpdateRequest(updateQuery, index, getRefreshPolicy(),
routingResolver.getRouting());
return Mono.from(execute(client -> client.update(request, Document.class))).flatMap(response -> {
UpdateResponse.Result result = result(response.result());
return result == null ? Mono.empty() : Mono.just(UpdateResponse.of(result));
});
}));
return Mono.from(execute(client -> client.update(request, Document.class))).flatMap(response -> {
UpdateResponse.Result result = result(response.result());
return result == null ? Mono.empty() : Mono.just(UpdateResponse.of(result));
});
}
@Override
@@ -359,8 +248,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
Assert.notNull(bulkOptions, "BulkOptions must not be null");
Assert.notNull(index, "Index must not be null");
return observeMono(ElasticsearchOperationName.BULK, index, queries.size(),
doBulkOperation(queries, bulkOptions, index).then());
return doBulkOperation(queries, bulkOptions, index).then();
}
private Flux<BulkResponseItem> doBulkOperation(List<?> queries, BulkOptions bulkOptions, IndexCoordinates index) {
@@ -380,7 +268,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
for (BulkResponseItem item : bulkResponse.items()) {
if (item.error() != null && item.id() != null) {
if (item.error() != null) {
failedDocuments.put(item.id(), new BulkFailureException.FailureDetails(item.status(), item.error().reason()));
}
}
@@ -423,24 +311,22 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
Assert.notNull(query, "query must not be null");
Assert.notNull(clazz, "clazz must not be null");
return observeFlux(ElasticsearchOperationName.MULTI_GET, index, Flux.defer(() -> {
MgetRequest request = requestConverter.documentMgetRequest(query, clazz, index);
MgetRequest request = requestConverter.documentMgetRequest(query, clazz, index);
ReadDocumentCallback<T> callback = new ReadDocumentCallback<>(converter, clazz, index);
ReadDocumentCallback<T> callback = new ReadDocumentCallback<>(converter, clazz, index);
Publisher<MgetResponse<EntityAsMap>> response = execute(client -> client.mget(request, EntityAsMap.class));
Publisher<MgetResponse<EntityAsMap>> response = execute(client -> client.mget(request, EntityAsMap.class));
return Mono.from(response)//
.flatMapMany(it -> Flux.fromIterable(DocumentAdapters.from(it))) //
.flatMap(multiGetItem -> {
if (multiGetItem.isFailed()) {
return Mono.just(MultiGetItem.of(null, multiGetItem.getFailure()));
} else {
return callback.toEntity(multiGetItem.getItem()) //
.map(t -> MultiGetItem.of(t, multiGetItem.getFailure()));
}
});
}));
return Mono.from(response)//
.flatMapMany(it -> Flux.fromIterable(DocumentAdapters.from(it))) //
.flatMap(multiGetItem -> {
if (multiGetItem.isFailed()) {
return Mono.just(MultiGetItem.of(null, multiGetItem.getFailure()));
} else {
return callback.toEntity(multiGetItem.getItem()) //
.map(t -> MultiGetItem.of(t, multiGetItem.getFailure()));
}
});
}
// endregion
@@ -450,14 +336,6 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
return new ReactiveElasticsearchTemplate(client, converter);
}
@Override
protected void customizeCopy(AbstractReactiveElasticsearchTemplate copy) {
if (copy instanceof ReactiveElasticsearchTemplate reactiveTemplate) {
reactiveTemplate.observationConvention = this.observationConvention;
}
}
// region search operations
@Override
@@ -537,7 +415,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
return Mono.empty();
}
List<Object> sortOptions = hits.get(hits.size() - 1).sort().stream().map(TypeUtils::toObjectNotNull)
List<Object> sortOptions = hits.get(hits.size() - 1).sort().stream().map(TypeUtils::toObject)
.collect(Collectors.toList());
baseQuery.setSearchAfter(sortOptions);
SearchRequest followSearchRequest = requestConverter.searchRequest(baseQuery,
@@ -631,8 +509,8 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
SearchRequest searchRequest = requestConverter.searchRequest(query, routingResolver.getRouting(), clazz, index,
false);
// noinspection unchecked
SearchDocumentCallback<T> callback = new ReadSearchDocumentCallback<>((Class<T>) clazz, index);
SearchDocumentResponse.EntityCreator<T> entityCreator = searchDocument -> callback.toEntity(searchDocument)
.toFuture();
@@ -108,7 +108,7 @@ public class ReactiveIndicesTemplate
}
@Override
public Mono<Boolean> create(Map<String, @Nullable Object> settings) {
public Mono<Boolean> create(Map<String, Object> settings) {
Assert.notNull(settings, "settings must not be null");
@@ -116,7 +116,7 @@ public class ReactiveIndicesTemplate
}
@Override
public Mono<Boolean> create(Map<String, @Nullable Object> settings, Document mapping) {
public Mono<Boolean> create(Map<String, Object> settings, Document mapping) {
Assert.notNull(settings, "settings must not be null");
Assert.notNull(mapping, "mapping must not be null");
@@ -132,7 +132,7 @@ public class ReactiveIndicesTemplate
doCreate(getIndexCoordinates(), settings, mapping))); //
}
private Mono<Boolean> doCreate(IndexCoordinates indexCoordinates, Map<String, @Nullable Object> settings,
private Mono<Boolean> doCreate(IndexCoordinates indexCoordinates, Map<String, Object> settings,
@Nullable Document mapping) {
Set<Alias> aliases = (boundClass != null) ? getAliasesFor(boundClass) : new HashSet<>();
CreateIndexSettings indexSettings = CreateIndexSettings.builder(indexCoordinates)
@@ -270,7 +270,7 @@ public class ReactiveIndicesTemplate
return getAliases(null, indexNames);
}
private Mono<Map<String, Set<AliasData>>> getAliases(String@Nullable [] aliasNames, String@Nullable [] indexNames) {
private Mono<Map<String, Set<AliasData>>> getAliases(@Nullable String[] aliasNames, @Nullable String[] indexNames) {
GetAliasRequest getAliasRequest = requestConverter.indicesGetAliasRequest(aliasNames, indexNames);
Mono<GetAliasResponse> getAliasResponse = Mono.from(execute(client -> client.getAlias(getAliasRequest)));
@@ -48,7 +48,6 @@ import co.elastic.clients.elasticsearch.core.search.Rescore;
import co.elastic.clients.elasticsearch.core.search.SearchRequestBody;
import co.elastic.clients.elasticsearch.core.search.SourceConfig;
import co.elastic.clients.elasticsearch.indices.*;
import co.elastic.clients.elasticsearch.indices.ExistsIndexTemplateRequest;
import co.elastic.clients.elasticsearch.indices.ExistsRequest;
import co.elastic.clients.elasticsearch.indices.update_aliases.Action;
import co.elastic.clients.elasticsearch.sql.query.SqlFormat;
@@ -74,17 +73,19 @@ import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.Optional;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.Nullable;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.RefreshPolicy;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.index.*;
import org.springframework.data.elasticsearch.core.index.AliasAction;
import org.springframework.data.elasticsearch.core.index.AliasActionParameters;
import org.springframework.data.elasticsearch.core.index.AliasActions;
import org.springframework.data.elasticsearch.core.index.DeleteIndexTemplateRequest;
import org.springframework.data.elasticsearch.core.index.DeleteTemplateRequest;
import org.springframework.data.elasticsearch.core.index.ExistsTemplateRequest;
@@ -114,7 +115,6 @@ import org.springframework.util.StringUtils;
* @author cdalxndr
* @author scoobyzhang
* @author Haibo Liu
* @author Steven Pearce
* @since 4.4
*/
class RequestConverter extends AbstractQueryProcessor {
@@ -175,9 +175,17 @@ class RequestConverter extends AbstractQueryProcessor {
private co.elastic.clients.elasticsearch.indices.Alias.Builder buildAlias(AliasActionParameters parameters,
co.elastic.clients.elasticsearch.indices.Alias.Builder aliasBuilder) {
getRouting(parameters.getRouting()).ifPresent(aliasBuilder::routing);
getRouting(parameters.getIndexRouting()).ifPresent(aliasBuilder::indexRouting);
getRouting(parameters.getSearchRouting()).ifPresent(aliasBuilder::searchRouting);
if (parameters.getRouting() != null) {
aliasBuilder.routing(parameters.getRouting());
}
if (parameters.getIndexRouting() != null) {
aliasBuilder.indexRouting(parameters.getIndexRouting());
}
if (parameters.getSearchRouting() != null) {
aliasBuilder.searchRouting(parameters.getSearchRouting());
}
if (parameters.getHidden() != null) {
aliasBuilder.isHidden(parameters.getHidden());
@@ -233,16 +241,12 @@ class RequestConverter extends AbstractQueryProcessor {
Map<String, co.elastic.clients.elasticsearch.indices.Alias> aliases = new HashMap<>();
for (Alias alias : indexSettings.getAliases()) {
co.elastic.clients.elasticsearch.indices.Alias esAlias = co.elastic.clients.elasticsearch.indices.Alias
.of(ab -> {
co.elastic.clients.elasticsearch.indices.Alias.Builder aliasBuilder = ab.filter(getQuery(alias.getFilter(), null))
.isHidden(alias.getHidden())
.isWriteIndex(alias.getWriteIndex());
getRouting(alias.getRouting()).ifPresent(aliasBuilder::routing);
getRouting(alias.getIndexRouting()).ifPresent(aliasBuilder::indexRouting);
getRouting(alias.getSearchRouting()).ifPresent(aliasBuilder::searchRouting);
return aliasBuilder;
});
.of(ab -> ab.filter(getQuery(alias.getFilter(), null))
.routing(alias.getRouting())
.indexRouting(alias.getIndexRouting())
.searchRouting(alias.getSearchRouting())
.isHidden(alias.getHidden())
.isWriteIndex(alias.getWriteIndex()));
aliases.put(alias.getAlias(), esAlias);
}
@@ -316,11 +320,10 @@ class RequestConverter extends AbstractQueryProcessor {
addActionBuilder //
.indices(Arrays.asList(parameters.getIndices())) //
.isHidden(parameters.getHidden()) //
.isWriteIndex(parameters.getWriteIndex()); //
getRouting(parameters.getRouting()).ifPresent(addActionBuilder::routing);
getRouting(parameters.getIndexRouting()).ifPresent(addActionBuilder::indexRouting);
getRouting(parameters.getSearchRouting()).ifPresent(addActionBuilder::searchRouting);
.isWriteIndex(parameters.getWriteIndex()) //
.routing(parameters.getRouting()) //
.indexRouting(parameters.getIndexRouting()) //
.searchRouting(parameters.getSearchRouting()); //
if (parameters.getAliases() != null) {
addActionBuilder.aliases(Arrays.asList(parameters.getAliases()));
@@ -588,11 +591,10 @@ class RequestConverter extends AbstractQueryProcessor {
builder.version(query.getVersion()).versionType(versionType);
}
builder
.ifSeqNo(query.getSeqNo())
.ifPrimaryTerm(query.getPrimaryTerm());
getRouting(query.getRouting()).ifPresent(builder::routing);
builder //
.ifSeqNo(query.getSeqNo()) //
.ifPrimaryTerm(query.getPrimaryTerm()) //
.routing(query.getRouting()); //
if (query.getOpType() != null) {
switch (query.getOpType()) {
@@ -642,9 +644,8 @@ class RequestConverter extends AbstractQueryProcessor {
builder //
.ifSeqNo(query.getSeqNo()) //
.ifPrimaryTerm(query.getPrimaryTerm()); //
getRouting(query.getRouting()).ifPresent(builder::routing);
.ifPrimaryTerm(query.getPrimaryTerm()) //
.routing(query.getRouting()); //
return builder.build();
}
@@ -685,9 +686,8 @@ class RequestConverter extends AbstractQueryProcessor {
builder //
.ifSeqNo(query.getSeqNo()) //
.ifPrimaryTerm(query.getPrimaryTerm()); //
getRouting(query.getRouting()).ifPresent(builder::routing);
.ifPrimaryTerm(query.getPrimaryTerm()) //
.routing(query.getRouting()); //
return builder.build();
}
@@ -724,12 +724,11 @@ class RequestConverter extends AbstractQueryProcessor {
});
uob
.routing(query.getRouting())
.ifSeqNo(query.getIfSeqNo())
.ifPrimaryTerm(query.getIfPrimaryTerm())
.retryOnConflict(query.getRetryOnConflict());
getRouting(query.getRouting()).ifPresent(uob::routing);
// no refresh, timeout, waitForActiveShards on UpdateOperation or UpdateAction
return uob.build();
@@ -779,7 +778,9 @@ class RequestConverter extends AbstractQueryProcessor {
builder.pipeline(bulkOptions.getPipeline());
}
getRouting(bulkOptions.getRoutingId()).ifPresent(builder::routing);
if (bulkOptions.getRoutingId() != null) {
builder.routing(bulkOptions.getRoutingId());
}
List<BulkOperation> operations = queries.stream().map(query -> {
BulkOperation.Builder ob = new BulkOperation.Builder();
@@ -806,13 +807,10 @@ class RequestConverter extends AbstractQueryProcessor {
Assert.notNull(id, "id must not be null");
Assert.notNull(indexCoordinates, "indexCoordinates must not be null");
return GetRequest.of(grb -> {
GetRequest.Builder builder = grb //
.index(indexCoordinates.getIndexName()) //
.id(id); //
getRouting(routing).ifPresent(builder::routing);
return builder;
});
return GetRequest.of(grb -> grb //
.index(indexCoordinates.getIndexName()) //
.id(id) //
.routing(routing));
}
public co.elastic.clients.elasticsearch.core.ExistsRequest documentExistsRequest(String id, @Nullable String routing,
@@ -821,13 +819,10 @@ class RequestConverter extends AbstractQueryProcessor {
Assert.notNull(id, "id must not be null");
Assert.notNull(indexCoordinates, "indexCoordinates must not be null");
return co.elastic.clients.elasticsearch.core.ExistsRequest.of(erb -> {
co.elastic.clients.elasticsearch.core.ExistsRequest.Builder builder = erb
.index(indexCoordinates.getIndexName())
.id(id);
getRouting(routing).ifPresent(builder::routing);
return builder;
});
return co.elastic.clients.elasticsearch.core.ExistsRequest.of(erb -> erb
.index(indexCoordinates.getIndexName())
.id(id)
.routing(routing));
}
public <T> MgetRequest documentMgetRequest(Query query, Class<T> clazz, IndexCoordinates index) {
@@ -845,14 +840,11 @@ class RequestConverter extends AbstractQueryProcessor {
SourceConfig sourceConfig = getSourceConfig(query);
List<MultiGetOperation> multiGetOperations = query.getIdsWithRouting().stream()
.map(idWithRouting -> MultiGetOperation.of(mgo -> {
MultiGetOperation.Builder builder = mgo //
.index(index.getIndexName()) //
.id(idWithRouting.id()) //
.source(sourceConfig);
getRouting(idWithRouting.routing()).ifPresent(builder::routing);
return builder;
}))
.map(idWithRouting -> MultiGetOperation.of(mgo -> mgo //
.index(index.getIndexName()) //
.id(idWithRouting.id()) //
.routing(idWithRouting.routing()) //
.source(sourceConfig)))
.collect(Collectors.toList());
return MgetRequest.of(mg -> mg//
@@ -910,17 +902,8 @@ class RequestConverter extends AbstractQueryProcessor {
}
SourceFilter sourceFilter = source.getSourceFilter();
if (sourceFilter != null && (sourceFilter.getIncludes() != null || sourceFilter.getExcludes() != null)) {
s.sourceFields(cfg -> cfg
.filter(f -> {
if (sourceFilter.getIncludes() != null) {
f.includes(Arrays.asList(sourceFilter.getIncludes()));
}
if (sourceFilter.getExcludes() != null) {
f.excludes(Arrays.asList(sourceFilter.getExcludes()));
}
return f;
}));
if (sourceFilter != null && sourceFilter.getIncludes() != null) {
s.sourceFields(Arrays.asList(sourceFilter.getIncludes()));
}
return s;
}) //
@@ -974,7 +957,10 @@ class RequestConverter extends AbstractQueryProcessor {
return DeleteRequest.of(r -> {
r.id(id).index(index.getIndexName());
getRouting(routing).ifPresent(r::routing);
if (routing != null) {
r.routing(routing);
}
r.refresh(refresh(refreshPolicy));
return r;
});
@@ -998,7 +984,11 @@ class RequestConverter extends AbstractQueryProcessor {
b.scroll(time(query.getScrollTime()));
getRouting(query.getRoute(), routing).ifPresent(b::routing);
if (query.getRoute() != null) {
b.routing(query.getRoute());
} else if (StringUtils.hasText(routing)) {
b.routing(routing);
}
return b;
});
@@ -1018,7 +1008,11 @@ class RequestConverter extends AbstractQueryProcessor {
.scroll(time(query.getScroll()))
.scrollSize(query.getScrollSize());
getRouting(query.getRouting(), routing).ifPresent(dqb::routing);
if (query.getRouting() != null) {
dqb.routing(query.getRouting());
} else if (StringUtils.hasText(routing)) {
dqb.routing(routing);
}
if (query.getQ() != null) {
dqb.q(query.getQ())
@@ -1071,10 +1065,7 @@ class RequestConverter extends AbstractQueryProcessor {
return UpdateRequest.of(uqb -> {
uqb.index(indexName).id(query.getId());
var scriptData = query.getScriptData();
var script = scriptData != null ? scriptData.script() : null;
if (script != null) {
if (query.getScript() != null) {
Map<String, JsonData> params = new HashMap<>();
if (query.getParams() != null) {
@@ -1082,14 +1073,11 @@ class RequestConverter extends AbstractQueryProcessor {
}
uqb.script(sb -> {
sb
.lang(scriptData.language())
.params(params);
if (script != null) {
sb.source(s -> s.scriptString(script));
sb.lang(query.getLang()).params(params);
if (query.getScript() != null) {
sb.source(s -> s.scriptString(query.getScript()));
}
sb.id(scriptData.scriptName());
sb.id(query.getId());
return sb;
});
@@ -1098,6 +1086,7 @@ class RequestConverter extends AbstractQueryProcessor {
uqb
.doc(query.getDocument())
.upsert(query.getUpsert())
.routing(query.getRouting() != null ? query.getRouting() : routing)
.scriptedUpsert(query.getScriptedUpsert())
.docAsUpsert(query.getDocAsUpsert())
.ifSeqNo(query.getIfSeqNo())
@@ -1105,8 +1094,6 @@ class RequestConverter extends AbstractQueryProcessor {
.refresh(query.getRefreshPolicy() != null ? refresh(query.getRefreshPolicy()) : refresh(refreshPolicy))
.retryOnConflict(query.getRetryOnConflict());
getRouting(query.getRouting(), routing).ifPresent(uqb::routing);
if (query.getFetchSource() != null) {
uqb.source(sc -> sc.fetch(query.getFetchSource()));
}
@@ -1139,7 +1126,8 @@ class RequestConverter extends AbstractQueryProcessor {
}
return uqb;
});
} //
);
}
public UpdateByQueryRequest documentUpdateByQueryRequest(UpdateQuery updateQuery, IndexCoordinates index,
@@ -1149,14 +1137,13 @@ class RequestConverter extends AbstractQueryProcessor {
ub //
.index(Arrays.asList(index.getIndexNames())) //
.refresh(refreshPolicy == RefreshPolicy.IMMEDIATE) //
.routing(updateQuery.getRouting()) //
.script(getScript(updateQuery.getScriptData())) //
.maxDocs(updateQuery.getMaxDocs() != null ? Long.valueOf(updateQuery.getMaxDocs()) : null) //
.pipeline(updateQuery.getPipeline()) //
.requestsPerSecond(updateQuery.getRequestsPerSecond()) //
.slices(slices(updateQuery.getSlices() != null ? Long.valueOf(updateQuery.getSlices()) : null));
getRouting(updateQuery.getRouting()).ifPresent(ub::routing);
if (updateQuery.getAbortOnVersionConflict() != null) {
ub.conflicts(updateQuery.getAbortOnVersionConflict() ? Conflicts.Abort : Conflicts.Proceed);
}
@@ -1230,7 +1217,12 @@ class RequestConverter extends AbstractQueryProcessor {
builder.query(getQuery(query, clazz));
getRouting(query.getRoute(), routing).ifPresent(builder::routing);
if (StringUtils.hasText(query.getRoute())) {
builder.routing(query.getRoute());
}
if (StringUtils.hasText(routing)) {
builder.routing(routing);
}
addPostFilter(query, builder);
@@ -1397,7 +1389,11 @@ class RequestConverter extends AbstractQueryProcessor {
.requestCache(query.getRequestCache()) //
;
getRouting(query.getRoute(), routing).ifPresent(h::routing);
if (StringUtils.hasText(query.getRoute())) {
h.routing(query.getRoute());
} else if (StringUtils.hasText(routing)) {
h.routing(routing);
}
if (query.getPreference() != null) {
h.preference(query.getPreference());
@@ -1427,7 +1423,6 @@ class RequestConverter extends AbstractQueryProcessor {
.searchType(searchType) //
.timeout(timeStringMs(query.getTimeout())) //
.requestCache(query.getRequestCache()) //
.includeNamedQueriesScore(query.getIncludeNamedQueriesScore()) //
;
var pointInTime = query.getPointInTime();
@@ -1443,7 +1438,11 @@ class RequestConverter extends AbstractQueryProcessor {
builder.expandWildcards(expandWildcards(expandWildcards));
}
getRouting(query.getRoute(), routing).ifPresent(builder::routing);
if (query.getRoute() != null) {
builder.routing(query.getRoute());
} else if (StringUtils.hasText(routing)) {
builder.routing(routing);
}
if (query.getPreference() != null) {
builder.preference(query.getPreference());
@@ -1874,7 +1873,11 @@ class RequestConverter extends AbstractQueryProcessor {
if (query.getSource() != null) {
builder.source(so -> so.scriptString(query.getSource()));
}
getRouting(query.getRoute(), routing).ifPresent(builder::routing);
if (query.getRoute() != null) {
builder.routing(query.getRoute());
} else if (StringUtils.hasText(routing)) {
builder.routing(routing);
}
var expandWildcards = query.getExpandWildcards();
if (expandWildcards != null && !expandWildcards.isEmpty()) {
@@ -1974,16 +1977,6 @@ class RequestConverter extends AbstractQueryProcessor {
return null;
}
Optional<String> getRouting(@Nullable String routing) {
if (StringUtils.hasText(routing)) {
return Optional.of(routing);
}
return Optional.empty();
}
Optional<String> getRouting(@Nullable String routing1, @Nullable String routing2) {
return getRouting(routing1).or(() -> getRouting(routing2));
}
private VersionType retrieveVersionTypeFromPersistentEntity(@Nullable Class<?> clazz) {
ElasticsearchPersistentEntity<?> persistentEntity = getPersistentEntity(clazz);
@@ -22,7 +22,7 @@ import co.elastic.clients.elasticsearch._types.BulkIndexByScrollFailure;
import co.elastic.clients.elasticsearch._types.ErrorCause;
import co.elastic.clients.elasticsearch._types.Time;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.cluster.ComponentTemplateSummaryRes;
import co.elastic.clients.elasticsearch.cluster.ComponentTemplateSummary;
import co.elastic.clients.elasticsearch.cluster.GetComponentTemplateResponse;
import co.elastic.clients.elasticsearch.cluster.HealthResponse;
import co.elastic.clients.elasticsearch.core.DeleteByQueryResponse;
@@ -66,7 +66,6 @@ import org.springframework.data.elasticsearch.core.reindex.ReindexResponse;
import org.springframework.data.elasticsearch.core.script.Script;
import org.springframework.data.elasticsearch.core.sql.SqlResponse;
import org.springframework.data.elasticsearch.support.DefaultStringObjectMap;
import org.springframework.lang.Contract;
import org.springframework.util.Assert;
/**
@@ -133,7 +132,7 @@ class ResponseConverter {
.build();
}
private TemplateResponseData clusterGetComponentTemplateData(ComponentTemplateSummaryRes componentTemplateSummary) {
private TemplateResponseData clusterGetComponentTemplateData(ComponentTemplateSummary componentTemplateSummary) {
var mapping = typeMapping(componentTemplateSummary.mappings());
var settings = new Settings();
@@ -336,11 +335,11 @@ class ResponseConverter {
.build();
}
private TemplateResponseData indexGetComponentTemplateData(IndexTemplateSummaryWithRollover indexTemplateSummary,
private TemplateResponseData indexGetComponentTemplateData(IndexTemplateSummary indexTemplateSummary,
List<String> composedOf) {
var mapping = typeMapping(indexTemplateSummary.mappings());
Function<@Nullable IndexSettings, @Nullable Settings> indexSettingsToSettings = indexSettings -> {
Function<IndexSettings, Settings> indexSettingsToSettings = indexSettings -> {
if (indexSettings == null) {
return null;
@@ -498,7 +497,7 @@ class ResponseConverter {
builder.withDeleted(response.deleted());
}
if (response.updated() != null) {
if(response.updated() != null) {
builder.withUpdated(response.updated());
}
@@ -575,20 +574,17 @@ class ResponseConverter {
}
}
@Contract("null -> null; !null -> !null")
@Nullable
static ElasticsearchErrorCause toErrorCause(@Nullable ErrorCause errorCause) {
if (errorCause != null) {
return new ElasticsearchErrorCause(
errorCause.type(),
errorCause.reason(),
errorCause.stackTrace(),
toErrorCause(errorCause.causedBy()),
(List<ElasticsearchErrorCause>) (errorCause.rootCause().stream()
.map(ResponseConverter::toErrorCause).collect(Collectors.toList())),
(List<ElasticsearchErrorCause>) (errorCause.suppressed().stream().map(ResponseConverter::toErrorCause)
.collect(Collectors.toList())));
return new ElasticsearchErrorCause( //
errorCause.type(), //
errorCause.reason(), //
errorCause.stackTrace(), //
toErrorCause(errorCause.causedBy()), //
errorCause.rootCause().stream().map(ResponseConverter::toErrorCause).collect(Collectors.toList()), //
errorCause.suppressed().stream().map(ResponseConverter::toErrorCause).collect(Collectors.toList()));
} else {
return null;
}
@@ -36,7 +36,6 @@ import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import org.jspecify.annotations.Nullable;
@@ -145,12 +144,6 @@ final class TypeUtils {
}
}
static Object toObjectNotNull(FieldValue fieldValue) {
Objects.requireNonNull(fieldValue);
return toObject(fieldValue);
}
@Nullable
static Object toObject(@Nullable FieldValue fieldValue) {
@@ -492,7 +485,7 @@ final class TypeUtils {
}
@Nullable
static IndexSettings indexSettings(@Nullable Map<String, @Nullable Object> settings) {
static IndexSettings indexSettings(@Nullable Map<String, Object> settings) {
return settings != null ? IndexSettings.of(b -> b.withJson(new StringReader(Document.from(settings).toJson())))
: null;
}
@@ -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())) {
@@ -1,18 +1,2 @@
/*
* Copyright 2022-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.
*/
@org.jspecify.annotations.NullMarked
package org.springframework.data.elasticsearch.client.elc.aot;
@@ -28,6 +28,7 @@ import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.message.BasicHeader;
import org.apache.hc.core5.http.nio.ssl.BasicClientTlsStrategy;
import org.apache.hc.core5.util.Timeout;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.support.HttpHeaders;
@@ -180,7 +181,7 @@ public final class Rest5Clients {
return builder;
}
private static HttpHost[] getHttpHosts(ClientConfiguration clientConfiguration) {
private static HttpHost @NonNull [] getHttpHosts(ClientConfiguration clientConfiguration) {
List<InetSocketAddress> hosts = clientConfiguration.getEndpoints();
boolean useSsl = clientConfiguration.useSsl();
return hosts.stream()
@@ -21,8 +21,10 @@ import org.apache.http.protocol.HttpContext;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchClients;
import org.springframework.data.elasticsearch.support.HttpHeaders;
import org.springframework.util.Assert;
@@ -105,7 +107,7 @@ public final class RestClients {
return builder;
}
private static HttpHost[] getHttpHosts(ClientConfiguration clientConfiguration) {
private static HttpHost @NonNull [] getHttpHosts(ClientConfiguration clientConfiguration) {
List<InetSocketAddress> hosts = clientConfiguration.getEndpoints();
boolean useSsl = clientConfiguration.useSsl();
return hosts.stream()
@@ -128,10 +130,10 @@ public final class RestClients {
record CustomHeaderInjector(Supplier<HttpHeaders> headersSupplier) implements HttpRequestInterceptor {
@Override
public void process(@Nullable HttpRequest request, @Nullable HttpContext context) {
public void process(HttpRequest request, HttpContext context) {
HttpHeaders httpHeaders = headersSupplier.get();
if (!httpHeaders.isEmpty() && request != null) {
if (httpHeaders != null && !httpHeaders.isEmpty()) {
Arrays.stream(toHeaderArray(httpHeaders)).forEach(request::addHeader);
}
}
@@ -1,18 +1,2 @@
/*
* Copyright 2022-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.
*/
@org.jspecify.annotations.NullMarked
package org.springframework.data.elasticsearch.client;
@@ -1,18 +1,2 @@
/*
* Copyright 2022-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.
*/
@org.jspecify.annotations.NullMarked
package org.springframework.data.elasticsearch.client.util;
@@ -18,7 +18,6 @@ package org.springframework.data.elasticsearch.config;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.jspecify.annotations.Nullable;
@@ -99,7 +98,7 @@ public class ElasticsearchConfigurationSupport {
protected Collection<String> getMappingBasePackages() {
Package mappingBasePackage = getClass().getPackage();
return mappingBasePackage == null ? Collections.emptyList() : List.of(mappingBasePackage.getName());
return Collections.singleton(mappingBasePackage == null ? null : mappingBasePackage.getName());
}
/**
@@ -1,18 +1,2 @@
/*
* Copyright 2022-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.
*/
@org.jspecify.annotations.NullMarked
package org.springframework.data.elasticsearch.config;
@@ -15,8 +15,6 @@
*/
package org.springframework.data.elasticsearch.core;
import io.micrometer.observation.ObservationRegistry;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collections;
@@ -45,6 +43,7 @@ import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersiste
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
import org.springframework.data.elasticsearch.core.query.BulkOptions;
import org.springframework.data.elasticsearch.core.query.ByQueryResponse;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder;
import org.springframework.data.elasticsearch.core.query.MoreLikeThisQuery;
@@ -59,7 +58,6 @@ import org.springframework.data.elasticsearch.support.VersionInfo;
import org.springframework.data.mapping.callback.EntityCallbacks;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.util.Streamable;
import org.springframework.lang.Contract;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -87,7 +85,6 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
@Nullable protected EntityCallbacks entityCallbacks;
@Nullable protected RefreshPolicy refreshPolicy;
protected RoutingResolver routingResolver;
protected ObservationRegistry observationRegistry = ObservationRegistry.NOOP;
public AbstractElasticsearchTemplate() {
this(null);
@@ -120,8 +117,6 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
copy.setRoutingResolver(routingResolver);
copy.setRefreshPolicy(refreshPolicy);
copy.setObservationRegistry(observationRegistry);
customizeCopy(copy);
return copy;
}
@@ -176,27 +171,6 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
return refreshPolicy;
}
/**
* Set the {@link ObservationRegistry} to use for recording observations.
*
* @param observationRegistry must not be {@literal null}.
* @since 6.1
*/
public void setObservationRegistry(ObservationRegistry observationRegistry) {
Assert.notNull(observationRegistry, "observationRegistry must not be null");
this.observationRegistry = observationRegistry;
}
/**
* Hook for subclasses to copy additional state during {@link #copy()}. Called after all common fields have been
* copied. The default implementation does nothing.
*
* @param copy the new template instance to customize
*/
protected void customizeCopy(AbstractElasticsearchTemplate copy) {}
/**
* logs the versions of the different Elasticsearch components.
*
@@ -261,7 +235,7 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
List<IndexedObjectInformation> indexedObjectInformationList = bulkIndex(indexQueries, index);
Iterator<IndexedObjectInformation> iterator = indexedObjectInformationList.iterator();
// noinspection unchecked,DataFlowIssue
// noinspection unchecked
return indexQueries.stream() //
.map(IndexQuery::getObject) //
.map(entity -> (T) entityOperations.updateIndexedObject(
@@ -594,7 +568,7 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
}
protected <T> SearchDocumentResponse.EntityCreator<T> getEntityCreator(ReadDocumentCallback<T> documentCallback) {
return searchDocument -> CompletableFuture.<T> completedFuture(documentCallback.doWith(searchDocument));
return searchDocument -> CompletableFuture.completedFuture(documentCallback.doWith(searchDocument));
}
/**
@@ -753,7 +727,6 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
// region Document callbacks
protected interface DocumentCallback<T> {
@Contract("null -> null")
@Nullable
T doWith(@Nullable Document document);
}
@@ -817,7 +790,6 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
@Override
public SearchHits<T> doWith(SearchDocumentResponse response) {
// noinspection NullableProblems,DataFlowIssue
List<T> entities = response.getSearchDocuments().stream().map(delegate::doWith).collect(Collectors.toList());
return SearchHitMapping.mappingFor(type, elasticsearchConverter).mapHits(response, entities);
}
@@ -838,7 +810,6 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
@Override
public SearchScrollHits<T> doWith(SearchDocumentResponse response) {
// noinspection DataFlowIssue,NullableProblems
List<T> entities = response.getSearchDocuments().stream().map(delegate::doWith).collect(Collectors.toList());
return SearchHitMapping.mappingFor(type, elasticsearchConverter).mapScrollHits(response, entities);
}
@@ -15,7 +15,6 @@
*/
package org.springframework.data.elasticsearch.core;
import io.micrometer.observation.ObservationRegistry;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.publisher.Sinks;
@@ -78,7 +77,6 @@ abstract public class AbstractReactiveElasticsearchTemplate
protected RoutingResolver routingResolver;
protected @Nullable ReactiveEntityCallbacks entityCallbacks;
protected ObservationRegistry observationRegistry = ObservationRegistry.NOOP;
// region Initialization
protected AbstractReactiveElasticsearchTemplate(@Nullable ElasticsearchConverter converter) {
@@ -111,8 +109,6 @@ abstract public class AbstractReactiveElasticsearchTemplate
}
copy.setRoutingResolver(routingResolver);
copy.setObservationRegistry(observationRegistry);
customizeCopy(copy);
return copy;
}
@@ -166,27 +162,6 @@ abstract public class AbstractReactiveElasticsearchTemplate
this.entityCallbacks = entityCallbacks;
}
/**
* Set the {@link ObservationRegistry} to use for recording observations.
*
* @param observationRegistry must not be {@literal null}.
* @since 6.1
*/
public void setObservationRegistry(ObservationRegistry observationRegistry) {
Assert.notNull(observationRegistry, "observationRegistry must not be null");
this.observationRegistry = observationRegistry;
}
/**
* Hook for subclasses to copy additional state during {@link #copy()}. Called after all common fields have been
* copied. The default implementation does nothing.
*
* @param copy the new template instance to customize
*/
protected void customizeCopy(AbstractReactiveElasticsearchTemplate copy) {}
/**
* logs the versions of the different Elasticsearch components.
*
@@ -270,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) {
@@ -283,13 +255,7 @@ abstract public class AbstractReactiveElasticsearchTemplate
} else {
sink.tryEmitComplete();
}
})
.subscribe(v -> {}, error -> {
if (subscription != null) {
subscription.cancel();
}
sink.tryEmitError(error);
});
}).subscribe();
}
@Override
@@ -766,8 +732,7 @@ abstract public class AbstractReactiveElasticsearchTemplate
/**
* Value class to capture client independent information from a response to an index request.
*/
public record IndexResponseMetaData(String id, String index, @Nullable Long seqNo, @Nullable Long primaryTerm,
long version) {
public record IndexResponseMetaData(String id, String index, long seqNo, long primaryTerm, long version) {
}
// endregion
@@ -17,6 +17,7 @@ package org.springframework.data.elasticsearch.core;
import java.util.Map;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
@@ -369,7 +370,7 @@ public class EntityOperations {
* @see org.springframework.data.elasticsearch.core.EntityOperations.AdaptableEntity#initializeVersionProperty()
*/
@Override
public T initializeVersionProperty() {
public @NonNull T initializeVersionProperty() {
return map;
}
@@ -398,7 +399,7 @@ public class EntityOperations {
* @see org.springframework.data.elasticsearch.core.EntityOperations.AdaptableEntity#incrementVersion()
*/
@Override
public T incrementVersion() {
public @NonNull T incrementVersion() {
return map;
}
@@ -407,7 +408,7 @@ public class EntityOperations {
* @see org.springframework.data.elasticsearch.core.EntityOperations.Entity#getBean()
*/
@Override
public T getBean() {
public @NonNull T getBean() {
return map;
}
@@ -52,7 +52,7 @@ public interface IndexOperations {
* @param settings the index settings
* @return {@literal true} if the index was created
*/
boolean create(Map<String, @Nullable Object> settings);
boolean create(Map<String, Object> settings);
/**
* Create an index for given settings and mapping.
@@ -62,7 +62,7 @@ public interface IndexOperations {
* @return {@literal true} if the index was created
* @since 4.2
*/
boolean create(Map<String, @Nullable Object> settings, Document mapping);
boolean create(Map<String, Object> settings, Document mapping);
/**
* Create an index with the settings and mapping defined for the entity this IndexOperations is bound to.
@@ -142,7 +142,7 @@ public interface IndexOperations {
*
* @return the mapping
*/
Map<String, @Nullable Object> getMapping();
Map<String, Object> getMapping();
// endregion
@@ -46,12 +46,12 @@ public interface IndexOperationsAdapter extends IndexOperations {
}
@Override
public boolean create(Map<String, @Nullable Object> settings) {
public boolean create(Map<String, Object> settings) {
return Boolean.TRUE.equals(reactiveIndexOperations.create(settings).block());
}
@Override
public boolean create(Map<String, @Nullable Object> settings, Document mapping) {
public boolean create(Map<String, Object> settings, Document mapping) {
return Boolean.TRUE.equals(reactiveIndexOperations.create(settings, mapping).block());
}
@@ -92,7 +92,7 @@ public interface IndexOperationsAdapter extends IndexOperations {
}
@Override
public Map<String, @Nullable Object> getMapping() {
public Map<String, Object> getMapping() {
return Objects.requireNonNull(reactiveIndexOperations.getMapping().block());
}
@@ -36,7 +36,7 @@ public class MultiGetItem<T> {
}
public static <T> MultiGetItem<T> of(@Nullable T item, @Nullable Failure failure) {
return new MultiGetItem(item, failure);
return new MultiGetItem<>(item, failure);
}
public boolean hasItem() {
@@ -21,7 +21,6 @@ import reactor.core.publisher.Mono;
import java.util.Map;
import java.util.Set;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.index.*;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
@@ -51,7 +50,7 @@ public interface ReactiveIndexOperations {
* @return a {@link Mono} signalling successful operation completion or an {@link Mono#error(Throwable) error} if eg.
* the index already exist.
*/
Mono<Boolean> create(Map<String, @Nullable Object> settings);
Mono<Boolean> create(Map<String, Object> settings);
/**
* Create an index for given settings and mapping.
@@ -62,7 +61,7 @@ public interface ReactiveIndexOperations {
* the index already exist.
* @since 4.2
*/
Mono<Boolean> create(Map<String, @Nullable Object> settings, Document mapping);
Mono<Boolean> create(Map<String, Object> settings, Document mapping);
/**
* Create an index with the settings and mapping defined for the entity this IndexOperations is bound to.
@@ -51,7 +51,7 @@ public class SearchHit<T> {
private final Map<String, Double> matchedQueries = new LinkedHashMap<>();
public SearchHit(@Nullable String index, @Nullable String id, @Nullable String routing, float score,
Object @Nullable [] sortValues, @Nullable Map<String, List<String>> highlightFields,
@Nullable Object[] sortValues, @Nullable Map<String, List<String>> highlightFields,
@Nullable Map<String, SearchHits<?>> innerHits, @Nullable NestedMetaData nestedMetaData,
@Nullable Explanation explanation, @Nullable Map<String, Double> matchedQueries, T content) {
this.index = index;
@@ -193,6 +193,7 @@ public class SearchHit<T> {
/**
* @return the matched queries for this SearchHit.
*/
@Nullable
public Map<String, Double> getMatchedQueries() {
return matchedQueries;
}
@@ -22,11 +22,11 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jspecify.annotations.Nullable;
import org.springframework.data.core.ReactiveWrappers;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.util.CloseableIterator;
import org.springframework.lang.Contract;
/**
* Utility class with helper methods for working with {@link SearchHit}.
@@ -47,7 +47,6 @@ public final class SearchHitSupport {
* @return a corresponding object where the SearchHits are replaced by their content if possible, otherwise the
* original object
*/
@Contract("null -> null; !null -> !null")
@Nullable
public static Object unwrapSearchHits(@Nullable Object result) {
@@ -133,7 +133,7 @@ public class SearchHitsImpl<T> implements SearchScrollHits<T> {
}
@Override
public @Nullable SearchShardStatistics getSearchShardStatistics() {
public SearchShardStatistics getSearchShardStatistics() {
return searchShardStatistics;
}
@@ -1,18 +1,5 @@
/*
* Copyright 2022-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.
/**
* Interfaces and classes related to Elasticsearch cluster information and management.
*/
@org.jspecify.annotations.NullMarked
package org.springframework.data.elasticsearch.core.cluster;
@@ -29,7 +29,7 @@ import org.springframework.data.mapping.context.MappingContext;
* @author Christoph Strobl
* @since 3.2
*/
public interface ElasticsearchTypeMapper extends TypeMapper<Map<String, @Nullable Object>> {
public interface ElasticsearchTypeMapper extends TypeMapper<Map<String, Object>> {
String DEFAULT_TYPE_KEY = "_class";
@@ -47,7 +47,7 @@ public interface ElasticsearchTypeMapper extends TypeMapper<Map<String, @Nullabl
@Nullable
String getTypeKey();
default boolean containsTypeInformation(Map<String, @Nullable Object> source) {
default boolean containsTypeInformation(Map<String, Object> source) {
return readType(source) != null;
}
@@ -20,9 +20,9 @@ import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import org.jspecify.annotations.Nullable;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
@@ -167,7 +167,7 @@ public class GeoConverters {
INSTANCE;
@Override
public GeoJson<? extends Iterable<?>> convert(Map<String, @Nullable Object> source) {
public GeoJson<? extends Iterable<?>> convert(Map<String, Object> source) {
String type = GeoConverters.getGeoJsonType(source);
@@ -206,7 +206,7 @@ public class GeoConverters {
INSTANCE;
@Override
public GeoJsonPoint convert(Map<String, @Nullable Object> source) {
public GeoJsonPoint convert(Map<String, Object> source) {
String type = GeoConverters.getGeoJsonType(source);
Assert.isTrue(type.equalsIgnoreCase(GeoJsonPoint.TYPE), "does not contain a type 'Point'");
@@ -244,7 +244,7 @@ public class GeoConverters {
INSTANCE;
@Override
public GeoJsonMultiPoint convert(Map<String, @Nullable Object> source) {
public GeoJsonMultiPoint convert(Map<String, Object> source) {
String type = GeoConverters.getGeoJsonType(source);
Assert.isTrue(type.equalsIgnoreCase(GeoJsonMultiPoint.TYPE), "does not contain a type 'MultiPoint'");
@@ -279,7 +279,7 @@ public class GeoConverters {
INSTANCE;
@Override
public GeoJsonLineString convert(Map<String, @Nullable Object> source) {
public GeoJsonLineString convert(Map<String, Object> source) {
String type = GeoConverters.getGeoJsonType(source);
Assert.isTrue(type.equalsIgnoreCase(GeoJsonLineString.TYPE), "does not contain a type 'LineString'");
@@ -311,7 +311,7 @@ public class GeoConverters {
INSTANCE;
@Override
public GeoJsonMultiLineString convert(Map<String, @Nullable Object> source) {
public GeoJsonMultiLineString convert(Map<String, Object> source) {
String type = GeoConverters.getGeoJsonType(source);
Assert.isTrue(type.equalsIgnoreCase(GeoJsonMultiLineString.TYPE), "does not contain a type 'MultiLineString'");
@@ -339,7 +339,7 @@ public class GeoConverters {
INSTANCE;
@Override
public GeoJsonPolygon convert(Map<String, @Nullable Object> source) {
public GeoJsonPolygon convert(Map<String, Object> source) {
String type = GeoConverters.getGeoJsonType(source);
Assert.isTrue(type.equalsIgnoreCase(GeoJsonPolygon.TYPE), "does not contain a type 'Polygon'");
@@ -368,6 +368,7 @@ public class GeoConverters {
List<Object> coordinates = source.getCoordinates().stream() //
.map(GeoJsonPolygonToMapConverter.INSTANCE::convert) //
.filter(Objects::nonNull) //
.map(it -> it.get("coordinates")) //
.collect(Collectors.toList()); //
map.put("coordinates", coordinates);
@@ -382,7 +383,7 @@ public class GeoConverters {
INSTANCE;
@Override
public GeoJsonMultiPolygon convert(Map<String, @Nullable Object> source) {
public GeoJsonMultiPolygon convert(Map<String, Object> source) {
String type = GeoConverters.getGeoJsonType(source);
Assert.isTrue(type.equalsIgnoreCase(GeoJsonMultiPolygon.TYPE), "does not contain a type 'MultiPolygon'");
@@ -429,7 +430,7 @@ public class GeoConverters {
INSTANCE;
@Override
public GeoJsonGeometryCollection convert(Map<String, @Nullable Object> source) {
public GeoJsonGeometryCollection convert(Map<String, Object> source) {
String type = GeoConverters.getGeoJsonType(source);
Assert.isTrue(type.equalsIgnoreCase(GeoJsonGeometryCollection.TYPE),
@@ -447,7 +448,7 @@ public class GeoConverters {
// endregion
// region helper functions
private static String getGeoJsonType(Map<String, @Nullable Object> source) {
private static String getGeoJsonType(Map<String, Object> source) {
Object type = source.get("type");
Assert.notNull(type, "Document to convert does not contain a type");
@@ -484,7 +485,7 @@ public class GeoConverters {
return map;
}
private static List<GeoJsonLineString> geoJsonLineStringsFromMap(Map<String, @Nullable Object> source) {
private static List<GeoJsonLineString> geoJsonLineStringsFromMap(Map<String, Object> source) {
Object coordinates = source.get("coordinates");
Assert.notNull(coordinates, "Document to convert does not contain coordinates");
Assert.isTrue(coordinates instanceof List, "coordinates must be a List");
@@ -24,6 +24,7 @@ import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
@@ -62,7 +63,16 @@ import org.springframework.data.mapping.Parameter;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.SimplePropertyHandler;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.*;
import org.springframework.data.mapping.model.CachingValueExpressionEvaluatorFactory;
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
import org.springframework.data.mapping.model.EntityInstantiator;
import org.springframework.data.mapping.model.EntityInstantiators;
import org.springframework.data.mapping.model.ParameterValueProvider;
import org.springframework.data.mapping.model.PersistentEntityParameterValueProvider;
import org.springframework.data.mapping.model.PropertyValueProvider;
import org.springframework.data.mapping.model.SpELContext;
import org.springframework.data.mapping.model.ValueExpressionEvaluator;
import org.springframework.data.mapping.model.ValueExpressionParameterValueProvider;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.format.datetime.DateFormatterRegistrar;
import org.springframework.util.Assert;
@@ -257,7 +267,7 @@ public class MappingElasticsearchConverter
@Nullable
@SuppressWarnings("unchecked")
private <R> R read(TypeInformation<R> typeInformation, Map<String, @Nullable Object> source) {
private <R> R read(TypeInformation<R> typeInformation, Map<String, Object> source) {
Assert.notNull(source, "Source must not be null!");
@@ -291,7 +301,7 @@ public class MappingElasticsearchConverter
}
@SuppressWarnings("unchecked")
private <R> R readMap(TypeInformation<?> type, Map<String, @Nullable Object> source) {
private <R> R readMap(TypeInformation<?> type, Map<String, Object> source) {
Assert.notNull(source, "Document must not be null!");
@@ -303,10 +313,9 @@ public class MappingElasticsearchConverter
Class<?> rawKeyType = keyType != null ? keyType.getType() : null;
Class<?> rawValueType = valueType != null ? valueType.getType() : null;
Map<Object, @Nullable Object> map = (Map<Object, @Nullable Object>) CollectionFactory.createMap(mapType,
rawKeyType, source.keySet().size());
Map<Object, Object> map = CollectionFactory.createMap(mapType, rawKeyType, source.keySet().size());
for (Entry<String, @Nullable Object> entry : source.entrySet()) {
for (Entry<String, Object> entry : source.entrySet()) {
if (typeMapper.isTypeKey(entry.getKey())) {
continue;
@@ -316,7 +325,6 @@ public class MappingElasticsearchConverter
if (rawKeyType != null && !rawKeyType.isAssignableFrom(key.getClass())) {
key = conversionService.convert(key, rawKeyType);
Assert.notNull(key, "converted key must not be null");
}
Object value = entry.getValue();
@@ -335,7 +343,7 @@ public class MappingElasticsearchConverter
return (R) map;
}
private <R> R readEntity(ElasticsearchPersistentEntity<?> entity, Map<String, @Nullable Object> source) {
private <R> R readEntity(ElasticsearchPersistentEntity<?> entity, Map<String, Object> source) {
ElasticsearchPersistentEntity<?> targetEntity = computeClosestEntity(entity, source);
ValueExpressionEvaluator evaluator = expressionEvaluatorFactory.create(source);
@@ -574,7 +582,7 @@ public class MappingElasticsearchConverter
: TypeInformation.OBJECT;
Class<?> rawComponentType = componentType.getType();
Collection<@Nullable Object> items = targetType.getType().isArray() //
Collection<Object> items = targetType.getType().isArray() //
? new ArrayList<>(source.size()) //
: CollectionFactory.createCollection(collectionType, rawComponentType, source.size());
@@ -664,7 +672,7 @@ public class MappingElasticsearchConverter
*/
private <T> void populateScriptedFields(ElasticsearchPersistentEntity<?> entity, T result,
SearchDocument searchDocument) {
Map<String, List<@Nullable Object>> fields = searchDocument.getFields();
Map<String, List<Object>> fields = searchDocument.getFields();
entity.doWithProperties((SimplePropertyHandler) property -> {
if (property.isAnnotationPresent(ScriptedField.class)) {
ScriptedField scriptedField = property.findAnnotation(ScriptedField.class);
@@ -687,7 +695,7 @@ public class MappingElasticsearchConverter
* Compute the type to use by checking the given entity against the store type;
*/
private ElasticsearchPersistentEntity<?> computeClosestEntity(ElasticsearchPersistentEntity<?> entity,
Map<String, @Nullable Object> source) {
Map<String, Object> source) {
TypeInformation<?> typeToUse = typeMapper.readType(source);
@@ -762,7 +770,7 @@ public class MappingElasticsearchConverter
INSTANCE;
@Override
public <T> @Nullable T getParameterValue(Parameter<T, ElasticsearchPersistentProperty> parameter) {
public <T> T getParameterValue(Parameter<T, ElasticsearchPersistentProperty> parameter) {
return null;
}
}
@@ -814,7 +822,7 @@ public class MappingElasticsearchConverter
* @param typeInformation type information for the source
*/
@SuppressWarnings("unchecked")
private void writeInternal(@Nullable Object source, Map<String, @Nullable Object> sink,
private void writeInternal(@Nullable Object source, Map<String, Object> sink,
@Nullable TypeInformation<?> typeInformation) {
if (null == source) {
@@ -825,7 +833,7 @@ public class MappingElasticsearchConverter
Optional<Class<?>> customTarget = conversions.getCustomWriteTarget(entityType, Map.class);
if (customTarget.isPresent()) {
Map<String, @Nullable Object> result = conversionService.convert(source, Map.class);
Map<String, Object> result = conversionService.convert(source, Map.class);
if (result != null) {
sink.putAll(result);
@@ -855,7 +863,7 @@ public class MappingElasticsearchConverter
* @param sink the destination
* @param entity entity for the source
*/
private void writeInternal(@Nullable Object source, Map<String, @Nullable Object> sink,
private void writeInternal(@Nullable Object source, Map<String, Object> sink,
@Nullable ElasticsearchPersistentEntity<?> entity) {
if (source == null) {
@@ -897,7 +905,7 @@ public class MappingElasticsearchConverter
* @param sink must not be {@literal null}.
* @param propertyType must not be {@literal null}.
*/
private Map<String, @Nullable Object> writeMapInternal(Map<?, ?> source, Map<String, @Nullable Object> sink,
private Map<String, Object> writeMapInternal(Map<?, ?> source, Map<String, Object> sink,
TypeInformation<?> propertyType) {
for (Map.Entry<?, ?> entry : source.entrySet()) {
@@ -914,7 +922,7 @@ public class MappingElasticsearchConverter
sink.put(simpleKey,
writeCollectionInternal(asCollection(value), propertyType.getMapValueType(), new ArrayList<>()));
} else {
Map<String, @Nullable Object> document = Document.create();
Map<String, Object> document = Document.create();
TypeInformation<?> valueTypeInfo = propertyType.isMap() ? propertyType.getMapValueType()
: TypeInformation.OBJECT;
writeInternal(value, document, valueTypeInfo);
@@ -958,7 +966,7 @@ public class MappingElasticsearchConverter
} else if (element instanceof Collection || elementType.isArray()) {
collection.add(writeCollectionInternal(asCollection(element), componentType, new ArrayList<>()));
} else {
Map<String, @Nullable Object> document = Document.create();
Map<String, Object> document = Document.create();
writeInternal(element, document, componentType);
collection.add(document);
}
@@ -1072,7 +1080,7 @@ public class MappingElasticsearchConverter
: mappingContext.getRequiredPersistentEntity(type);
Object existingValue = sink.get(property);
Map<String, @Nullable Object> document = existingValue instanceof Map ? (Map<String, Object>) existingValue
Map<String, Object> document = existingValue instanceof Map ? (Map<String, Object>) existingValue
: Document.create();
addCustomTypeKeyIfNecessary(value, document, TypeInformation.of(property.getRawType()));
@@ -1089,7 +1097,7 @@ public class MappingElasticsearchConverter
* @param sink must not be {@literal null}.
* @param type type to compare to
*/
private void addCustomTypeKeyIfNecessary(Object source, Map<String, @Nullable Object> sink,
private void addCustomTypeKeyIfNecessary(Object source, Map<String, Object> sink,
@Nullable TypeInformation<?> type) {
if (!writeTypeHints) {
@@ -1210,7 +1218,7 @@ public class MappingElasticsearchConverter
Assert.notNull(map, "Given map must not be null!");
Assert.notNull(property, "PersistentProperty must not be null!");
return writeMapInternal(map, new LinkedHashMap(map.size()), property.getTypeInformation());
return writeMapInternal(map, new LinkedHashMap<>(map.size()), property.getTypeInformation());
}
/**
@@ -1484,9 +1492,9 @@ public class MappingElasticsearchConverter
@SuppressWarnings("ClassCanBeRecord")
static class MapValueAccessor {
final Map<String, @Nullable Object> target;
final Map<String, Object> target;
MapValueAccessor(Map<String, @Nullable Object> target) {
MapValueAccessor(Map<String, Object> target) {
this.target = target;
}
@@ -1520,7 +1528,7 @@ public class MappingElasticsearchConverter
}
Iterator<String> parts = Arrays.asList(fieldName.split("\\.")).iterator();
Map<String, @Nullable Object> source = target;
Map<String, Object> source = target;
Object result = null;
while (parts.hasNext()) {
@@ -1551,11 +1559,11 @@ public class MappingElasticsearchConverter
target.put(property.getFieldName(), value);
}
private Map<String, @Nullable Object> getAsMap(Object result) {
private Map<String, Object> getAsMap(Object result) {
if (result instanceof Map) {
// noinspection unchecked
return (Map<String, @Nullable Object>) result;
return (Map<String, Object>) result;
}
throw new IllegalArgumentException(String.format("%s is not a Map.", result));
@@ -1,18 +1,2 @@
/*
* Copyright 2022-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.
*/
@org.jspecify.annotations.NullMarked
package org.springframework.data.elasticsearch.core.convert;
@@ -15,8 +15,7 @@
*/
package org.springframework.data.elasticsearch.core.document;
import tools.jackson.core.JacksonException;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Function;
@@ -88,7 +87,7 @@ public interface Document extends StringObjectMap<Document> {
clear();
try {
putAll(MapDocument.OBJECT_MAPPER.readerFor(Map.class).readValue(json));
} catch (JacksonException e) {
} catch (IOException e) {
throw new ConversionException("Cannot parse JSON", e);
}
return this;
@@ -62,7 +62,7 @@ public class Explanation {
}
@Override
public boolean equals(@Nullable Object o) {
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
@@ -81,7 +81,7 @@ public class Explanation {
@Override
public int hashCode() {
int result = (Boolean.TRUE.equals(match) ? 1 : 0);
int result = (match ? 1 : 0);
result = 31 * result + value.hashCode();
result = 31 * result + (description != null ? description.hashCode() : 0);
result = 31 * result + details.hashCode();
@@ -15,9 +15,6 @@
*/
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;
@@ -28,6 +25,9 @@ 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}.
*
@@ -215,7 +215,7 @@ class MapDocument implements Document {
* @see java.util.Map#containsKey(java.lang.Object)
*/
@Override
public boolean containsKey(@Nullable Object key) {
public boolean containsKey(Object key) {
return documentAsMap.containsKey(key);
}
@@ -224,7 +224,7 @@ class MapDocument implements Document {
* @see java.util.Map#containsValue(java.lang.Object)
*/
@Override
public boolean containsValue(@Nullable Object value) {
public boolean containsValue(Object value) {
return documentAsMap.containsValue(value);
}
@@ -233,7 +233,7 @@ class MapDocument implements Document {
* @see java.util.Map#get(java.lang.Object)
*/
@Override
public Object get(@Nullable Object key) {
public Object get(Object key) {
return documentAsMap.get(key);
}
@@ -242,7 +242,7 @@ class MapDocument implements Document {
* @see java.lang.Object#getOrDefault(java.lang.Object, java.lang.Object)
*/
@Override
public Object getOrDefault(@Nullable Object key, @Nullable Object defaultValue) {
public Object getOrDefault(Object key, Object defaultValue) {
return documentAsMap.getOrDefault(key, defaultValue);
}
@@ -251,7 +251,7 @@ class MapDocument implements Document {
* @see java.util.Map#put(java.lang.Object, java.lang.Object)
*/
@Override
public Object put(String key, @Nullable Object value) {
public Object put(String key, Object value) {
return documentAsMap.put(key, value);
}
@@ -260,7 +260,7 @@ class MapDocument implements Document {
* @see java.util.Map#remove(java.lang.Object)
*/
@Override
public Object remove(@Nullable Object key) {
public Object remove(Object key) {
return documentAsMap.remove(key);
}
@@ -314,7 +314,7 @@ class MapDocument implements Document {
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(@Nullable Object o) {
public boolean equals(Object o) {
return documentAsMap.equals(o);
}
@@ -332,8 +332,7 @@ class MapDocument implements Document {
* @see java.util.Map#forEach(java.util.function.BiConsumer)
*/
@Override
public void forEach(
@SuppressWarnings("NullableProblems") BiConsumer<? super String, ? super @Nullable Object> action) {
public void forEach(BiConsumer<? super String, ? super Object> action) {
documentAsMap.forEach(action);
}
@@ -345,7 +344,7 @@ class MapDocument implements Document {
public String toJson() {
try {
return OBJECT_MAPPER.writeValueAsString(this);
} catch (JacksonException e) {
} catch (JsonProcessingException e) {
throw new MappingException("Cannot render document to JSON", e);
}
}
@@ -41,7 +41,7 @@ public interface SearchDocument extends Document {
/**
* @return the fields for the search result, not {@literal null}
*/
Map<String, List<@Nullable Object>> getFields();
Map<String, List<Object>> getFields();
/**
* The first value of the given field.
@@ -50,7 +50,7 @@ public interface SearchDocument extends Document {
*/
@Nullable
default <V> V getFieldValue(final String name) {
List<@Nullable Object> values = getFields().get(name);
List<Object> values = getFields().get(name);
if (values == null || values.isEmpty()) {
return null;
}
@@ -64,7 +64,7 @@ public interface SearchDocument extends Document {
*/
@Nullable
default <V> List<V> getFieldValues(final String name) {
List<@Nullable Object> values = getFields().get(name);
List<Object> values = getFields().get(name);
if (values == null) {
return null;
}
@@ -74,7 +74,8 @@ public interface SearchDocument extends Document {
/**
* @return the sort values for the search hit
*/
default Object @Nullable [] getSortValues() {
@Nullable
default Object[] getSortValues() {
return null;
}
@@ -35,7 +35,7 @@ public class SearchDocumentAdapter implements SearchDocument {
private final float score;
private final Object[] sortValues;
private final Map<String, List<@Nullable Object>> fields = new HashMap<>();
private final Map<String, List<Object>> fields = new HashMap<>();
private final Document delegate;
private final Map<String, List<String>> highlightFields = new HashMap<>();
private final Map<String, SearchDocumentResponse> innerHits = new HashMap<>();
@@ -44,11 +44,9 @@ public class SearchDocumentAdapter implements SearchDocument {
@Nullable private final Map<String, Double> matchedQueries;
@Nullable private final String routing;
public SearchDocumentAdapter(Document delegate, float score, Object[] sortValues,
Map<String, List<@Nullable Object>> fields,
public SearchDocumentAdapter(Document delegate, float score, Object[] sortValues, Map<String, List<Object>> fields,
Map<String, List<String>> highlightFields, Map<String, SearchDocumentResponse> innerHits,
@Nullable NestedMetaData nestedMetaData, @Nullable Explanation explanation,
@Nullable Map<String, Double> matchedQueries,
@Nullable NestedMetaData nestedMetaData, @Nullable Explanation explanation, @Nullable Map<String, Double> matchedQueries,
@Nullable String routing) {
this.delegate = delegate;
@@ -76,7 +74,7 @@ public class SearchDocumentAdapter implements SearchDocument {
}
@Override
public Map<String, List<@Nullable Object>> getFields() {
public Map<String, List<Object>> getFields() {
return fields;
}
@@ -188,17 +186,17 @@ public class SearchDocumentAdapter implements SearchDocument {
}
@Override
public boolean containsKey(@Nullable Object key) {
public boolean containsKey(Object key) {
return delegate.containsKey(key);
}
@Override
public boolean containsValue(@Nullable Object value) {
public boolean containsValue(Object value) {
return delegate.containsValue(value);
}
@Override
public Object get(@Nullable Object key) {
public Object get(Object key) {
if (delegate.containsKey(key)) {
return delegate.get(key);
@@ -209,12 +207,12 @@ public class SearchDocumentAdapter implements SearchDocument {
}
@Override
public @Nullable Object put(String key, @Nullable Object value) {
public Object put(String key, Object value) {
return delegate.put(key, value);
}
@Override
public Object remove(@Nullable Object key) {
public Object remove(Object key) {
return delegate.remove(key);
}
@@ -234,12 +232,12 @@ public class SearchDocumentAdapter implements SearchDocument {
}
@Override
public Collection<@Nullable Object> values() {
public Collection<Object> values() {
return delegate.values();
}
@Override
public Set<Entry<String, @Nullable Object>> entrySet() {
public Set<Entry<String, Object>> entrySet() {
return delegate.entrySet();
}
@@ -256,7 +254,7 @@ public class SearchDocumentAdapter implements SearchDocument {
}
@Override
public boolean equals(@Nullable Object o) {
public boolean equals(Object o) {
if (this == o) {
return true;
}
@@ -272,17 +270,17 @@ public class SearchDocumentAdapter implements SearchDocument {
}
@Override
public void forEach(@Nullable BiConsumer<? super String, ? super @Nullable Object> action) {
public void forEach(BiConsumer<? super String, ? super Object> action) {
delegate.forEach(action);
}
@Override
public boolean remove(@Nullable Object key, @Nullable Object value) {
public boolean remove(Object key, Object value) {
return delegate.remove(key, value);
}
@Override
public @Nullable String getRouting() {
public String getRouting() {
return routing;
}
@@ -1,18 +1,5 @@
/*
* Copyright 2022-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.
/**
* Classes related to the Document structure of Elasticsearch documents and search responses.
*/
@org.jspecify.annotations.NullMarked
package org.springframework.data.elasticsearch.core.document;
@@ -31,11 +31,10 @@ import org.springframework.data.mapping.callback.EntityCallback;
public interface AfterLoadCallback<T> extends EntityCallback<Document> {
/**
* Entity callback method invoked after a {@link Document} is read from Elasticsearch. Can return either the same or a
* modified instance of the {@link Document} object.
* Entity callback method invoked after a domain object is materialized from a {@link Document}. Can return either the
* same or a modified instance of the {@link Document} object.
*
* @param document the document.
* @param type the type into which the document will be converted
* @param indexCoordinates of the index the document was read from.
* @return a possible modified or new {@link Document}.
*/
@@ -1,18 +1,5 @@
/*
* Copyright 2022-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.
/**
* classes and interfaces related to Spring Data Elasticsearch events and callbacks.
*/
@org.jspecify.annotations.NullMarked
package org.springframework.data.elasticsearch.core.event;
@@ -1,34 +1,26 @@
package org.springframework.data.elasticsearch.core.geo;
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.jspecify.annotations.Nullable;
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 org.springframework.data.geo.Point;
import org.springframework.util.Assert;
class PointSerializer extends ValueSerializer<Point> {
class PointSerializer extends JsonSerializer<Point> {
@Override
public void serialize(Point value, @Nullable JsonGenerator gen, @Nullable SerializationContext serializers)
throws JacksonException {
Assert.notNull(gen, "gen must not be null");
gen.writePOJO(GeoPoint.fromPoint(value));
public void serialize(Point value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeObject(GeoPoint.fromPoint(value));
}
}
class PointDeserializer extends ValueDeserializer<Point> {
class PointDeserializer extends JsonDeserializer<Point> {
@Override
public Point deserialize(@Nullable JsonParser p, @Nullable DeserializationContext context) throws JacksonException {
Assert.notNull(p, "p must not be null");
public Point deserialize(JsonParser p, DeserializationContext context) throws IOException {
return GeoPoint.toPoint(p.readValueAs(GeoPoint.class));
}
}
@@ -19,7 +19,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
/**
@@ -69,7 +68,7 @@ public class GeoJsonGeometryCollection implements GeoJson<Iterable<GeoJson<?>>>
}
@Override
public boolean equals(@Nullable Object o) {
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
@@ -21,7 +21,6 @@ import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.jspecify.annotations.Nullable;
import org.springframework.data.geo.Point;
import org.springframework.util.Assert;
@@ -123,7 +122,7 @@ public class GeoJsonLineString implements GeoJson<Iterable<Point>> {
}
@Override
public boolean equals(@Nullable Object o) {
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
@@ -21,7 +21,6 @@ import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.jspecify.annotations.Nullable;
import org.springframework.data.geo.Point;
import org.springframework.util.Assert;
@@ -82,7 +81,7 @@ public class GeoJsonMultiLineString implements GeoJson<Iterable<GeoJsonLineStrin
}
@Override
public boolean equals(@Nullable Object o) {
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
@@ -21,7 +21,6 @@ import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.jspecify.annotations.Nullable;
import org.springframework.data.geo.Point;
import org.springframework.util.Assert;
@@ -123,7 +122,7 @@ public class GeoJsonMultiPoint implements GeoJson<Iterable<Point>> {
}
@Override
public boolean equals(@Nullable Object o) {
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
@@ -19,7 +19,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
/**
@@ -63,7 +62,7 @@ public class GeoJsonMultiPolygon implements GeoJson<Iterable<GeoJsonPolygon>> {
}
@Override
public boolean equals(@Nullable Object o) {
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
@@ -18,7 +18,6 @@ package org.springframework.data.elasticsearch.core.geo;
import java.util.Arrays;
import java.util.List;
import org.jspecify.annotations.Nullable;
import org.springframework.data.geo.Point;
/**
@@ -90,7 +89,7 @@ public class GeoJsonPoint implements GeoJson<List<Double>> {
}
@Override
public boolean equals(@Nullable Object o) {
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
@@ -21,7 +21,6 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.jspecify.annotations.Nullable;
import org.springframework.data.geo.Point;
import org.springframework.util.Assert;
@@ -208,7 +207,7 @@ public class GeoJsonPolygon implements GeoJson<Iterable<GeoJsonLineString>> {
}
@Override
public boolean equals(@Nullable Object o) {
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
@@ -17,7 +17,6 @@ package org.springframework.data.elasticsearch.core.geo;
import java.util.Objects;
import org.jspecify.annotations.Nullable;
import org.springframework.data.geo.Point;
/**
@@ -64,7 +63,7 @@ public class GeoPoint {
}
@Override
public boolean equals(@Nullable Object o) {
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
@@ -1,18 +1,2 @@
/*
* Copyright 2022-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.
*/
@org.jspecify.annotations.NullMarked
package org.springframework.data.elasticsearch.core.geo;
@@ -27,7 +27,7 @@ import org.springframework.util.Assert;
*/
public class AliasActionParameters {
private final String[] indices;
private final String @Nullable [] aliases;
@Nullable private final String[] aliases;
@Nullable private final Query filterQuery;
@Nullable private final Class<?> filterQueryClass;
@Nullable private final Boolean isHidden;
@@ -36,7 +36,7 @@ public class AliasActionParameters {
@Nullable private final String indexRouting;
@Nullable private final String searchRouting;
private AliasActionParameters(String[] indices, String @Nullable [] aliases, @Nullable Boolean isHidden,
private AliasActionParameters(String[] indices, @Nullable String[] aliases, @Nullable Boolean isHidden,
@Nullable Boolean isWriteIndex, @Nullable String routing, @Nullable String indexRouting,
@Nullable String searchRouting, @Nullable Query filterQuery, @Nullable Class<?> filterQueryClass) {
this.indices = indices;
@@ -66,7 +66,7 @@ public class AliasActionParameters {
return indices;
}
public String @Nullable [] getAliases() {
public String@Nullable[] getAliases() {
return aliases;
}
@@ -38,7 +38,7 @@ public class AliasActions {
*
* @param actions {@link AliasAction} elements
*/
public AliasActions(AliasAction @Nullable... actions) {
public AliasActions(@Nullable AliasAction... actions) {
add(actions);
}
@@ -52,9 +52,10 @@ public class AliasActions {
* @param actions elements to add
* @return this object
*/
public AliasActions add(AliasAction @Nullable... actions) {
public AliasActions add(@Nullable AliasAction... actions) {
if (actions != null) {
// noinspection NullableProblems
this.actions.addAll(Arrays.asList(actions));
}
@@ -40,7 +40,7 @@ public record ComponentTemplateRequestData(@Nullable Settings settings, @Nullabl
@Nullable private AliasActions aliasActions;
@Nullable private Boolean allowAutoCreate;
public Builder withSettings(Map<String, @Nullable Object> settings) {
public Builder withSettings(Map<String, Object> settings) {
this.settings = new Settings(settings);
return this;
}
@@ -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,13 +18,6 @@ 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;
@@ -36,7 +29,9 @@ import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.annotation.Transient;
import org.springframework.data.core.TypeInformation;
@@ -53,6 +48,13 @@ 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
@@ -110,15 +112,9 @@ public class MappingBuilder {
protected final ElasticsearchConverter elasticsearchConverter;
private final ObjectMapper objectMapper = new ObjectMapper();
private final MappingParametersCustomizer customizer;
public MappingBuilder(ElasticsearchConverter elasticsearchConverter) {
this(elasticsearchConverter, MappingParameters::from);
}
public MappingBuilder(ElasticsearchConverter elasticsearchConverter, MappingParametersCustomizer customizer) {
this.elasticsearchConverter = elasticsearchConverter;
this.customizer = customizer;
}
/**
@@ -188,7 +184,7 @@ public class MappingBuilder {
if (!excludeFromSource.isEmpty()) {
ObjectNode sourceNode = objectNode.putObject(SOURCE);
ArrayNode excludes = sourceNode.putArray(SOURCE_EXCLUDES);
excludeFromSource.stream().map(StringNode::new).forEach(excludes::add);
excludeFromSource.stream().map(TextNode::new).forEach(excludes::add);
}
return objectMapper.writer().writeValueAsString(objectNode);
@@ -242,7 +238,7 @@ public class MappingBuilder {
if (mappingAnnotation.dynamicDateFormats().length > 0) {
objectNode.putArray(DYNAMIC_DATE_FORMATS).addAll(Arrays.stream(mappingAnnotation.dynamicDateFormats())
.map(StringNode::valueOf).collect(Collectors.toList()));
.map(TextNode::valueOf).collect(Collectors.toList()));
}
if (runtimeFields != null) {
@@ -279,7 +275,7 @@ public class MappingBuilder {
writeTypeHintMapping(propertiesNode);
if (entity != null) {
entity.doWithProperties((PropertyHandler<ElasticsearchPersistentProperty>) property -> {
entity.doWithProperties((PropertyHandler<@NonNull ElasticsearchPersistentProperty>) property -> {
try {
if (property.isAnnotationPresent(Transient.class) || isInIgnoreFields(property, parentFieldAnnotation)) {
return;
@@ -550,7 +546,7 @@ public class MappingBuilder {
if (children.length > 1) {
relationsNode.putArray(parent)
.addAll(Arrays.stream(children).map(StringNode::valueOf).collect(Collectors.toList()));
.addAll(Arrays.stream(children).map(TextNode::valueOf).collect(Collectors.toList()));
} else if (children.length == 1) {
relationsNode.put(parent, children[0]);
}
@@ -593,7 +589,7 @@ public class MappingBuilder {
private void addFieldMappingParameters(ObjectNode fieldNode, Annotation annotation, boolean nestedOrObjectField)
throws IOException {
MappingParameters mappingParameters = customizer.from(annotation);
MappingParameters mappingParameters = MappingParameters.from(annotation);
if (!nestedOrObjectField && mappingParameters.isStore()) {
fieldNode.put(FIELD_PARAM_STORE, true);
@@ -15,9 +15,6 @@
*/
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;
@@ -31,12 +28,13 @@ 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
* {@link org.springframework.data.elasticsearch.annotations.InnerField} annotation. The class allows extensibility
* (non-final) to simplify mapping parameters customization, provided by
* {@link org.springframework.data.elasticsearch.core.index.MappingParametersCustomizer}.
* {@link org.springframework.data.elasticsearch.annotations.InnerField} annotation.
*
* @author Peter-Josef Meisch
* @author Aleksei Arsenev
@@ -44,10 +42,9 @@ import org.springframework.util.StringUtils;
* @author Morgan Lutz
* @author Sascha Woo
* @author Haibo Liu
* @author Andriy Redko
* @since 4.0
*/
public class MappingParameters {
public final class MappingParameters {
static final String FIELD_PARAM_COERCE = "coerce";
static final String FIELD_PARAM_COPY_TO = "copy_to";
@@ -89,7 +86,7 @@ public class MappingParameters {
private final String analyzer;
private final boolean coerce;
private final String @Nullable [] copyTo;
@Nullable private final String[] copyTo;
private final DateFormat[] dateFormats;
private final String[] dateFormatPatterns;
private final boolean docValues;
@@ -140,7 +137,7 @@ public class MappingParameters {
}
}
protected MappingParameters(Field field) {
private MappingParameters(Field field) {
index = field.index();
store = field.store();
fielddata = field.fielddata();
@@ -187,7 +184,7 @@ public class MappingParameters {
eagerGlobalOrdinals = field.eagerGlobalOrdinals();
}
protected MappingParameters(InnerField field) {
private MappingParameters(InnerField field) {
index = field.index();
store = field.store();
fielddata = field.fielddata();
@@ -288,7 +285,7 @@ public class MappingParameters {
if (copyTo != null && copyTo.length > 0) {
objectNode.putArray(FIELD_PARAM_COPY_TO)
.addAll(Arrays.stream(copyTo).map(StringNode::valueOf).collect(Collectors.toList()));
.addAll(Arrays.stream(copyTo).map(TextNode::valueOf).collect(Collectors.toList()));
}
if (ignoreAbove != null) {
@@ -420,132 +417,4 @@ public class MappingParameters {
objectNode.put(FIELD_PARAM_EAGER_GLOBAL_ORDINALS, eagerGlobalOrdinals);
}
}
protected String analyzer() {
return analyzer;
}
protected boolean coerce() {
return coerce;
}
protected String @Nullable [] copyTo() {
return copyTo;
}
protected DateFormat[] dateFormats() {
return dateFormats;
}
protected String[] dateFormatPatterns() {
return dateFormatPatterns;
}
protected boolean hasDocValues() {
return docValues;
}
protected boolean hasEagerGlobalOrdinals() {
return eagerGlobalOrdinals;
}
protected boolean isEnabled() {
return enabled;
}
protected boolean hasFielddata() {
return fielddata;
}
protected Integer ignoreAbove() {
return ignoreAbove;
}
protected boolean isIgnoreMalformed() {
return ignoreMalformed;
}
protected boolean isIndex() {
return index;
}
protected IndexOptions indexOptions() {
return indexOptions;
}
protected boolean isIndexPhrases() {
return indexPhrases;
}
protected IndexPrefixes indexPrefixes() {
return indexPrefixes;
}
protected String normalizer() {
return normalizer;
}
protected boolean hasNorms() {
return norms;
}
protected Integer maxShingleSize() {
return maxShingleSize;
}
protected String nullValue() {
return nullValue;
}
protected NullValueType nullValueType() {
return nullValueType;
}
protected Integer positionIncrementGap() {
return positionIncrementGap;
}
protected boolean positiveScoreImpact() {
return positiveScoreImpact;
}
protected Integer dims() {
return dims;
}
protected String elementType() {
return elementType;
}
protected KnnSimilarity knnSimilarity() {
return knnSimilarity;
}
protected KnnIndexOptions knnIndexOptions() {
return knnIndexOptions;
}
protected String searchAnalyzer() {
return searchAnalyzer;
}
protected double scalingFactor() {
return scalingFactor;
}
protected String similarity() {
return similarity;
}
protected TermVector termVector() {
return termVector;
}
protected FieldType type() {
return type;
}
protected String mappedTypeName() {
return mappedTypeName;
}
}
@@ -1,36 +0,0 @@
/*
* Copyright 2026-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.core.index;
import java.lang.annotation.Annotation;
/**
* Allows to customize {@link org.springframework.data.elasticsearch.core.index.MappingParameters} that are being
* emitted for each supported annotation. Needed by dependent projects like Spring-Data-Opensearch.
*
* @author Andriy Redko
* @since 6.1.0
*/
public interface MappingParametersCustomizer {
/**
* Customize @link org.springframework.data.elasticsearch.core.index.MappingParameters} for each supported annotation.
*
* @param annotation supported annotation
* @return customized @link org.springframework.data.elasticsearch.core.index.MappingParameters}
*/
MappingParameters from(Annotation annotation);
}
@@ -102,7 +102,7 @@ public class PutTemplateRequest {
this.indexPatterns = indexPatterns;
}
public TemplateRequestBuilder withSettings(Map<String, @Nullable Object> settings) {
public TemplateRequestBuilder withSettings(Map<String, Object> settings) {
this.settings = new Settings(settings);
return this;
}
@@ -31,7 +31,6 @@ import org.springframework.data.mapping.MappingException;
* Subclass of {@link MappingBuilder} with specialized methods To inhibit blocking calls
*
* @author Peter-Josef Meisch
* @author Andriy Redko
* @since 4.3
*/
public class ReactiveMappingBuilder extends MappingBuilder {
@@ -40,10 +39,6 @@ public class ReactiveMappingBuilder extends MappingBuilder {
super(elasticsearchConverter);
}
public ReactiveMappingBuilder(ElasticsearchConverter elasticsearchConverter, MappingParametersCustomizer customizer) {
super(elasticsearchConverter, customizer);
}
@Override
public String buildPropertyMapping(Class<?> clazz) throws MappingException {
throw new UnsupportedOperationException(
@@ -21,7 +21,6 @@ import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jspecify.annotations.Nullable;
import org.springframework.data.elasticsearch.support.DefaultStringObjectMap;
import org.springframework.util.Assert;
@@ -35,7 +34,7 @@ public class Settings extends DefaultStringObjectMap<Settings> {
public Settings() {}
public Settings(Map<String, @Nullable Object> map) {
public Settings(Map<String, Object> map) {
super(map);
}
@@ -55,7 +54,7 @@ public class Settings extends DefaultStringObjectMap<Settings> {
}
@Override
public Object get(@Nullable Object key) {
public Object get(Object key) {
return containsKey(key) ? super.get(key) : path(key.toString());
}
@@ -76,11 +75,11 @@ public class Settings extends DefaultStringObjectMap<Settings> {
* taken from https://stackoverflow.com/a/29698326/4393565
*/
@SuppressWarnings("unchecked")
private static Map<?, ?> deepMerge(Map<String, @Nullable Object> original, Map<String, @Nullable Object> newMap) {
private static Map<?, ?> deepMerge(Map<String, Object> original, Map<String, Object> newMap) {
for (Object key : newMap.keySet()) {
if (newMap.get(key) instanceof Map && original.get(key) instanceof Map) {
Map<String, @Nullable Object> originalChild = (Map<String, Object>) original.get(key);
Map<String, @Nullable Object> newChild = (Map<String, Object>) newMap.get(key);
Map<String, Object> originalChild = (Map<String, Object>) original.get(key);
Map<String, Object> newChild = (Map<String, Object>) newMap.get(key);
original.put(key.toString(), deepMerge(originalChild, newChild));
} else if (newMap.get(key) instanceof List && original.get(key) instanceof List) {
List<Object> originalChild = (List<Object>) original.get(key);
@@ -113,9 +112,9 @@ public class Settings extends DefaultStringObjectMap<Settings> {
* flattens a Map<String, Object> to a stream of Map.Entry objects where the keys are the dot separated concatenated
* keys of sub map entries
*/
static private Stream<Map.Entry<String, @Nullable Object>> doFlatten(Map.Entry<String, @Nullable Object> entry) {
static private Stream<Map.Entry<String, Object>> doFlatten(Map.Entry<String, Object> entry) {
if (entry.getValue() instanceof Map<?, ?> nested) {
if (entry.getValue()instanceof Map<?, ?> nested) {
// noinspection unchecked
return nested.entrySet().stream() //
@@ -91,7 +91,7 @@ public class TemplateData {
return this;
}
public TemplateDataBuilder withSettings(Map<String, @Nullable Object> settings) {
public TemplateDataBuilder withSettings(Map<String, Object> settings) {
this.settings = new Settings(settings);
return this;
}
@@ -1,18 +1,5 @@
/*
* Copyright 2022-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.
/**
* Classes related to Elasticsearch index management.
*/
@org.jspecify.annotations.NullMarked
package org.springframework.data.elasticsearch.core.index;
@@ -64,7 +64,7 @@ public class JoinField<ID> {
}
@Override
public boolean equals(@Nullable Object obj) {
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
@@ -1,18 +0,0 @@
/*
* Copyright 2022-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.
*/
@org.jspecify.annotations.NullMarked
package org.springframework.data.elasticsearch.core.join;
@@ -115,7 +115,7 @@ public class Alias {
}
@Override
public boolean equals(@Nullable Object o) {
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof Alias that))
@@ -33,7 +33,7 @@ public class CreateIndexSettings {
private final IndexCoordinates indexCoordinates;
private final Set<Alias> aliases;
@Nullable private final Map<String, @Nullable Object> settings;
@Nullable private final Map<String, Object> settings;
@Nullable private final Document mapping;
@@ -58,7 +58,7 @@ public class CreateIndexSettings {
}
@Nullable
public Map<String, @Nullable Object> getSettings() {
public Map<String, Object> getSettings() {
return settings;
}
@@ -71,7 +71,7 @@ public class CreateIndexSettings {
private final IndexCoordinates indexCoordinates;
private final Set<Alias> aliases = new HashSet<>();
@Nullable private Map<String, @Nullable Object> settings;
@Nullable private Map<String, Object> settings;
@Nullable private Document mapping;
@@ -94,7 +94,7 @@ public class CreateIndexSettings {
return this;
}
public Builder withSettings(Map<String, @Nullable Object> settings) {
public Builder withSettings(Map<String, Object> settings) {
Assert.notNull(settings, "settings must not be null");
this.settings = settings;
@@ -17,7 +17,6 @@ package org.springframework.data.elasticsearch.core.mapping;
import java.util.Arrays;
import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
/**
@@ -56,7 +55,7 @@ public class IndexCoordinates {
* @since 4.2
*/
@Override
public boolean equals(@Nullable Object o) {
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
@@ -23,7 +23,9 @@ import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.core.TypeInformation;
@@ -261,7 +263,6 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
throw new MappingException("@IndexedIndexName annotation must be put on String property");
}
// noinspection VariableNotUsedInsideIf
if (indexedIndexNameProperty != null) {
throw new MappingException(
"@IndexedIndexName annotation can only be put on one property in an entity");
@@ -300,7 +301,7 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
return fieldNamePropertyCache.computeIfAbsent(fieldName, key -> {
AtomicReference<@Nullable ElasticsearchPersistentProperty> propertyRef = new AtomicReference<>();
doWithProperties((PropertyHandler<ElasticsearchPersistentProperty>) property -> {
doWithProperties((PropertyHandler<@NonNull ElasticsearchPersistentProperty>) property -> {
if (key.equals(property.getFieldName())) {
propertyRef.set(property);
}
@@ -424,7 +425,8 @@ public class SimpleElasticsearchPersistentEntity<T> extends BasicPersistentEntit
try {
Expression expression = routingExpressions.computeIfAbsent(routing, PARSER::parseExpression);
ExpressionDependencies expressionDependencies = ExpressionDependencies.discover(expression);
ExpressionDependencies expressionDependencies = expression != null ? ExpressionDependencies.discover(expression)
: ExpressionDependencies.none();
EvaluationContext context = getEvaluationContext(null, expressionDependencies);
context.setVariable("entity", bean);
@@ -1,18 +1,2 @@
/*
* Copyright 2022-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.
*/
@org.jspecify.annotations.NullMarked
package org.springframework.data.elasticsearch.core.mapping;
@@ -1,18 +1,2 @@
/*
* Copyright 2022-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.
*/
@org.jspecify.annotations.NullMarked
package org.springframework.data.elasticsearch.core;
@@ -81,7 +81,6 @@ public class BaseQuery implements Query {
protected List<IdWithRouting> idsWithRouting = Collections.emptyList();
protected List<RuntimeField> runtimeFields = new ArrayList<>();
@Nullable protected PointInTime pointInTime;
@Nullable protected Boolean includeNamedQueriesScore;
private boolean queryIsUpdatedByConverter = false;
@Nullable private Integer reactiveBatchSize = null;
@Nullable private Boolean allowNoIndices = null;
@@ -124,7 +123,6 @@ public class BaseQuery implements Query {
this.docValueFields = builder.getDocValueFields();
this.scriptedFields = builder.getScriptedFields();
this.runtimeFields = builder.getRuntimeFields();
this.includeNamedQueriesScore = builder.getIncludeNamedQueriesScore();
}
/**
@@ -457,14 +455,6 @@ public class BaseQuery implements Query {
this.requestCache = value;
}
/**
* @since 6.1
*/
@Override
public void setIncludeNamedQueriesScore(@Nullable Boolean value) {
this.includeNamedQueriesScore = value;
}
@Override
@Nullable
public Boolean getRequestCache() {
@@ -490,14 +480,6 @@ public class BaseQuery implements Query {
return indicesBoost;
}
/**
* @since 6.1
*/
@Override
public @Nullable Boolean getIncludeNamedQueriesScore() {
return this.includeNamedQueriesScore;
}
/**
* @since 5.0
*/
@@ -602,7 +584,6 @@ public class BaseQuery implements Query {
// searchForStream to work correctly (#3098) as there the page size defines what is
// returned in a single request, and the max result determines the total number of
// documents returned.
// noinspection DataFlowIssue maxResults is not null here, this is checked with isLimiting()
requestSize = Math.min(pageable.getPageSize(), getMaxResults());
}
} else if (pageable == UNSET_PAGE) {
@@ -623,7 +604,6 @@ public class BaseQuery implements Query {
// searchForStream to work correctly (#3098) as there the page size defines what is
// returned in a single request, and the max result determines the total number of
// documents returned.
// noinspection DataFlowIssue maxResults is not null here, this is checked with isLimiting()
requestSize = Math.min(INDEX_MAX_RESULT_WINDOW, getMaxResults());
}
}
@@ -71,7 +71,6 @@ public abstract class BaseQueryBuilder<Q extends BaseQuery, SELF extends BaseQue
@Nullable Integer reactiveBatchSize;
private final List<DocValueField> docValueFields = new ArrayList<>();
private final List<ScriptedField> scriptedFields = new ArrayList<>();
@Nullable private Boolean includeNamedQueryScore;
@Nullable
public Sort getSort() {
@@ -97,6 +96,7 @@ public abstract class BaseQueryBuilder<Q extends BaseQuery, SELF extends BaseQue
return maxResults;
}
@Nullable
public Collection<String> getIds() {
return ids;
}
@@ -177,14 +177,6 @@ public abstract class BaseQueryBuilder<Q extends BaseQuery, SELF extends BaseQue
return requestCache;
}
/**
* @since 6.1
*/
@Nullable
public Boolean getIncludeNamedQueriesScore() {
return includeNamedQueryScore;
}
public List<Query.IdWithRouting> getIdsWithRouting() {
return idsWithRouting;
}
@@ -389,14 +381,6 @@ public abstract class BaseQueryBuilder<Q extends BaseQuery, SELF extends BaseQue
return self();
}
/**
* @since 6.1
*/
public SELF withIncludeNamedQueryScore(@Nullable Boolean namedQueryScore) {
this.includeNamedQueryScore = namedQueryScore;
return self();
}
/**
* Set Ids with routing values for a multi-get request run with this query. Not used in any other searches.
*
@@ -61,16 +61,16 @@ public class Criteria {
private float boost = Float.NaN;
private boolean negating = false;
// we cache this and recalculate when properties used in equals change
// see https://github.com/spring-projects/spring-data-elasticsearch/issues/3083
private int hashCode;
// we cash this and recalculate when properties used in equals change
// see https://github.com/spring-projects/spring-data-elasticsearch/issues/3083
private int hashCode;
private final CriteriaChain criteriaChain = new CriteriaChain();
private final Set<CriteriaEntry> queryCriteriaEntries = new LinkedHashSet<>();
private final Set<CriteriaEntry> filterCriteriaEntries = new LinkedHashSet<>();
private final Set<Criteria> subCriteria = new LinkedHashSet<>();
private final CriteriaChain criteriaChain = new CriteriaChain();
private final Set<CriteriaEntry> queryCriteriaEntries = new LinkedHashSet<>();
private final Set<CriteriaEntry> filterCriteriaEntries = new LinkedHashSet<>();
private final Set<Criteria> subCriteria = new LinkedHashSet<>();
// region criteria creation
// region criteria creation
/**
* @return factory method to create an and-Criteria that is not bound to a field
@@ -89,8 +89,8 @@ public class Criteria {
}
public Criteria() {
recalculateHashCode();
}
recalculateHashCode();
}
/**
* Creates a new Criteria with provided field name
@@ -113,7 +113,7 @@ public class Criteria {
this.field = field;
this.criteriaChain.add(this);
recalculateHashCode();
recalculateHashCode();
}
/**
@@ -143,7 +143,7 @@ public class Criteria {
this.field = field;
this.criteriaChain.addAll(criteriaChain);
this.criteriaChain.add(this);
recalculateHashCode();
recalculateHashCode();
}
/**
@@ -197,7 +197,7 @@ public class Criteria {
*/
public Criteria not() {
this.negating = true;
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -216,7 +216,7 @@ public class Criteria {
Assert.isTrue(boost >= 0, "boost must not be negative");
this.boost = boost;
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -274,7 +274,7 @@ public class Criteria {
Assert.notNull(criteria, "Cannot chain 'null' criteria.");
this.criteriaChain.add(criteria);
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -289,7 +289,7 @@ public class Criteria {
Assert.notNull(criterias, "Cannot chain 'null' criterias.");
this.criteriaChain.addAll(Arrays.asList(criterias));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -332,7 +332,7 @@ public class Criteria {
orCriteria.subCriteria.addAll(criteria.subCriteria);
orCriteria.boost = criteria.boost;
orCriteria.negating = criteria.isNegating();
orCriteria.recalculateHashCode();
orCriteria.recalculateHashCode();
return orCriteria;
}
@@ -348,7 +348,7 @@ public class Criteria {
Assert.notNull(criteria, "criteria must not be null");
subCriteria.add(criteria);
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -363,7 +363,7 @@ public class Criteria {
*/
public Criteria is(Object o) {
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.EQUALS, o));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -375,7 +375,7 @@ public class Criteria {
*/
public Criteria exists() {
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.EXISTS));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -394,7 +394,7 @@ public class Criteria {
}
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.BETWEEN, new Object[] { lowerBound, upperBound }));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -410,7 +410,7 @@ public class Criteria {
assertNoBlankInWildcardQuery(s, false, true);
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.STARTS_WITH, s));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -427,7 +427,7 @@ public class Criteria {
assertNoBlankInWildcardQuery(s, true, true);
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.CONTAINS, s));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -444,7 +444,7 @@ public class Criteria {
assertNoBlankInWildcardQuery(s, true, false);
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.ENDS_WITH, s));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -472,7 +472,7 @@ public class Criteria {
Assert.notNull(values, "Collection of 'in' values must not be null");
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.IN, values));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -499,7 +499,7 @@ public class Criteria {
Assert.notNull(values, "Collection of 'NotIn' values must not be null");
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.NOT_IN, values));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -512,7 +512,7 @@ public class Criteria {
*/
public Criteria expression(String s) {
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.EXPRESSION, s));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -524,7 +524,7 @@ public class Criteria {
*/
public Criteria fuzzy(String s) {
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.FUZZY, s));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -539,7 +539,7 @@ public class Criteria {
Assert.notNull(upperBound, "upperBound must not be null");
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.LESS_EQUAL, upperBound));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -554,7 +554,7 @@ public class Criteria {
Assert.notNull(upperBound, "upperBound must not be null");
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.LESS, upperBound));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -569,7 +569,7 @@ public class Criteria {
Assert.notNull(lowerBound, "lowerBound must not be null");
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.GREATER_EQUAL, lowerBound));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -584,7 +584,7 @@ public class Criteria {
Assert.notNull(lowerBound, "lowerBound must not be null");
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.GREATER, lowerBound));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -601,7 +601,7 @@ public class Criteria {
Assert.notNull(value, "value must not be null");
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.MATCHES, value));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -618,7 +618,7 @@ public class Criteria {
Assert.notNull(value, "value must not be null");
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.MATCHES_ALL, value));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -631,7 +631,7 @@ public class Criteria {
public Criteria empty() {
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.EMPTY));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -644,7 +644,7 @@ public class Criteria {
public Criteria notEmpty() {
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.NOT_EMPTY));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -660,7 +660,7 @@ public class Criteria {
Assert.notNull(value, "value must not be null");
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.REGEXP, value));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -679,7 +679,7 @@ public class Criteria {
Assert.notNull(boundingBox, "boundingBox value for boundedBy criteria must not be null");
filterCriteriaEntries.add(new CriteriaEntry(OperationKey.BBOX, new Object[] { boundingBox }));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -696,7 +696,7 @@ public class Criteria {
filterCriteriaEntries
.add(new CriteriaEntry(OperationKey.BBOX, new Object[] { boundingBox.getFirst(), boundingBox.getSecond() }));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -714,7 +714,7 @@ public class Criteria {
filterCriteriaEntries
.add(new CriteriaEntry(OperationKey.BBOX, new Object[] { topLeftGeohash, bottomRightGeohash }));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -731,7 +731,7 @@ public class Criteria {
Assert.notNull(bottomRightPoint, "bottomRightPoint must not be null");
filterCriteriaEntries.add(new CriteriaEntry(OperationKey.BBOX, new Object[] { topLeftPoint, bottomRightPoint }));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -749,7 +749,7 @@ public class Criteria {
filterCriteriaEntries.add(new CriteriaEntry(OperationKey.BBOX,
new Object[] { GeoPoint.fromPoint(topLeftPoint), GeoPoint.fromPoint(bottomRightPoint) }));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -767,7 +767,7 @@ public class Criteria {
Assert.notNull(location, "Distance value for near criteria must not be null");
filterCriteriaEntries.add(new CriteriaEntry(OperationKey.WITHIN, new Object[] { location, distance }));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -784,7 +784,7 @@ public class Criteria {
Assert.notNull(location, "Distance value for near criteria must not be null");
filterCriteriaEntries.add(new CriteriaEntry(OperationKey.WITHIN, new Object[] { location, distance }));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -801,7 +801,7 @@ public class Criteria {
Assert.isTrue(StringUtils.hasLength(geoLocation), "geoLocation value must not be null");
filterCriteriaEntries.add(new CriteriaEntry(OperationKey.WITHIN, new Object[] { geoLocation, distance }));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -816,7 +816,7 @@ public class Criteria {
Assert.notNull(geoShape, "geoShape must not be null");
filterCriteriaEntries.add(new CriteriaEntry(OperationKey.GEO_INTERSECTS, geoShape));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -831,7 +831,7 @@ public class Criteria {
Assert.notNull(geoShape, "geoShape must not be null");
filterCriteriaEntries.add(new CriteriaEntry(OperationKey.GEO_IS_DISJOINT, geoShape));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -845,7 +845,7 @@ public class Criteria {
Assert.notNull(geoShape, "geoShape must not be null");
filterCriteriaEntries.add(new CriteriaEntry(OperationKey.GEO_WITHIN, geoShape));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -859,7 +859,7 @@ public class Criteria {
Assert.notNull(geoShape, "geoShape must not be null");
filterCriteriaEntries.add(new CriteriaEntry(OperationKey.GEO_CONTAINS, geoShape));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -873,7 +873,7 @@ public class Criteria {
Assert.notNull(query, "has_child query must not be null.");
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.HAS_CHILD, query));
recalculateHashCode();
recalculateHashCode();
return this;
}
@@ -887,7 +887,7 @@ public class Criteria {
Assert.notNull(query, "has_parent query must not be null.");
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.HAS_PARENT, query));
recalculateHashCode();
recalculateHashCode();
return this;
}
// endregion
@@ -909,7 +909,7 @@ public class Criteria {
// region equals/hashcode
@Override
public boolean equals(@Nullable Object o) {
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
@@ -937,19 +937,19 @@ public class Criteria {
return hashCode;
}
private void recalculateHashCode() {
int result = field != null ? field.hashCode() : 0;
result = 31 * result + (boost != +0.0f ? Float.floatToIntBits(boost) : 0);
result = 31 * result + (negating ? 1 : 0);
// the criteriaChain contains "this" object, so we need to filter it out
// to avoid a stackoverflow here, because the hashcode implementation
// uses the element's hashcodes
result = 31 * result + criteriaChain.filter(this).hashCode();
result = 31 * result + queryCriteriaEntries.hashCode();
result = 31 * result + filterCriteriaEntries.hashCode();
result = 31 * result + subCriteria.hashCode();
this.hashCode = result;
}
private void recalculateHashCode() {
int result = field != null ? field.hashCode() : 0;
result = 31 * result + (boost != +0.0f ? Float.floatToIntBits(boost) : 0);
result = 31 * result + (negating ? 1 : 0);
// the criteriaChain contains "this" object, so we need to filter it out
// to avoid a stackoverflow here, because the hashcode implementation
// uses the element's hashcodes
result = 31 * result + criteriaChain.filter(this).hashCode();
result = 31 * result + queryCriteriaEntries.hashCode();
result = 31 * result + filterCriteriaEntries.hashCode();
result = 31 * result + subCriteria.hashCode();
this.hashCode = result;
}
// endregion
@Override
@@ -1141,7 +1141,7 @@ public class Criteria {
}
@Override
public boolean equals(@Nullable Object o) {
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())

Some files were not shown because too many files have changed in this diff Show More