Compare commits
180 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9e85d0f27c | |||
| 535cc84df5 | |||
| 3647fc6463 | |||
| bdb7ca03c6 | |||
| 200a18b4b1 | |||
| d0f7f79968 | |||
| 027ff29436 | |||
| 69d80bd602 | |||
| f7a788ce2d | |||
| 230616a3c3 | |||
| 44ae2b2af5 | |||
| e934a2eea8 | |||
| 468b573b77 | |||
| 9a442df5b9 | |||
| 7b96041bc3 | |||
| ac73ba4774 | |||
| fa7a285606 | |||
| 939179ced4 | |||
| a45281b1fd | |||
| 474ff84d6e | |||
| c58e3b3a8f | |||
| 4ed7f96919 | |||
| 776a30af75 | |||
| c92480b97d | |||
| 6e8357caa7 | |||
| 541faff07f | |||
| 9aeac006d1 | |||
| 6e52b97b76 | |||
| c7550e8d82 | |||
| c17159ce1c | |||
| 0a1d10f8b4 | |||
| ef0e47c6bb | |||
| 7e2ebb299c | |||
| 594566a44a | |||
| bf3248b536 | |||
| c59bb0b434 | |||
| d93d1e0738 | |||
| aebdc8f86b | |||
| 2be27593d6 | |||
| aa22d8239d | |||
| 6746bc5278 | |||
| a6cb959605 | |||
| e04905a1a7 | |||
| 834b10f578 | |||
| 823cfa919a | |||
| ab29ae4219 | |||
| 62fb89208a | |||
| e8c3badc56 | |||
| 187c9bbd5d | |||
| e1a5811406 | |||
| b643669d36 | |||
| f66af53480 | |||
| 14099970bb | |||
| 0ab253422f | |||
| 0d2a6b98e8 | |||
| 30602496bd | |||
| 95762b4fde | |||
| 0771e90031 | |||
| 6729330500 | |||
| 8796292611 | |||
| f3f9ca4002 | |||
| 083a38ed57 | |||
| 4f3aa52958 | |||
| 3256a2bfe0 | |||
| 95401a5bd7 | |||
| 12cd64cfc8 | |||
| 7e557317d1 | |||
| c9846ab8ad | |||
| 08f7ad85ca | |||
| 4db496ca4e | |||
| 9353de13ee | |||
| 5717d6ee57 | |||
| bbee64c8a4 | |||
| 5cf38d064f | |||
| 8894dd3d21 | |||
| f1b4a54bc2 | |||
| c37bbd5a7e | |||
| 2b5ab64724 | |||
| 0d943069e3 | |||
| ceadec0a3c | |||
| 9b2ff56dad | |||
| d221fa8c49 | |||
| c4fd0acbc6 | |||
| 288705ca72 | |||
| 59fdbbeb19 | |||
| 4a8e012e04 | |||
| 2450d579e9 | |||
| 5e4ce56414 | |||
| b8f8a60fd7 | |||
| 7ae55b9e75 | |||
| 464fc31d87 | |||
| 175e7b51ae | |||
| b8ae9b4a83 | |||
| 53581a9a79 | |||
| d9b23ede70 | |||
| d1528ed67f | |||
| c436c4cd63 | |||
| 8907be835f | |||
| 1d291aeeff | |||
| 56b1343ee7 | |||
| bf165a663a | |||
| 35b6efebf3 | |||
| 3128fab595 | |||
| fa66702980 | |||
| 8ab84fcc7a | |||
| 7c35e5327e | |||
| 6941e31ba4 | |||
| 3b8f0c9d56 | |||
| 64624aec70 | |||
| e71758686c | |||
| 35c7ef4fed | |||
| 305d930870 | |||
| e688fc70e0 | |||
| 50f2d83442 | |||
| fd3410bb75 | |||
| 325d3f7a09 | |||
| 655db1e362 | |||
| 3eac1bb173 | |||
| 36b449c385 | |||
| 1c8e0e03d3 | |||
| d80d920a57 | |||
| e6869bcdfd | |||
| d2e3ea26b8 | |||
| f74dd879df | |||
| 3b921b7454 | |||
| fa6f636906 | |||
| d3e8c9fce5 | |||
| d88fb037da | |||
| 7c35756923 | |||
| 4f7e7526e3 | |||
| c470a3d4eb | |||
| c79b6d158a | |||
| bf248d78de | |||
| 8e3d8669ea | |||
| 039e59d3c2 | |||
| 27094724dc | |||
| 567bdf21fa | |||
| 7c340bc9a2 | |||
| 271e1eee0d | |||
| 66d13444aa | |||
| 6f84a1c589 | |||
| 112ca59c95 | |||
| 4ce1137b4e | |||
| a16a87f3fa | |||
| 3871d2d073 | |||
| e80737f76d | |||
| bc4c913a97 | |||
| b515f18b33 | |||
| 38dc7fb0fb | |||
| 6b0c4281a4 | |||
| 44e7ab63b0 | |||
| 67d084beea | |||
| 7582617a26 | |||
| e8f73b75ba | |||
| 5ed655e0aa | |||
| 0836411d45 | |||
| 25b323c00d | |||
| 33bc36d111 | |||
| a830e76807 | |||
| 06f2103c2e | |||
| e96d09fa51 | |||
| 38b1763b34 | |||
| df0d65eda2 | |||
| 3a900599f2 | |||
| be93ebd6a6 | |||
| 159687e241 | |||
| 502ce0b6aa | |||
| 5b6789539c | |||
| 775bf66401 | |||
| f8fbf7721a | |||
| a2ca312fb2 | |||
| 91742b1114 | |||
| 8b7f0f8327 | |||
| 84b441eadc | |||
| e193e1672b | |||
| 79087c4ada | |||
| 7ace63485d | |||
| 19ec481856 | |||
| 728ba0af5b | |||
| 054235e590 |
@@ -1,12 +1,18 @@
|
||||
<!--
|
||||
|
||||
Thank you for proposing a pull request. This template will guide you through the essential steps necessary for a pull request.
|
||||
|
||||
When contributing, please make sure an issue exists in issue tracker and comment on this issue with how you want to address it. By this we not only know that someone is working on an issue, we can also align architectural questions and possible solutions before work is invested . We so can prevent that much work is put into Pull Requests that have little or no chances of being merged.
|
||||
|
||||
Make sure that:
|
||||
|
||||
-->
|
||||
|
||||
- [ ] You have read the [Spring Data contribution guidelines](https://github.com/spring-projects/spring-data-build/blob/master/CONTRIBUTING.adoc).
|
||||
- [ ] There is a ticket in the bug tracker for the project in our [issue tracker](https://github.com/spring-projects/spring-data-elasticsearch/issues).
|
||||
- [ ] **There is a ticket in the bug tracker for the project in our [issue tracker](https://github.
|
||||
com/spring-projects/spring-data-elasticsearch/issues)**. Add the issue number to the _Closes #issue-number_ line below
|
||||
- [ ] You use the code formatters provided [here](https://github.com/spring-projects/spring-data-build/tree/master/etc/ide) and have them applied to your changes. Don’t submit any formatting related changes.
|
||||
- [ ] You submit test cases (unit or integration tests) that back your changes.
|
||||
- [ ] You added yourself as author in the headers of the classes you touched. Amend the date range in the Apache license header if needed. For new types, add the license header (copy from another file and set the current year only).
|
||||
|
||||
Closes #issue-number
|
||||
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2007-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.
|
||||
*/
|
||||
import java.net.*;
|
||||
import java.io.*;
|
||||
import java.nio.channels.*;
|
||||
import java.util.Properties;
|
||||
|
||||
public class MavenWrapperDownloader {
|
||||
|
||||
private static final String WRAPPER_VERSION = "0.5.6";
|
||||
/**
|
||||
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
|
||||
*/
|
||||
private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
|
||||
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
|
||||
|
||||
/**
|
||||
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
|
||||
* use instead of the default one.
|
||||
*/
|
||||
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
|
||||
".mvn/wrapper/maven-wrapper.properties";
|
||||
|
||||
/**
|
||||
* Path where the maven-wrapper.jar will be saved to.
|
||||
*/
|
||||
private static final String MAVEN_WRAPPER_JAR_PATH =
|
||||
".mvn/wrapper/maven-wrapper.jar";
|
||||
|
||||
/**
|
||||
* Name of the property which should be used to override the default download url for the wrapper.
|
||||
*/
|
||||
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
|
||||
|
||||
public static void main(String args[]) {
|
||||
System.out.println("- Downloader started");
|
||||
File baseDirectory = new File(args[0]);
|
||||
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
|
||||
|
||||
// If the maven-wrapper.properties exists, read it and check if it contains a custom
|
||||
// wrapperUrl parameter.
|
||||
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
|
||||
String url = DEFAULT_DOWNLOAD_URL;
|
||||
if(mavenWrapperPropertyFile.exists()) {
|
||||
FileInputStream mavenWrapperPropertyFileInputStream = null;
|
||||
try {
|
||||
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
|
||||
Properties mavenWrapperProperties = new Properties();
|
||||
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
|
||||
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
|
||||
} catch (IOException e) {
|
||||
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
|
||||
} finally {
|
||||
try {
|
||||
if(mavenWrapperPropertyFileInputStream != null) {
|
||||
mavenWrapperPropertyFileInputStream.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Ignore ...
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println("- Downloading from: " + url);
|
||||
|
||||
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
|
||||
if(!outputFile.getParentFile().exists()) {
|
||||
if(!outputFile.getParentFile().mkdirs()) {
|
||||
System.out.println(
|
||||
"- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
|
||||
}
|
||||
}
|
||||
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
|
||||
try {
|
||||
downloadFileFromURL(url, outputFile);
|
||||
System.out.println("Done");
|
||||
System.exit(0);
|
||||
} catch (Throwable e) {
|
||||
System.out.println("- Error downloading");
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
|
||||
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
|
||||
String username = System.getenv("MVNW_USERNAME");
|
||||
char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
|
||||
Authenticator.setDefault(new Authenticator() {
|
||||
@Override
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(username, password);
|
||||
}
|
||||
});
|
||||
}
|
||||
URL website = new URL(urlString);
|
||||
ReadableByteChannel rbc;
|
||||
rbc = Channels.newChannel(website.openStream());
|
||||
FileOutputStream fos = new FileOutputStream(destination);
|
||||
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
|
||||
fos.close();
|
||||
rbc.close();
|
||||
}
|
||||
|
||||
}
|
||||
BIN
Binary file not shown.
+3
-1
@@ -1 +1,3 @@
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip
|
||||
#Fri Jun 03 09:42:29 CEST 2022
|
||||
wrapperUrl=https\://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
|
||||
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.5/apache-maven-3.8.5-bin.zip
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
= Continuous Integration
|
||||
|
||||
image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-elasticsearch%2Fmaster&subject=2020.0.0%20(master)[link=https://jenkins.spring.io/view/SpringData/job/spring-data-elasticsearch/]
|
||||
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/]
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
= Spring Data contribution guidelines
|
||||
|
||||
You find the contribution guidelines for Spring Data projects https://github.com/spring-projects/spring-data-build/blob/master/CONTRIBUTING.adoc[here].
|
||||
You find the contribution guidelines for Spring Data projects https://github.com/spring-projects/spring-data-build/blob/main/CONTRIBUTING.adoc[here].
|
||||
|
||||
== Running the test locally
|
||||
|
||||
|
||||
Vendored
+32
-52
@@ -1,9 +1,15 @@
|
||||
def p = [:]
|
||||
node {
|
||||
checkout scm
|
||||
p = readProperties interpolate: true, file: 'ci/pipeline.properties'
|
||||
}
|
||||
|
||||
pipeline {
|
||||
agent none
|
||||
|
||||
triggers {
|
||||
pollSCM 'H/10 * * * *'
|
||||
upstream(upstreamProjects: "spring-data-commons/2.5.x", threshold: hudson.model.Result.SUCCESS)
|
||||
upstream(upstreamProjects: "spring-data-commons/2.6.x", threshold: hudson.model.Result.SUCCESS)
|
||||
}
|
||||
|
||||
options {
|
||||
@@ -12,10 +18,11 @@ pipeline {
|
||||
}
|
||||
|
||||
stages {
|
||||
stage("test: baseline (jdk8)") {
|
||||
stage("test: baseline (main)") {
|
||||
when {
|
||||
beforeAgent(true)
|
||||
anyOf {
|
||||
branch '4.2.x'
|
||||
branch(pattern: "main|(\\d\\.\\d\\.x)", comparator: "REGEXP")
|
||||
not { triggeredBy 'UpstreamCause' }
|
||||
}
|
||||
}
|
||||
@@ -25,14 +32,14 @@ pipeline {
|
||||
options { timeout(time: 30, unit: 'MINUTES') }
|
||||
|
||||
environment {
|
||||
DOCKER_HUB = credentials('hub.docker.com-springbuildmaster')
|
||||
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
|
||||
DOCKER_HUB = credentials("${p['docker.credentials']}")
|
||||
ARTIFACTORY = credentials("${p['artifactory.credentials']}")
|
||||
}
|
||||
|
||||
steps {
|
||||
script {
|
||||
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
|
||||
docker.image('adoptopenjdk/openjdk8:latest').inside('-u root -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v $HOME:/tmp/jenkins-home') {
|
||||
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
|
||||
docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.docker']) {
|
||||
sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}"
|
||||
sh 'PROFILE=none ci/verify.sh'
|
||||
sh "ci/clean.sh"
|
||||
@@ -45,28 +52,28 @@ pipeline {
|
||||
stage("Test other configurations") {
|
||||
when {
|
||||
allOf {
|
||||
branch '4.2.x'
|
||||
branch 'main'
|
||||
not { triggeredBy 'UpstreamCause' }
|
||||
}
|
||||
}
|
||||
parallel {
|
||||
stage("test: baseline (jdk11)") {
|
||||
stage("test: baseline (next)") {
|
||||
agent {
|
||||
label 'data'
|
||||
}
|
||||
options { timeout(time: 30, unit: 'MINUTES') }
|
||||
|
||||
environment {
|
||||
DOCKER_HUB = credentials('hub.docker.com-springbuildmaster')
|
||||
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
|
||||
DOCKER_HUB = credentials("${p['docker.credentials']}")
|
||||
ARTIFACTORY = credentials("${p['artifactory.credentials']}")
|
||||
}
|
||||
|
||||
steps {
|
||||
script {
|
||||
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
|
||||
docker.image('adoptopenjdk/openjdk11:latest').inside('-u root -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v $HOME:/tmp/jenkins-home') {
|
||||
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
|
||||
docker.image(p['docker.java.next.image']).inside(p['docker.java.inside.docker']) {
|
||||
sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}"
|
||||
sh 'PROFILE=java11 ci/verify.sh'
|
||||
sh 'PROFILE=none ci/verify.sh'
|
||||
sh "ci/clean.sh"
|
||||
}
|
||||
}
|
||||
@@ -74,23 +81,23 @@ pipeline {
|
||||
}
|
||||
}
|
||||
|
||||
stage("test: baseline (jdk16)") {
|
||||
stage("test: baseline (LTS)") {
|
||||
agent {
|
||||
label 'data'
|
||||
}
|
||||
options { timeout(time: 30, unit: 'MINUTES') }
|
||||
|
||||
environment {
|
||||
DOCKER_HUB = credentials('hub.docker.com-springbuildmaster')
|
||||
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
|
||||
DOCKER_HUB = credentials("${p['docker.credentials']}")
|
||||
ARTIFACTORY = credentials("${p['artifactory.credentials']}")
|
||||
}
|
||||
|
||||
steps {
|
||||
script {
|
||||
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
|
||||
docker.image('adoptopenjdk/openjdk16:latest').inside('-u root -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v $HOME:/tmp/jenkins-home') {
|
||||
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
|
||||
docker.image(p['docker.java.lts.image']).inside(p['docker.java.inside.docker']) {
|
||||
sh "docker login --username ${DOCKER_HUB_USR} --password ${DOCKER_HUB_PSW}"
|
||||
sh 'PROFILE=java11 ci/verify.sh'
|
||||
sh 'PROFILE=none ci/verify.sh'
|
||||
sh "ci/clean.sh"
|
||||
}
|
||||
}
|
||||
@@ -102,8 +109,9 @@ pipeline {
|
||||
|
||||
stage('Release to artifactory') {
|
||||
when {
|
||||
beforeAgent(true)
|
||||
anyOf {
|
||||
branch '4.2.x'
|
||||
branch(pattern: "main|(\\d\\.\\d\\.x)", comparator: "REGEXP")
|
||||
not { triggeredBy 'UpstreamCause' }
|
||||
}
|
||||
}
|
||||
@@ -113,13 +121,13 @@ pipeline {
|
||||
options { timeout(time: 20, unit: 'MINUTES') }
|
||||
|
||||
environment {
|
||||
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
|
||||
ARTIFACTORY = credentials("${p['artifactory.credentials']}")
|
||||
}
|
||||
|
||||
steps {
|
||||
script {
|
||||
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
|
||||
docker.image('adoptopenjdk/openjdk8:latest').inside('-v $HOME:/tmp/jenkins-home') {
|
||||
docker.withRegistry(p['docker.registry'], p['docker.credentials']) {
|
||||
docker.image(p['docker.java.main.image']).inside(p['docker.java.inside.basic']) {
|
||||
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,artifactory -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-elasticsearch-non-root ' +
|
||||
'-Dartifactory.server=https://repo.spring.io ' +
|
||||
"-Dartifactory.username=${ARTIFACTORY_USR} " +
|
||||
@@ -133,34 +141,6 @@ pipeline {
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Publish documentation') {
|
||||
when {
|
||||
branch '4.2.x'
|
||||
}
|
||||
agent {
|
||||
label 'data'
|
||||
}
|
||||
options { timeout(time: 20, unit: 'MINUTES') }
|
||||
|
||||
environment {
|
||||
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
|
||||
}
|
||||
|
||||
steps {
|
||||
script {
|
||||
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
|
||||
docker.image('adoptopenjdk/openjdk8:latest').inside('-v $HOME:/tmp/jenkins-home') {
|
||||
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -s settings.xml -Pci,distribute -Dmaven.repo.local=/tmp/jenkins-home/.m2/spring-data-elasticsearch-non-root ' +
|
||||
'-Dartifactory.server=https://repo.spring.io ' +
|
||||
"-Dartifactory.username=${ARTIFACTORY_USR} " +
|
||||
"-Dartifactory.password=${ARTIFACTORY_PSW} " +
|
||||
"-Dartifactory.distribution-repository=temp-private-local " +
|
||||
'-Dmaven.test.skip=true clean deploy -U -B'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post {
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
image:https://spring.io/badges/spring-data-elasticsearch/ga.svg[Spring Data Elasticsearch,link=https://projects.spring.io/spring-data-elasticsearch#quick-start] image:https://spring.io/badges/spring-data-elasticsearch/snapshot.svg[Spring Data Elasticsearch,link=https://projects.spring.io/spring-data-elasticsearch#quick-start]
|
||||
|
||||
= Spring Data for Elasticsearch image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-elasticsearch%2Fmaster&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]]
|
||||
= 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]]
|
||||
|
||||
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.
|
||||
|
||||
@@ -217,7 +217,7 @@ The generated documentation is available from `target/site/reference/html/index.
|
||||
|
||||
== Examples
|
||||
|
||||
For examples on using the Spring Data for Elasticsearch, see the https://github.com/spring-projects/spring-data-examples/tree/master/elasticsearch/example[spring-data-examples] project.
|
||||
For examples on using the Spring Data for Elasticsearch, see the https://github.com/spring-projects/spring-data-examples/tree/main/elasticsearch/example[spring-data-examples] project.
|
||||
|
||||
== License
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# Java versions
|
||||
java.main.tag=8u332-b09-jdk
|
||||
java.next.tag=11.0.15_10-jdk
|
||||
java.lts.tag=17.0.3_7-jdk
|
||||
|
||||
# Docker container images - standard
|
||||
docker.java.main.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.main.tag}
|
||||
docker.java.next.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.next.tag}
|
||||
docker.java.lts.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.lts.tag}
|
||||
|
||||
# Supported versions of MongoDB
|
||||
docker.mongodb.4.0.version=4.0.28
|
||||
docker.mongodb.4.4.version=4.4.12
|
||||
docker.mongodb.5.0.version=5.0.6
|
||||
|
||||
# Supported versions of Redis
|
||||
docker.redis.6.version=6.2.6
|
||||
|
||||
# Supported versions of Cassandra
|
||||
docker.cassandra.3.version=3.11.12
|
||||
|
||||
# Docker environment settings
|
||||
docker.java.inside.basic=-v $HOME:/tmp/jenkins-home
|
||||
docker.java.inside.docker=-u root -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v $HOME:/tmp/jenkins-home
|
||||
|
||||
# Credentials
|
||||
docker.registry=
|
||||
docker.credentials=hub.docker.com-springbuildmaster
|
||||
artifactory.credentials=02bd1690-b54f-4c9f-819d-a77cb7a9822c
|
||||
@@ -8,7 +8,7 @@
|
||||
# "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
|
||||
# http://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
|
||||
@@ -19,7 +19,7 @@
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Maven2 Start Up Batch script
|
||||
# Maven Start Up Batch script
|
||||
#
|
||||
# Required ENV vars:
|
||||
# ------------------
|
||||
@@ -114,7 +114,6 @@ if $mingw ; then
|
||||
M2_HOME="`(cd "$M2_HOME"; pwd)`"
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
|
||||
# TODO classpath?
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
@@ -212,7 +211,11 @@ else
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
|
||||
fi
|
||||
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
|
||||
if [ -n "$MVNW_REPOURL" ]; then
|
||||
jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
|
||||
else
|
||||
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
|
||||
fi
|
||||
while IFS="=" read key value; do
|
||||
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
|
||||
esac
|
||||
@@ -221,22 +224,38 @@ else
|
||||
echo "Downloading from: $jarUrl"
|
||||
fi
|
||||
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
|
||||
if $cygwin; then
|
||||
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
|
||||
fi
|
||||
|
||||
if command -v wget > /dev/null; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Found wget ... using wget"
|
||||
fi
|
||||
wget "$jarUrl" -O "$wrapperJarPath"
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
wget "$jarUrl" -O "$wrapperJarPath"
|
||||
else
|
||||
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
|
||||
fi
|
||||
elif command -v curl > /dev/null; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Found curl ... using curl"
|
||||
fi
|
||||
curl -o "$wrapperJarPath" "$jarUrl"
|
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
|
||||
curl -o "$wrapperJarPath" "$jarUrl" -f
|
||||
else
|
||||
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
|
||||
fi
|
||||
|
||||
else
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Falling back to using Java to download"
|
||||
fi
|
||||
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
|
||||
# For Cygwin, switch paths to Windows format before running javac
|
||||
if $cygwin; then
|
||||
javaClass=`cygpath --path --windows "$javaClass"`
|
||||
fi
|
||||
if [ -e "$javaClass" ]; then
|
||||
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
@@ -277,6 +296,11 @@ if $cygwin; then
|
||||
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
|
||||
fi
|
||||
|
||||
# Provide a "standardized" way to retrieve the CLI args that will
|
||||
# work with both Windows and non-Windows executions.
|
||||
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
|
||||
export MAVEN_CMD_LINE_ARGS
|
||||
|
||||
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
exec "$JAVACMD" \
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
@REM "License"); you may not use this file except in compliance
|
||||
@REM with the License. You may obtain a copy of the License at
|
||||
@REM
|
||||
@REM https://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM http://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
@@ -18,7 +18,7 @@
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Maven2 Start Up Batch script
|
||||
@REM Maven Start Up Batch script
|
||||
@REM
|
||||
@REM Required ENV vars:
|
||||
@REM JAVA_HOME - location of a JDK home dir
|
||||
@@ -26,7 +26,7 @@
|
||||
@REM Optional ENV vars
|
||||
@REM M2_HOME - location of maven2's installed home dir
|
||||
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
|
||||
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
|
||||
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
|
||||
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
@REM e.g. to debug Maven itself, use
|
||||
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
@@ -37,7 +37,7 @@
|
||||
@echo off
|
||||
@REM set title of command window
|
||||
title %0
|
||||
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
|
||||
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
|
||||
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
||||
|
||||
@REM set %HOME% to equivalent of $HOME
|
||||
@@ -120,23 +120,44 @@ SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
|
||||
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
|
||||
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
|
||||
FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO (
|
||||
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
|
||||
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
|
||||
|
||||
FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
|
||||
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
|
||||
)
|
||||
|
||||
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||
if exist %WRAPPER_JAR% (
|
||||
echo Found %WRAPPER_JAR%
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Found %WRAPPER_JAR%
|
||||
)
|
||||
) else (
|
||||
echo Couldn't find %WRAPPER_JAR%, downloading it ...
|
||||
echo Downloading from: %DOWNLOAD_URL%
|
||||
powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"
|
||||
echo Finished downloading %WRAPPER_JAR%
|
||||
if not "%MVNW_REPOURL%" == "" (
|
||||
SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
|
||||
)
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Couldn't find %WRAPPER_JAR%, downloading it ...
|
||||
echo Downloading from: %DOWNLOAD_URL%
|
||||
)
|
||||
|
||||
powershell -Command "&{"^
|
||||
"$webclient = new-object System.Net.WebClient;"^
|
||||
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
|
||||
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
|
||||
"}"^
|
||||
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
|
||||
"}"
|
||||
if "%MVNW_VERBOSE%" == "true" (
|
||||
echo Finished downloading %WRAPPER_JAR%
|
||||
)
|
||||
)
|
||||
@REM End of extension
|
||||
|
||||
@REM Provide a "standardized" way to retrieve the CLI args that will
|
||||
@REM work with both Windows and non-Windows executions.
|
||||
set MAVEN_CMD_LINE_ARGS=%*
|
||||
|
||||
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
|
||||
if ERRORLEVEL 1 goto error
|
||||
goto end
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-elasticsearch</artifactId>
|
||||
<version>4.2.2</version>
|
||||
<version>4.3.9</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.build</groupId>
|
||||
<artifactId>spring-data-parent</artifactId>
|
||||
<version>2.5.2</version>
|
||||
<version>2.6.9</version>
|
||||
</parent>
|
||||
|
||||
<name>Spring Data Elasticsearch</name>
|
||||
@@ -18,13 +18,22 @@
|
||||
<url>https://github.com/spring-projects/spring-data-elasticsearch</url>
|
||||
|
||||
<properties>
|
||||
<commonslang>2.6</commonslang>
|
||||
<elasticsearch>7.12.1</elasticsearch>
|
||||
<log4j>2.13.3</log4j>
|
||||
<netty>4.1.52.Final</netty>
|
||||
<springdata.commons>2.5.2</springdata.commons>
|
||||
<testcontainers>1.15.1</testcontainers>
|
||||
<elasticsearch>7.15.2</elasticsearch>
|
||||
<log4j>2.17.0</log4j>
|
||||
<netty>4.1.65.Final</netty>
|
||||
<springdata.commons>2.6.9</springdata.commons>
|
||||
<testcontainers>1.15.3</testcontainers>
|
||||
<blockhound-junit>1.0.6.RELEASE</blockhound-junit>
|
||||
<java-module-name>spring.data.elasticsearch</java-module-name>
|
||||
|
||||
<!--
|
||||
properties defining the maven phase for the tests and integration tests
|
||||
set to "none" to disable the corresponding test execution (-Dmvn.unit-test.goal=none)
|
||||
default execution for unit-test: "test", for the integration tests: "integration-test"
|
||||
-->
|
||||
<mvn.unit-test.goal>test</mvn.unit-test.goal>
|
||||
<mvn.integration-test-elasticsearch.goal>integration-test</mvn.integration-test-elasticsearch.goal>
|
||||
<mvn.integration-test-opensearch.goal>none</mvn.integration-test-opensearch.goal>
|
||||
</properties>
|
||||
|
||||
<developers>
|
||||
@@ -131,27 +140,17 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- APACHE -->
|
||||
<dependency>
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
<version>${commonslang}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- JODA Time -->
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
<artifactId>joda-time</artifactId>
|
||||
<version>${jodatime}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Elasticsearch -->
|
||||
<dependency>
|
||||
<groupId>org.elasticsearch.client</groupId>
|
||||
<artifactId>transport</artifactId>
|
||||
<version>${elasticsearch}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -159,6 +158,12 @@
|
||||
<groupId>org.elasticsearch.plugin</groupId>
|
||||
<artifactId>transport-netty4-client</artifactId>
|
||||
<version>${elasticsearch}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -256,6 +261,14 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.projectreactor.tools</groupId>
|
||||
<artifactId>blockhound-junit-platform</artifactId>
|
||||
<version>${blockhound-junit}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!--
|
||||
we don't use lombok in Spring Data Elasticsearch anymore. But the dependency is set in the parent project, and so the
|
||||
lombok compiler stuff is executed regardless of the fact that we don't need it.
|
||||
@@ -272,25 +285,6 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>org.apache.openwebbeans.test</groupId>
|
||||
<artifactId>cditest-owb</artifactId>
|
||||
<version>1.2.8</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.apache.geronimo.specs</groupId>
|
||||
<artifactId>geronimo-jcdi_1.0_spec</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.geronimo.specs</groupId>
|
||||
<artifactId>geronimo-atinject_1.0_spec</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.skyscreamer</groupId>
|
||||
<artifactId>jsonassert</artifactId>
|
||||
@@ -373,9 +367,6 @@
|
||||
</resources>
|
||||
|
||||
<plugins>
|
||||
<!--
|
||||
please do not remove this configuration for surefire - we need that to avoid issue with jar hell
|
||||
-->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
@@ -394,7 +385,7 @@
|
||||
<!-- the default-test execution runs only the unit tests -->
|
||||
<execution>
|
||||
<id>default-test</id>
|
||||
<phase>test</phase>
|
||||
<phase>${mvn.unit-test.goal}</phase>
|
||||
<goals>
|
||||
<goal>test</goal>
|
||||
</goals>
|
||||
@@ -402,15 +393,32 @@
|
||||
<excludedGroups>integration-test</excludedGroups>
|
||||
</configuration>
|
||||
</execution>
|
||||
<!-- execution to run the integration tests -->
|
||||
<!-- execution to run the integration tests against Elasticsearch -->
|
||||
<execution>
|
||||
<id>integration-test</id>
|
||||
<phase>integration-test</phase>
|
||||
<id>integration-test-elasticsearch</id>
|
||||
<phase>${mvn.integration-test-elasticsearch.goal}</phase>
|
||||
<goals>
|
||||
<goal>test</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<groups>integration-test</groups>
|
||||
<systemPropertyVariables>
|
||||
<sde.integration-test.environment>elasticsearch</sde.integration-test.environment>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</execution>
|
||||
<!-- execution to run the integration tests against Opensearch -->
|
||||
<execution>
|
||||
<id>integration-test-opensearch</id>
|
||||
<phase>${mvn.integration-test-opensearch.goal}</phase>
|
||||
<goals>
|
||||
<goal>test</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<groups>integration-test</groups>
|
||||
<systemPropertyVariables>
|
||||
<sde.integration-test.environment>opensearch</sde.integration-test.environment>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
@@ -448,9 +456,7 @@
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
|
||||
<id>ci</id>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
@@ -467,15 +473,34 @@
|
||||
</module>
|
||||
</checkstyleRules>
|
||||
<includes>**/*</includes>
|
||||
<excludes>.git/**/*,target/**/*,**/target/**/*,.idea/**/*,**/spring.schemas,**/*.svg,mvnw,mvnw.cmd,**/*.policy</excludes>
|
||||
<excludes>
|
||||
.git/**/*,target/**/*,**/target/**/*,.idea/**/*,**/spring.schemas,**/*.svg,mvnw,mvnw.cmd,**/*.policy
|
||||
</excludes>
|
||||
<sourceDirectories>./</sourceDirectories>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>jdk13+</id>
|
||||
<!-- on jDK13+, Blockhound needs this JVM flag set -->
|
||||
<activation>
|
||||
<jdk>[13,)</jdk>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<argLine>-XX:+AllowRedefinitionToAddDeleteMethods</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<repositories>
|
||||
|
||||
@@ -34,7 +34,8 @@ The following table shows the Elasticsearch versions that are used by Spring Dat
|
||||
[cols="^,^,^,^,^",options="header"]
|
||||
|===
|
||||
| Spring Data Release Train | Spring Data Elasticsearch | Elasticsearch | Spring Framework | Spring Boot
|
||||
| 2021.0 (Pascal) | 4.2.1 | 7.12.1 | 5.3.7 | 2.5.x
|
||||
| 2021.1 (Q)footnote:cdv[Currently in development] | 4.3.xfootnote:cdv[] | 7.15.2 | 5.3.xfootnote:cdv[] | 2.5 .xfootnote:cdv[]
|
||||
| 2021.0 (Pascal) | 4.2.x | 7.12.0 | 5.3.x | 2.5.x
|
||||
| 2020.0 (Ockham) | 4.1.x | 7.9.3 | 5.3.2 | 2.4.x
|
||||
| Neumann | 4.0.x | 7.6.2 | 5.2.12 |2.3.x
|
||||
| Moore | 3.2.x |6.8.12 | 5.2.12| 2.2.x
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
|
||||
This chapter illustrates configuration and usage of supported Elasticsearch client implementations.
|
||||
|
||||
Spring Data Elasticsearch operates upon an Elasticsearch client that is connected to a single Elasticsearch node or a cluster. Although the Elasticsearch Client can be used to work with the cluster, applications using Spring Data Elasticsearch normally use the higher level abstractions of <<elasticsearch.operations>> and <<elasticsearch.repositories>>.
|
||||
Spring Data Elasticsearch operates upon an Elasticsearch client that is connected to a single Elasticsearch node or a cluster.
|
||||
Although the Elasticsearch Client can be used to work with the cluster, applications using Spring Data Elasticsearch normally use the higher level abstractions of <<elasticsearch.operations>> and <<elasticsearch.repositories>>.
|
||||
|
||||
[[elasticsearch.clients.transport]]
|
||||
== Transport Client
|
||||
|
||||
WARNING: The `TransportClient` is deprecated as of Elasticsearch 7 and will be removed in Elasticsearch 8. (https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/transport-client.html[see the Elasticsearch documentation]). Spring Data Elasticsearch will support the `TransportClient` as long as it is available in the used
|
||||
Elasticsearch <<elasticsearch.versions,version>> but has deprecated the classes using it since version 4.0.
|
||||
WARNING: The `TransportClient` is deprecated as of Elasticsearch 7 and will be removed in Elasticsearch 8. (https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/transport-client.html[see the Elasticsearch documentation]).
|
||||
Spring Data Elasticsearch will support the `TransportClient` as long as it is available in the used Elasticsearch <<elasticsearch.versions,version>> but has deprecated the classes using it since version 4.0.
|
||||
|
||||
We strongly recommend to use the <<elasticsearch.clients.rest>> instead of the `TransportClient`.
|
||||
|
||||
@@ -46,6 +47,7 @@ IndexRequest request = new IndexRequest("spring-data")
|
||||
|
||||
IndexResponse response = client.index(request);
|
||||
----
|
||||
|
||||
<.> The `TransportClient` must be configured with the cluster name.
|
||||
<.> The host and port to connect the client to.
|
||||
<.> the RefreshPolicy must be set in the `ElasticsearchTemplate` (override `refreshPolicy()` to not use the default)
|
||||
@@ -54,8 +56,7 @@ IndexResponse response = client.index(request);
|
||||
[[elasticsearch.clients.rest]]
|
||||
== High Level REST Client
|
||||
|
||||
The Java High Level REST Client is the default client of Elasticsearch, it provides a straight forward replacement for the `TransportClient` as it accepts and returns
|
||||
the very same request/response objects and therefore depends on the Elasticsearch core project.
|
||||
The Java High Level REST Client is the default client of Elasticsearch, it provides a straight forward replacement for the `TransportClient` as it accepts and returns the very same request/response objects and therefore depends on the Elasticsearch core project.
|
||||
Asynchronous calls are operated upon a client managed thread pool and require a callback to be notified when the request is done.
|
||||
|
||||
.High Level REST Client
|
||||
@@ -93,6 +94,7 @@ IndexRequest request = new IndexRequest("spring-data")
|
||||
|
||||
IndexResponse response = highLevelClient.index(request,RequestOptions.DEFAULT);
|
||||
----
|
||||
|
||||
<1> Use the builder to provide cluster addresses, set default `HttpHeaders` or enable SSL.
|
||||
<2> Create the RestHighLevelClient.
|
||||
<3> It is also possible to obtain the `lowLevelRest()` client.
|
||||
@@ -131,6 +133,7 @@ Mono<IndexResponse> response = client.index(request ->
|
||||
.source(singletonMap("feature", "reactive-client"));
|
||||
);
|
||||
----
|
||||
|
||||
<.> Use the builder to provide cluster addresses, set default `HttpHeaders` or enable SSL.
|
||||
====
|
||||
|
||||
@@ -162,25 +165,30 @@ ClientConfiguration clientConfiguration = ClientConfiguration.builder()
|
||||
headers.add("currentTime", LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
|
||||
return headers;
|
||||
})
|
||||
.withWebClientConfigurer(webClient -> { <.>
|
||||
//...
|
||||
return webClient;
|
||||
})
|
||||
.withHttpClientConfigurer(clientBuilder -> { <.>
|
||||
//...
|
||||
.withClientConfigurer( <.>
|
||||
ReactiveRestClients.WebClientConfigurationCallback.from(webClient -> {
|
||||
// ...
|
||||
return webClient;
|
||||
}))
|
||||
.withClientConfigurer( <.>
|
||||
RestClients.RestClientConfigurationCallback.from(clientBuilder -> {
|
||||
// ...
|
||||
return clientBuilder;
|
||||
})
|
||||
}))
|
||||
. // ... other options
|
||||
.build();
|
||||
|
||||
----
|
||||
|
||||
<.> Define default headers, if they need to be customized
|
||||
<.> Use the builder to provide cluster addresses, set default `HttpHeaders` or enable SSL.
|
||||
<.> Optionally enable SSL.
|
||||
<.> Optionally set a proxy.
|
||||
<.> Optionally set a path prefix, mostly used when different clusters a behind some reverse proxy.
|
||||
<.> Set the connection timeout. Default is 10 sec.
|
||||
<.> Set the socket timeout. Default is 5 sec.
|
||||
<.> Set the connection timeout.
|
||||
Default is 10 sec.
|
||||
<.> Set the socket timeout.
|
||||
Default is 5 sec.
|
||||
<.> Optionally set headers.
|
||||
<.> Add basic authentication.
|
||||
<.> A `Supplier<Header>` function can be specified which is called every time before a request is sent to Elasticsearch - here, as an example, the current time is written in a header.
|
||||
@@ -188,13 +196,36 @@ ClientConfiguration clientConfiguration = ClientConfiguration.builder()
|
||||
<.> for non-reactive setup a function configuring the REST client
|
||||
====
|
||||
|
||||
IMPORTANT: Adding a Header supplier as shown in above example allows to inject headers that may change over the time, like authentication JWT tokens. If this is used in the reactive setup, the supplier function *must not* block!
|
||||
IMPORTANT: Adding a Header supplier as shown in above example allows to inject headers that may change over the time, like authentication JWT tokens.
|
||||
If this is used in the reactive setup, the supplier function *must not* block!
|
||||
|
||||
=== Elasticsearch 7 compatibility headers
|
||||
|
||||
When using Spring Data Elasticsearch 4 - which uses the Elasticsearch 7 client libraries - and accessing an Elasticsearch cluster that is running on version 8, it is necessary to set the compatibility headers
|
||||
https://www.elastic.co/guide/en/elasticsearch/reference/8.0/rest-api-compatibility.html[see Elasticserach documentation].
|
||||
This should be done using a header supplier like shown above:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
ClientConfigurationBuilder configurationBuilder = new ClientConfigurationBuilder();
|
||||
configurationBuilder //
|
||||
// ...
|
||||
.withHeaders(() -> {
|
||||
HttpHeaders defaultCompatibilityHeaders = new HttpHeaders();
|
||||
defaultCompatibilityHeaders.add("Accept",
|
||||
"application/vnd.elasticsearch+json;compatible-with=7");
|
||||
defaultCompatibilityHeaders.add("Content-Type",
|
||||
"application/vnd.elasticsearch+json;compatible-with=7");
|
||||
return defaultCompatibilityHeaders;
|
||||
});
|
||||
----
|
||||
====
|
||||
|
||||
[[elasticsearch.clients.logging]]
|
||||
== Client Logging
|
||||
|
||||
To see what is actually sent to and received from the server `Request` / `Response` logging on the transport level needs
|
||||
to be turned on as outlined in the snippet below.
|
||||
To see what is actually sent to and received from the server `Request` / `Response` logging on the transport level needs to be turned on as outlined in the snippet below.
|
||||
|
||||
.Enable transport layer logging
|
||||
[source,xml]
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
[[elasticsearch-migration-guide-4.2-4.3]]
|
||||
= Upgrading from 4.2.x to 4.3.x
|
||||
|
||||
This section describes breaking changes from version 4.2.x to 4.3.x and how removed features can be replaced by new introduced features.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Elasticsearch is working on a new Client that will replace the `RestHighLevelClient` because the `RestHighLevelClient` uses code from Elasticsearch core libraries which are not Apache 2 licensed anymore.
|
||||
Spring Data Elasticsearch is preparing for this change as well.
|
||||
This means that internally the implementations for the `*Operations` interfaces need to change - which should be no problem if users program against the interfaces like `ElasticsearchOperations` or `ReactiveElasticsearchOperations`.
|
||||
If you are using the implementation classes like `ElasticsearchRestTemplate` directly, you will need to adapt to these changes.
|
||||
|
||||
Spring Data Elasticsearch also removes or replaces the use of classes from the `org.elasticsearch` packages in it's API classes and methods, only using them in the implementation where the access to Elasticsearch is implemented.
|
||||
For the user that means, that some enum classes that were used are replaced by enums that live in `org.springframework.data.elasticsearch` with the same values, these are internally mapped onto the Elasticsearch ones.
|
||||
|
||||
Places where classes are used that cannot easily be replaced, this usage is marked as deprecated, we are working on replacements.
|
||||
|
||||
Check the sections on <<elasticsearch-migration-guide-4.2-4.3.deprecations>> and <<elasticsearch-migration-guide-4.2-4.3.breaking-changes>> for further details.
|
||||
====
|
||||
|
||||
[[elasticsearch-migration-guide-4.2-4.3.deprecations]]
|
||||
== Deprecations
|
||||
|
||||
=== suggest methods
|
||||
|
||||
In `SearchOperations`, and so in `ElasticsearchOperations` as well, the `suggest` methods taking a `org.elasticsearch.search.suggest.SuggestBuilder` as argument and returning a `org.elasticsearch.action.search.SearchResponse` have been deprecated.
|
||||
Use `SearchHits<T> search(Query query, Class<T> clazz)` instead, passing in a `NativeSearchQuery` which can contain a `SuggestBuilder` and read the suggest results from the returned `SearchHit<T>`.
|
||||
|
||||
In `ReactiveSearchOperations` the new `suggest` methods return a `Mono<org.springframework.data.elasticsearch.core.suggest.response.Suggest>` now.
|
||||
Here as well the old methods are deprecated.
|
||||
|
||||
[[elasticsearch-migration-guide-4.2-4.3.breaking-changes]]
|
||||
== Breaking Changes
|
||||
|
||||
=== Removal of `org.elasticsearch` classes from the API.
|
||||
|
||||
* In the `org.springframework.data.elasticsearch.annotations.CompletionContext` annotation the property `type()` has changed from `org.elasticsearch.search.suggest.completion.context.ContextMapping.Type` to `org.springframework.data.elasticsearch.annotations.CompletionContext.ContextMappingType`, the available enum values are the same.
|
||||
* In the `org.springframework.data.elasticsearch.annotations.Document` annotation the `versionType()` property has changed to `org.springframework.data.elasticsearch.annotations.Document.VersionType`, the available enum values are the same.
|
||||
* In the `org.springframework.data.elasticsearch.core.query.Query` interface the `searchType()` property has changed to `org.springframework.data.elasticsearch.core.query.Query.SearchType`, the available enum values are the same.
|
||||
* In the `org.springframework.data.elasticsearch.core.query.Query` interface the return value of `timeout()` was changed to `java.time.Duration`.
|
||||
* The `SearchHits<T>`class does not contain the `org.elasticsearch.search.aggregations.Aggregations` anymore.
|
||||
Instead it now contains an instance of the `org.springframework.data.elasticsearch.core.AggregationsContainer<T>` class where `T` is the concrete aggregations type from the underlying client that is used.
|
||||
Currently this will be a `org
|
||||
.springframework.data.elasticsearch.core.clients.elasticsearch7.ElasticsearchAggregations` object; later different implementations will be available.
|
||||
The same change has been done to the `ReactiveSearchOperations.aggregate()` functions, the now return a `Flux<AggregationContainer<?>>`.
|
||||
Programs using the aggregations need to be changed to cast the returned value to the appropriate class to further proces it.
|
||||
* methods that might have thrown a `org.elasticsearch.ElasticsearchStatusException` now will throw `org.springframework.data.elasticsearch.RestStatusException` instead.
|
||||
|
||||
=== Handling of field and sourceFilter properties of Query
|
||||
|
||||
Up to version 4.2 the `fields` property of a `Query` was interpreted and added to the include list of the `sourceFilter`.
|
||||
This was not correct, as these are different things for Elasticsearch.
|
||||
This has been corrected.
|
||||
As a consequence code might not work anymore that relies on using `fields` to specify which fields should be returned from the document's `_source' and should be changed to use the `sourceFilter`.
|
||||
|
||||
=== search_type default value
|
||||
|
||||
The default value for the `search_type` in Elasticsearch is `query_then_fetch`.
|
||||
This now is also set as default value in the `Query` implementations, it was previously set to `dfs_query_then_fetch`.
|
||||
|
||||
=== BulkOptions changes
|
||||
|
||||
Some properties of the `org.springframework.data.elasticsearch.core.query.BulkOptions` class have changed their type:
|
||||
|
||||
* the type of the `timeout` property has been changed to `java.time.Duration`.
|
||||
* the type of the`refreshPolicy` property has been changed to `org.springframework.data.elasticsearch.core.RefreshPolicy`.
|
||||
|
||||
=== IndicesOptions change
|
||||
|
||||
Spring Data Elasticsearch now uses `org.springframework.data.elasticsearch.core.query.IndicesOptions` instead of `org.elasticsearch.action.support.IndicesOptions`.
|
||||
|
||||
=== Completion classes
|
||||
|
||||
The classes from the package `org.springframework.data.elasticsearch.core.completion` have been moved to `org.springframework.data.elasticsearch.core.suggest`.
|
||||
|
||||
=== Other renamings
|
||||
|
||||
The `org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentPropertyConverter` interface has been renamed to `org.springframework.data.elasticsearch.core.mapping.PropertyValueConverter`.
|
||||
Likewise the implementations classes named _XXPersistentPropertyConverter_ have been renamed to _XXPropertyValueConverter_.
|
||||
@@ -7,7 +7,8 @@ It is recommended to add those operations as custom implementation as described
|
||||
[[elasticsearc.misc.index.settings]]
|
||||
== Index settings
|
||||
|
||||
When creating Elasticsearch indices with Spring Data Elasticsearch different index settings can be defined by using the `@Setting` annotation. The following arguments are available:
|
||||
When creating Elasticsearch indices with Spring Data Elasticsearch different index settings can be defined by using the `@Setting` annotation.
|
||||
The following arguments are available:
|
||||
|
||||
* `useServerConfiguration` does not send any settings parameters, so the Elasticsearch server configuration determines them.
|
||||
* `settingPath` refers to a JSON file defining the settings that must be resolvable in the classpath
|
||||
@@ -42,10 +43,38 @@ class Entity {
|
||||
// getter and setter...
|
||||
}
|
||||
----
|
||||
|
||||
<.> when defining sort fields, use the name of the Java property (_firstField_), not the name that might be defined for Elasticsearch (_first_field_)
|
||||
<.> `sortModes`, `sortOrders` and `sortMissingValues` are optional, but if they are set, the number of entries must match the number of `sortFields` elements
|
||||
====
|
||||
|
||||
[[elasticsearch.misc.mappings]]
|
||||
== Index Mapping
|
||||
|
||||
When Spring Data Elasticsearch creates the index mapping with the `IndexOperations.createMapping()` methods, it uses the annotations described in <<elasticsearch.mapping.meta-model.annotations>>, especially the `@Field` annotation.
|
||||
In addition to that it is possible to add the `@Mapping` annotation to a class.
|
||||
This annotation has the following properties:
|
||||
|
||||
* `mappingPath` a classpath resource in JSON format; if this is not empty it is used as the mapping, no other mapping processing is done.
|
||||
* `enabled` when set to false, this flag is written to the mapping and no further processing is done.
|
||||
* `dateDetection` and `numericDetection` set the corresponding properties in the mapping when not set to `DEFAULT`.
|
||||
* `dynamicDateFormats` when this String array is not empty, it defines the date formats used for automatic date detection.
|
||||
* `runtimeFieldsPath` a classpath resource in JSON format containing the definition of runtime fields which is written to the index mappings, for example:
|
||||
|
||||
====
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
"day_of_week": {
|
||||
"type": "keyword",
|
||||
"script": {
|
||||
"source": "emit(doc['@timestamp'].value.dayOfWeekEnum.getDisplayName(TextStyle.FULL, Locale.ROOT))"
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
[[elasticsearch.misc.filter]]
|
||||
== Filter Builder
|
||||
|
||||
@@ -141,7 +170,10 @@ interface SampleEntityRepository extends Repository<SampleEntity, String> {
|
||||
[[elasticsearch.misc.sorts]]
|
||||
== Sort options
|
||||
|
||||
In addition to the default sort options described <<repositories.paging-and-sorting>> Spring Data Elasticsearch has a `GeoDistanceOrder` class which can be used to have the result of a search operation ordered by geographical distance.
|
||||
In addition to the default sort options described in <<repositories.paging-and-sorting>>, Spring Data Elasticsearch provides the class `org.springframework.data.elasticsearch.core.query.Order` which derives from `org.springframework.data.domain.Sort.Order`.
|
||||
It offers additional parameters that can be sent to Elasticsearch when specifying the sorting of the result (see https://www.elastic.co/guide/en/elasticsearch/reference/7.15/sort-search-results.html).
|
||||
|
||||
There also is the `org.springframework.data.elasticsearch.core.query.GeoDistanceOrder` class which can be used to have the result of a search operation ordered by geographical distance.
|
||||
|
||||
If the class to be retrieved has a `GeoPoint` property named _location_, the following `Sort` would sort the results by distance to the given point:
|
||||
|
||||
@@ -151,3 +183,81 @@ If the class to be retrieved has a `GeoPoint` property named _location_, the fol
|
||||
Sort.by(new GeoDistanceOrder("location", new GeoPoint(48.137154, 11.5761247)))
|
||||
----
|
||||
====
|
||||
|
||||
[[elasticsearch.misc.runtime-fields]]
|
||||
== Runtime Fields
|
||||
|
||||
From version 7.12 on Elasticsearch has added the feature of runtime fields (https://www.elastic.co/guide/en/elasticsearch/reference/7.12/runtime.html).
|
||||
Spring Data Elasticsearch supports this in two ways:
|
||||
|
||||
=== Runtime field definitions in the index mappings
|
||||
|
||||
The first way to define runtime fields is by adding the definitions to the index mappings (see https://www.elastic.co/guide/en/elasticsearch/reference/7.12/runtime-mapping-fields.html).
|
||||
To use this approach in Spring Data Elasticsearch the user must provide a JSON file that contains the corresponding definition, for example:
|
||||
|
||||
.runtime-fields.json
|
||||
====
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
"day_of_week": {
|
||||
"type": "keyword",
|
||||
"script": {
|
||||
"source": "emit(doc['@timestamp'].value.dayOfWeekEnum.getDisplayName(TextStyle.FULL, Locale.ROOT))"
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The path to this JSON file, which must be present on the classpath, must then be set in the `@Mapping` annotation of the entity:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Document(indexName = "runtime-fields")
|
||||
@Mapping(runtimeFieldsPath = "/runtime-fields.json")
|
||||
public class RuntimeFieldEntity {
|
||||
// properties, getter, setter,...
|
||||
}
|
||||
|
||||
----
|
||||
====
|
||||
|
||||
=== Runtime fields definitions set on a Query
|
||||
|
||||
The second way to define runtime fields is by adding the definitions to a search query (see https://www.elastic.co/guide/en/elasticsearch/reference/7.12/runtime-search-request.html).
|
||||
The following code example shows how to do this with Spring Data Elasticsearch :
|
||||
|
||||
The entity used is a simple object that has a `price` property:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Document(indexName = "some_index_name")
|
||||
public class SomethingToBuy {
|
||||
|
||||
private @Id @Nullable String id;
|
||||
@Nullable @Field(type = FieldType.Text) private String description;
|
||||
@Nullable @Field(type = FieldType.Double) private Double price;
|
||||
|
||||
// getter and setter
|
||||
}
|
||||
|
||||
----
|
||||
====
|
||||
|
||||
The following query uses a runtime field that calculates a `priceWithTax` value by adding 19% to the price and uses this value in the search query to find all entities where `priceWithTax` is higher or equal than a given value:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
RuntimeField runtimeField = new RuntimeField("priceWithTax", "double", "emit(doc['price'].value * 1.19)");
|
||||
Query query = new CriteriaQuery(new Criteria("priceWithTax").greaterThanEqual(16.5));
|
||||
query.addRuntimeField(runtimeField);
|
||||
|
||||
SearchHits<SomethingToBuy> searchHits = operations.search(query, SomethingToBuy.class);
|
||||
----
|
||||
====
|
||||
|
||||
This works with every implementation of the `Query` interface.
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
[[new-features]]
|
||||
= What's new
|
||||
|
||||
[[new-features.4-3-0]]
|
||||
== New in Spring Data Elasticsearch 4.3
|
||||
|
||||
* Upgrade to Elasticsearch 7.15.2.
|
||||
* Allow runtime_fields to be defined in the index mapping.
|
||||
* Add native support for range field types by using a range object.
|
||||
* Add repository search for nullable or empty properties.
|
||||
* Enable custom converters for single fields.
|
||||
* Supply a custom `Sort.Order` providing Elasticsearch specific parameters.
|
||||
|
||||
[[new-features.4-2-0]]
|
||||
== New in Spring Data Elasticsearch 4.2
|
||||
|
||||
|
||||
@@ -34,8 +34,6 @@ The following annotations are available:
|
||||
The most important attributes are:
|
||||
** `indexName`: the name of the index to store this entity in.
|
||||
This can contain a SpEL template expression like `"log-#{T(java.time.LocalDate).now().toString()}"`
|
||||
** `type`: [line-through]#the mapping type.
|
||||
If not set, the lowercased simple name of the class is used.# (deprecated since version 4.0)
|
||||
** `createIndex`: flag whether to create an index on repository bootstrapping.
|
||||
Default value is _true_.
|
||||
See <<elasticsearch.repositories.autocreation>>
|
||||
@@ -49,34 +47,37 @@ Constructor arguments are mapped by name to the key values in the retrieved Docu
|
||||
* `@Field`: Applied at the field level and defines properties of the field, most of the attributes map to the respective https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html[Elasticsearch Mapping] definitions (the following list is not complete, check the annotation Javadoc for a complete reference):
|
||||
** `name`: The name of the field as it will be represented in the Elasticsearch document, if not set, the Java field name is used.
|
||||
** `type`: The field type, can be one of _Text, Keyword, Long, Integer, Short, Byte, Double, Float, Half_Float, Scaled_Float, Date, Date_Nanos, Boolean, Binary, Integer_Range, Float_Range, Long_Range, Double_Range, Date_Range, Ip_Range, Object, Nested, Ip, TokenCount, Percolator, Flattened, Search_As_You_Type_.
|
||||
See https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html[Elasticsearch Mapping Types]
|
||||
See https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html[Elasticsearch Mapping Types].
|
||||
If the field type is not specified, it defaults to `FieldType.Auto`.
|
||||
This means, that no mapping entry is written for the property and that Elasticsearch will add a mapping entry dynamically when the first data for this property is stored (check the Elasticsearch documentation for dynamic mapping rules).
|
||||
** `format`: One or more built-in date formats, see the next section <<elasticsearch.mapping.meta-model.date-formats>>.
|
||||
** `pattern`: One or more custom date formats, see the next section <<elasticsearch.mapping.meta-model.date-formats>>.
|
||||
** `store`: Flag whether the original field value should be store in Elasticsearch, default value is _false_.
|
||||
** `analyzer`, `searchAnalyzer`, `normalizer` for specifying custom analyzers and normalizer.
|
||||
* `@GeoPoint`: Marks a field as _geo_point_ datatype.
|
||||
Can be omitted if the field is an instance of the `GeoPoint` class.
|
||||
* `@ValueConverter` defines a class to be used to convert the given property.
|
||||
In difference to a registered Spring `Converter` this only converts the annotated property and not every property of the given type.
|
||||
|
||||
The mapping metadata infrastructure is defined in a separate spring-data-commons project that is technology agnostic.
|
||||
|
||||
[[elasticsearch.mapping.meta-model.date-formats]]
|
||||
==== Date format mapping
|
||||
|
||||
Properties that derive from `TemporalAccessor` or are of type `java.util.Date` must either have a `@Field` annotation
|
||||
of type `FieldType.Date` or a custom converter must be registered for this type. This paragraph describes the use of
|
||||
Properties that derive from `TemporalAccessor` or are of type `java.util.Date` must either have a `@Field` annotation of type `FieldType.Date` or a custom converter must be registered for this type.
|
||||
This paragraph describes the use of
|
||||
`FieldType.Date`.
|
||||
|
||||
There are two attributes of the `@Field` annotation that define which date format information is written to the
|
||||
mapping (also see https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-date-format.html#built-in-date-formats[Elasticsearch Built In Formats] and https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-date-format.html#custom-date-formats[Elasticsearch Custom Date Formats])
|
||||
There are two attributes of the `@Field` annotation that define which date format information is written to the mapping (also see https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-date-format.html#built-in-date-formats[Elasticsearch Built In Formats] and https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-date-format.html#custom-date-formats[Elasticsearch Custom Date Formats])
|
||||
|
||||
The `format` attributes is used to define at least one of the predefined formats. If it is not defined, then a
|
||||
default value of __date_optional_time_ and _epoch_millis_ is used.
|
||||
The `format` attributes is used to define at least one of the predefined formats.
|
||||
If it is not defined, then a default value of __date_optional_time_ and _epoch_millis_ is used.
|
||||
|
||||
The `pattern` attribute can be used to add additional custom format strings. If you want to use only custom date formats, you must set the `format` property to empty `{}`.
|
||||
The `pattern` attribute can be used to add additional custom format strings.
|
||||
If you want to use only custom date formats, you must set the `format` property to empty `{}`.
|
||||
|
||||
The following table shows the different attributes and the mapping created from their values:
|
||||
|
||||
|
||||
[cols=2*,options=header]
|
||||
|===
|
||||
| annotation
|
||||
@@ -102,12 +103,59 @@ The following table shows the different attributes and the mapping created from
|
||||
NOTE: If you are using a custom date format, you need to use _uuuu_ for the year instead of _yyyy_.
|
||||
This is due to a https://www.elastic.co/guide/en/elasticsearch/reference/current/migrate-to-java-time.html#java-time-migration-incompatible-date-formats[change in Elasticsearch 7].
|
||||
|
||||
==== Range types
|
||||
|
||||
When a field is annotated with a type of one of _Integer_Range, Float_Range, Long_Range, Double_Range, Date_Range,_ or _Ip_Range_ the field must be an instance of a class that will be mapped to an Elasticsearch range, for example:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
class SomePersonData {
|
||||
|
||||
@Field(type = FieldType.Integer_Range)
|
||||
private ValidAge validAge;
|
||||
|
||||
// getter and setter
|
||||
}
|
||||
|
||||
class ValidAge {
|
||||
@Field(name="gte")
|
||||
private Integer from;
|
||||
|
||||
@Field(name="lte")
|
||||
private Integer to;
|
||||
|
||||
// getter and setter
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
As an alternative Spring Data Elasticsearch provides a `Range<T>` class so that the previous example can be written as:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
class SomePersonData {
|
||||
|
||||
@Field(type = FieldType.Integer_Range)
|
||||
private Range<Integer> validAge;
|
||||
|
||||
// getter and setter
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Supported classes for the type `<T>` are `Integer`, `Long`, `Float`, `Double`, `Date` and classes that implement the
|
||||
`TemporalAccessor` interface.
|
||||
|
||||
==== Mapped field names
|
||||
|
||||
Without further configuration, Spring Data Elasticsearch will use the property name of an object as field name in Elasticsearch. This can be changed for individual field by using the `@Field` annotation on that property.
|
||||
Without further configuration, Spring Data Elasticsearch will use the property name of an object as field name in Elasticsearch.
|
||||
This can be changed for individual field by using the `@Field` annotation on that property.
|
||||
|
||||
It is also possible to define a `FieldNamingStrategy` in the configuration of the client (<<elasticsearch.clients>>). If for example a `SnakeCaseFieldNamingStrategy` is configured, the property _sampleProperty_ of the object would be mapped to _sample_property_ in Elasticsearch. A `FieldNamingStrategy` applies to all entities; it can be overwritten by
|
||||
setting a specific name with `@Field` on a property.
|
||||
It is also possible to define a `FieldNamingStrategy` in the configuration of the client (<<elasticsearch.clients>>).
|
||||
If for example a `SnakeCaseFieldNamingStrategy` is configured, the property _sampleProperty_ of the object would be mapped to _sample_property_ in Elasticsearch.
|
||||
A `FieldNamingStrategy` applies to all entities; it can be overwritten by setting a specific name with `@Field` on a property.
|
||||
|
||||
[[elasticsearch.mapping.meta-model.rules]]
|
||||
=== Mapping Rules
|
||||
@@ -138,6 +186,7 @@ public class Person { <1>
|
||||
"lastname" : "Connor"
|
||||
}
|
||||
----
|
||||
|
||||
<1> By default the domain types class name is used for the type hint.
|
||||
====
|
||||
|
||||
@@ -165,11 +214,32 @@ public class Person {
|
||||
"id" : ...
|
||||
}
|
||||
----
|
||||
|
||||
<1> The configured alias is used when writing the entity.
|
||||
====
|
||||
|
||||
NOTE: Type hints will not be written for nested Objects unless the properties type is `Object`, an interface or the actual value type does not match the properties declaration.
|
||||
|
||||
===== Disabling Type Hints
|
||||
|
||||
It may be necessary to disable writing of type hints when the index that should be used already exists without having the type hints defined in its mapping and with the mapping mode set to strict.
|
||||
In this case, writing the type hint will produce an error, as the field cannot be added automatically.
|
||||
|
||||
Type hints can be disabled for the whole application by overriding the method `writeTypeHints()` in a configuration class derived from `AbstractElasticsearchConfiguration` (see <<elasticsearch.clients>>).
|
||||
|
||||
As an alternativ they can be disabled for a single index with the `@Document` annotation:
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Document(indexName = "index", writeTypeHint = WriteTypeHint.FALSE)
|
||||
----
|
||||
====
|
||||
|
||||
WARNING: We strongly advise against disabling Type Hints.
|
||||
Only do this if you are forced to.
|
||||
Disabling type hints can lead to documents not being retrieved correctly from Elasticsearch in case of polymorphic data or document retrieval may fail completely.
|
||||
|
||||
==== Geospatial Types
|
||||
|
||||
Geospatial types like `Point` & `GeoPoint` are converted into _lat/lon_ pairs.
|
||||
@@ -355,6 +425,7 @@ public class Config extends AbstractElasticsearchConfiguration {
|
||||
"localidad" : { "lat" : 34.118347, "lon" : -118.3026284 }
|
||||
}
|
||||
----
|
||||
|
||||
<1> Add `Converter` implementations.
|
||||
<2> Set up the `Converter` used for writing `DomainType` to Elasticsearch.
|
||||
<3> Set up the `Converter` used for reading `DomainType` from search result.
|
||||
|
||||
@@ -20,11 +20,11 @@ The default implementations of the interfaces offer:
|
||||
[NOTE]
|
||||
====
|
||||
.Index management and automatic creation of indices and mappings.
|
||||
The `IndexOperations` interface and the provided implementation which can be obtained from an `ElasticsearchOperations` instance - for example with a call to `operations.indexOps(clazz)`- give the user the ability to create indices, put mappings or store template and alias information in the Elasticsearch cluster.
|
||||
Details of the index that will be created can be set by using the `@Setting` annotation, refer to <<elasticsearc.misc.index.settings>> for further information.
|
||||
|
||||
The `IndexOperations` interface and the provided implementation which can be obtained from an `ElasticsearchOperations` instance - for example with a call to `operations.indexOps(clazz)`- give the user the ability to create indices, put mappings or store template and alias information in the Elasticsearch cluster. Details of the index that will be created
|
||||
can be set by using the `@Setting` annotation, refer to <<elasticsearc.misc.index.settings>> for further information.
|
||||
|
||||
**None of these operations are done automatically** by the implementations of `IndexOperations` or `ElasticsearchOperations`. It is the user's responsibility to call the methods.
|
||||
**None of these operations are done automatically** by the implementations of `IndexOperations` or `ElasticsearchOperations`.
|
||||
It is the user's responsibility to call the methods.
|
||||
|
||||
There is support for automatic creation of indices and writing the mappings when using Spring Data Elasticsearch repositories, see <<elasticsearch.repositories.autocreation>>
|
||||
|
||||
@@ -58,6 +58,7 @@ public class TransportClientConfig extends ElasticsearchConfigurationSupport {
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
<1> Setting up the <<elasticsearch.clients.transport>>.
|
||||
Deprecated as of version 4.0.
|
||||
<2> Creating the `ElasticsearchTemplate` bean, offering both names, _elasticsearchOperations_ and _elasticsearchTemplate_.
|
||||
@@ -82,6 +83,7 @@ public class RestClientConfig extends AbstractElasticsearchConfiguration {
|
||||
// no special bean creation needed <2>
|
||||
}
|
||||
----
|
||||
|
||||
<1> Setting up the <<elasticsearch.clients.rest>>.
|
||||
<2> The base class `AbstractElasticsearchConfiguration` already provides the `elasticsearchTemplate` bean.
|
||||
====
|
||||
@@ -127,6 +129,7 @@ public class TestController {
|
||||
}
|
||||
|
||||
----
|
||||
|
||||
<1> Let Spring inject the provided `ElasticsearchOperations` bean in the constructor.
|
||||
<2> Store some entity in the Elasticsearch cluster.
|
||||
<3> Retrieve the entity with a query by id.
|
||||
@@ -164,6 +167,7 @@ Contains the following information:
|
||||
* Maximum score
|
||||
* A list of `SearchHit<T>` objects
|
||||
* Returned aggregations
|
||||
* Returned suggest results
|
||||
|
||||
.SearchPage<T>
|
||||
Defines a Spring Data `Page` that contains a `SearchHits<T>` element and can be used for paging access using repository methods.
|
||||
@@ -182,12 +186,12 @@ Almost all of the methods defined in the `SearchOperations` and `ReactiveSearchO
|
||||
[[elasticsearch.operations.criteriaquery]]
|
||||
=== CriteriaQuery
|
||||
|
||||
`CriteriaQuery` based queries allow the creation of queries to search for data without knowing the syntax or basics of Elasticsearch queries. They allow the user to build queries by simply chaining and combining `Criteria` objects that specifiy the criteria the searched documents must fulfill.
|
||||
`CriteriaQuery` based queries allow the creation of queries to search for data without knowing the syntax or basics of Elasticsearch queries.
|
||||
They allow the user to build queries by simply chaining and combining `Criteria` objects that specifiy the criteria the searched documents must fulfill.
|
||||
|
||||
NOTE: when talking about AND or OR when combining criteria keep in mind, that in Elasticsearch AND are converted to a **must** condition and OR to a **should**
|
||||
|
||||
`Criteria` and their usage are best explained by example
|
||||
(let's assume we have a `Book` entity with a `price` property):
|
||||
`Criteria` and their usage are best explained by example (let's assume we have a `Book` entity with a `price` property):
|
||||
|
||||
.Get books with a given price
|
||||
====
|
||||
@@ -211,7 +215,7 @@ Query query = new CriteriaQuery(criteria);
|
||||
|
||||
When chaining `Criteria`, by default a AND logic is used:
|
||||
|
||||
.Get all persons with first name _James_ and last name _Miller_:
|
||||
.Get all persons with first name _James_ and last name _Miller_:
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@@ -219,11 +223,13 @@ Criteria criteria = new Criteria("lastname").is("Miller") <1>
|
||||
.and("firstname").is("James") <2>
|
||||
Query query = new CriteriaQuery(criteria);
|
||||
----
|
||||
|
||||
<1> the first `Criteria`
|
||||
<2> the and() creates a new `Criteria` and chaines it to the first one.
|
||||
====
|
||||
|
||||
If you want to create nested queries, you need to use subqueries for this. Let's assume we want to find all persons with a last name of _Miller_ and a first name of either _Jack_ or _John_:
|
||||
If you want to create nested queries, you need to use subqueries for this.
|
||||
Let's assume we want to find all persons with a last name of _Miller_ and a first name of either _Jack_ or _John_:
|
||||
|
||||
.Nested subqueries
|
||||
====
|
||||
@@ -236,6 +242,7 @@ Criteria miller = new Criteria("lastName").is("Miller") <.>
|
||||
);
|
||||
Query query = new CriteriaQuery(criteria);
|
||||
----
|
||||
|
||||
<.> create a first `Criteria` for the last name
|
||||
<.> this is combined with AND to a subCriteria
|
||||
<.> This sub Criteria is an OR combination for the first name _John_
|
||||
@@ -281,5 +288,3 @@ Query query = new NativeSearchQueryBuilder()
|
||||
SearchHits<Person> searchHits = operations.search(query, Person.class);
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
||||
@@ -242,10 +242,6 @@ A list of supported keywords for Elasticsearch is shown below.
|
||||
| `findByNameNotIn(Collection<String>names)`
|
||||
| `{"query": {"bool": {"must": [{"query_string": {"query": "NOT(\"?\" \"?\")", "fields": ["name"]}}]}}}`
|
||||
|
||||
| `Near`
|
||||
| `findByStoreNear`
|
||||
| `Not Supported Yet !`
|
||||
|
||||
| `True`
|
||||
| `findByAvailableTrue`
|
||||
| `{ "query" : {
|
||||
@@ -277,6 +273,26 @@ A list of supported keywords for Elasticsearch is shown below.
|
||||
}, "sort":[{"name":{"order":"desc"}}]
|
||||
}`
|
||||
|
||||
| `Exists`
|
||||
| `findByNameExists`
|
||||
| `{"query":{"bool":{"must":[{"exists":{"field":"name"}}]}}}`
|
||||
|
||||
| `IsNull`
|
||||
| `findByNameIsNull`
|
||||
| `{"query":{"bool":{"must_not":[{"exists":{"field":"name"}}]}}}`
|
||||
|
||||
| `IsNotNull`
|
||||
| `findByNameIsNotNull`
|
||||
| `{"query":{"bool":{"must":[{"exists":{"field":"name"}}]}}}`
|
||||
|
||||
| `IsEmpty`
|
||||
| `findByNameIsEmpty`
|
||||
| `{"query":{"bool":{"must":[{"bool":{"must":[{"exists":{"field":"name"}}],"must_not":[{"wildcard":{"name":{"wildcard":"*"}}}]}}]}}}`
|
||||
|
||||
| `IsNotEmpty`
|
||||
| `findByNameIsNotEmpty`
|
||||
| `{"query":{"bool":{"must":[{"wildcard":{"name":{"wildcard":"*"}}}]}}}`
|
||||
|
||||
|===
|
||||
|
||||
NOTE: Methods names to build Geo-shape queries taking `GeoJson` parameters are not supported.
|
||||
|
||||
@@ -8,4 +8,6 @@ include::elasticsearch-migration-guide-3.2-4.0.adoc[]
|
||||
include::elasticsearch-migration-guide-4.0-4.1.adoc[]
|
||||
|
||||
include::elasticsearch-migration-guide-4.1-4.2.adoc[]
|
||||
|
||||
include::elasticsearch-migration-guide-4.2-4.3.adoc[]
|
||||
:leveloffset: -1
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
|
||||
/**
|
||||
* Exception class for REST status exceptions independent from the used client/backend.
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.3
|
||||
*/
|
||||
public class RestStatusException extends DataAccessException {
|
||||
|
||||
// we do not use a dedicated status class from Elasticsearch, OpenSearch, Spring web or webflux here
|
||||
private final int status;
|
||||
|
||||
public RestStatusException(int status, String msg) {
|
||||
super(msg);
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public RestStatusException(int status, String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public int getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RestStatusException{" + "status=" + status + "} " + super.toString();
|
||||
}
|
||||
}
|
||||
+34
-3
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright 2019-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.annotations;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
@@ -7,12 +22,11 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.elasticsearch.search.suggest.completion.context.ContextMapping;
|
||||
|
||||
/**
|
||||
* Based on reference doc - https://www.elastic.co/guide/en/elasticsearch/reference/current/suggester-context.html
|
||||
*
|
||||
* @author Robert Gruendler
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
@@ -22,9 +36,26 @@ public @interface CompletionContext {
|
||||
|
||||
String name();
|
||||
|
||||
ContextMapping.Type type();
|
||||
ContextMappingType type();
|
||||
|
||||
String precision() default "";
|
||||
|
||||
String path() default "";
|
||||
|
||||
/**
|
||||
* @since 4.3
|
||||
*/
|
||||
enum ContextMappingType {
|
||||
CATEGORY("category"), GEO("geo");
|
||||
|
||||
private final String mappedName;
|
||||
|
||||
ContextMappingType(String mappedName) {
|
||||
this.mappedName = mappedName;
|
||||
}
|
||||
|
||||
public String getMappedName() {
|
||||
return mappedName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -23,13 +23,15 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Based on the reference doc - https://www.elastic.co/guide/en/elasticsearch/reference/current/search-suggesters-completion.html
|
||||
* Based on the reference doc -
|
||||
* https://www.elastic.co/guide/en/elasticsearch/reference/current/search-suggesters-completion.html
|
||||
*
|
||||
* @author Mewes Kochheim
|
||||
* @author Robert Gruendler
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
@Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE })
|
||||
@Documented
|
||||
@Inherited
|
||||
public @interface CompletionField {
|
||||
|
||||
@@ -21,7 +21,6 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.elasticsearch.index.VersionType;
|
||||
import org.springframework.data.annotation.Persistent;
|
||||
|
||||
/**
|
||||
@@ -33,6 +32,7 @@ import org.springframework.data.annotation.Persistent;
|
||||
* @author Ivan Greene
|
||||
* @author Mark Paluch
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Sascha Woo
|
||||
*/
|
||||
@Persistent
|
||||
@Inherited
|
||||
@@ -44,7 +44,7 @@ public @interface Document {
|
||||
* Name of the Elasticsearch index.
|
||||
* <ul>
|
||||
* <li>Lowercase only</li>
|
||||
* <li><Cannot include \, /, *, ?, ", <, >, |, ` ` (space character), ,, #/li>
|
||||
* <li>Cannot include \, /, *, ?, ", >, <, |, ` ` (space character), ,, #</li>
|
||||
* <li>Cannot start with -, _, +</li>
|
||||
* <li>Cannot be . or ..</li>
|
||||
* <li>Cannot be longer than 255 bytes (note it is bytes, so multi-byte characters will count towards the 255 limit
|
||||
@@ -105,4 +105,25 @@ public @interface Document {
|
||||
* Configuration of version management.
|
||||
*/
|
||||
VersionType versionType() default VersionType.EXTERNAL;
|
||||
|
||||
/**
|
||||
* Defines if type hints should be written. {@see WriteTypeHint}.
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
WriteTypeHint writeTypeHint() default WriteTypeHint.DEFAULT;
|
||||
|
||||
/**
|
||||
* Controls how Elasticsearch dynamically adds fields to the document.
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
Dynamic dynamic() default Dynamic.INHERIT;
|
||||
|
||||
/**
|
||||
* @since 4.3
|
||||
*/
|
||||
enum VersionType {
|
||||
INTERNAL, EXTERNAL, EXTERNAL_GTE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.annotations;
|
||||
|
||||
/**
|
||||
* Values for the {@code dynamic} mapping parameter.
|
||||
*
|
||||
* @author Sascha Woo
|
||||
* @since 4.3
|
||||
*/
|
||||
public enum Dynamic {
|
||||
/**
|
||||
* New fields are added to the mapping.
|
||||
*/
|
||||
TRUE("true"),
|
||||
/**
|
||||
* New fields are added to the mapping as
|
||||
* <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/runtime.html">runtime fields</a>. These
|
||||
* fields are not indexed, and are loaded from {@code _source} at query time.
|
||||
*/
|
||||
RUNTIME("runtime"),
|
||||
/**
|
||||
* New fields are ignored. These fields will not be indexed or searchable, but will still appear in the
|
||||
* {@code _source} field of returned hits. These fields will not be added to the mapping, and new fields must be added
|
||||
* explicitly.
|
||||
*/
|
||||
FALSE("false"),
|
||||
/**
|
||||
* If new fields are detected, an exception is thrown and the document is rejected. New fields must be explicitly
|
||||
* added to the mapping.
|
||||
*/
|
||||
STRICT("strict"),
|
||||
/**
|
||||
* Inherit the dynamic setting from their parent object or from the mapping type.
|
||||
*/
|
||||
INHERIT("nherit");
|
||||
|
||||
private final String mappedName;
|
||||
|
||||
Dynamic(String mappedName) {
|
||||
this.mappedName = mappedName;
|
||||
}
|
||||
|
||||
public String getMappedName() {
|
||||
return mappedName;
|
||||
}
|
||||
}
|
||||
@@ -26,11 +26,14 @@ import java.lang.annotation.Target;
|
||||
* {@see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic.html">elasticsearch doc</a>}
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Sascha Woo
|
||||
* @since 4.0
|
||||
* @deprecated since 4.3, use {@link Document#dynamic()} or {@link Field#dynamic()} instead.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.TYPE, ElementType.FIELD })
|
||||
@Documented
|
||||
@Deprecated
|
||||
public @interface DynamicMapping {
|
||||
|
||||
DynamicMappingValue value() default DynamicMappingValue.True;
|
||||
|
||||
+15
-2
@@ -17,10 +17,23 @@ package org.springframework.data.elasticsearch.annotations;
|
||||
|
||||
/**
|
||||
* values for the {@link DynamicMapping annotation}
|
||||
*
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Sascha Woo
|
||||
* @since 4.0
|
||||
* @deprecated since 4.3, use {@link Document#dynamic()} or {@link Field#dynamic()} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public enum DynamicMappingValue {
|
||||
True, False, Strict
|
||||
True("true"), False("false"), Strict("strict");
|
||||
|
||||
private final String mappedName;
|
||||
|
||||
DynamicMappingValue(String mappedName) {
|
||||
this.mappedName = mappedName;
|
||||
}
|
||||
|
||||
public String getMappedName() {
|
||||
return mappedName;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-6
@@ -9,19 +9,17 @@ import java.lang.annotation.Target;
|
||||
import org.springframework.data.annotation.Persistent;
|
||||
|
||||
/**
|
||||
* Elasticsearch dynamic templates mapping.
|
||||
* This annotation is handy if you prefer apply dynamic templates on fields with annotation e.g. {@link Field}
|
||||
* with type = FieldType.Object etc. instead of static mapping on Document via {@link Mapping} annotation.
|
||||
* DynamicTemplates annotation is omitted if {@link Mapping} annotation is used.
|
||||
* Elasticsearch dynamic templates mapping. This annotation is handy if you prefer apply dynamic templates on fields
|
||||
* with annotation e.g. {@link Field} with type = FieldType.Object etc. instead of static mapping on Document via
|
||||
* {@link Mapping} annotation. DynamicTemplates annotation is omitted if {@link Mapping} annotation is used.
|
||||
*
|
||||
* @author Petr Kukral
|
||||
*/
|
||||
@Persistent
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE})
|
||||
@Target({ ElementType.TYPE })
|
||||
public @interface DynamicTemplates {
|
||||
|
||||
String mappingPath() default "";
|
||||
|
||||
}
|
||||
|
||||
@@ -195,4 +195,20 @@ public @interface Field {
|
||||
* @since 4.2
|
||||
*/
|
||||
int dims() default -1;
|
||||
|
||||
/**
|
||||
* Controls how Elasticsearch dynamically adds fields to the inner object within the document.<br>
|
||||
* To be used in combination with {@link FieldType#Object} or {@link FieldType#Nested}
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
Dynamic dynamic() default Dynamic.INHERIT;
|
||||
|
||||
/**
|
||||
* marks this field to be excluded from the _source in Elasticsearch
|
||||
* (https://www.elastic.co/guide/en/elasticsearch/reference/7.15/mapping-source-field.html#include-exclude)
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
boolean excludeFromSource() default false;
|
||||
}
|
||||
|
||||
@@ -26,40 +26,51 @@ package org.springframework.data.elasticsearch.annotations;
|
||||
* @author Morgan Lutz
|
||||
*/
|
||||
public enum FieldType {
|
||||
Auto, //
|
||||
Text, //
|
||||
Keyword, //
|
||||
Long, //
|
||||
Integer, //
|
||||
Short, //
|
||||
Byte, //
|
||||
Double, //
|
||||
Float, //
|
||||
Half_Float, //
|
||||
Scaled_Float, //
|
||||
Date, //
|
||||
Date_Nanos, //
|
||||
Boolean, //
|
||||
Binary, //
|
||||
Integer_Range, //
|
||||
Float_Range, //
|
||||
Long_Range, //
|
||||
Double_Range, //
|
||||
Date_Range, //
|
||||
Ip_Range, //
|
||||
Object, //
|
||||
Nested, //
|
||||
Ip, //
|
||||
TokenCount, //
|
||||
Percolator, //
|
||||
Flattened, //
|
||||
Search_As_You_Type, //
|
||||
Auto("auto"), //
|
||||
Text("text"), //
|
||||
Keyword("keyword"), //
|
||||
Long("long"), //
|
||||
Integer("integer"), //
|
||||
Short("short"), //
|
||||
Byte("byte"), //
|
||||
Double("double"), //
|
||||
Float("float"), //
|
||||
Half_Float("half_float"), //
|
||||
Scaled_Float("scaled_float"), //
|
||||
Date("date"), //
|
||||
Date_Nanos("date_nanos"), //
|
||||
Boolean("boolean"), //
|
||||
Binary("binary"), //
|
||||
Integer_Range("integer_range"), //
|
||||
Float_Range("float_range"), //
|
||||
Long_Range("long_range"), //
|
||||
Double_Range("double_range"), //
|
||||
Date_Range("date_range"), //
|
||||
Ip_Range("ip_range"), //
|
||||
Object("object"), //
|
||||
Nested("nested"), //
|
||||
Ip("ip"), //
|
||||
TokenCount("token_count"), //
|
||||
Percolator("percolator"), //
|
||||
Flattened("flattened"), //
|
||||
Search_As_You_Type("search_as_you_type"), //
|
||||
/** @since 4.1 */
|
||||
Rank_Feature, //
|
||||
Rank_Feature("rank_feature"), //
|
||||
/** @since 4.1 */
|
||||
Rank_Features, //
|
||||
Rank_Features("rank_features"), //
|
||||
/** since 4.2 */
|
||||
Wildcard, //
|
||||
Wildcard("wildcard"), //
|
||||
/** @since 4.2 */
|
||||
Dense_Vector //
|
||||
Dense_Vector("dense_vector") //
|
||||
;
|
||||
|
||||
private final String mappedName;
|
||||
|
||||
FieldType(String mappedName) {
|
||||
this.mappedName = mappedName;
|
||||
}
|
||||
|
||||
public String getMappedName() {
|
||||
return mappedName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.springframework.data.annotation.Persistent;
|
||||
* Elasticsearch Mapping
|
||||
*
|
||||
* @author Mohsin Husen
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
@Persistent
|
||||
@Inherited
|
||||
@@ -38,8 +39,42 @@ public @interface Mapping {
|
||||
|
||||
/**
|
||||
* whether mappings are enabled
|
||||
*
|
||||
*
|
||||
* @since 4.2
|
||||
*/
|
||||
boolean enabled() default true;
|
||||
|
||||
/**
|
||||
* whether date_detection is enabled
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
Detection dateDetection() default Detection.DEFAULT;
|
||||
|
||||
/**
|
||||
* whether numeric_detection is enabled
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
Detection numericDetection() default Detection.DEFAULT;
|
||||
|
||||
/**
|
||||
* custom dynamic date formats
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
String[] dynamicDateFormats() default {};
|
||||
|
||||
/**
|
||||
* classpath to a JSON file containing the values for a runtime mapping definition. The file must contain the JSON
|
||||
* object that is written as the value of the runtime property. {@see <a href=
|
||||
* "https://www.elastic.co/guide/en/elasticsearch/reference/7.12/runtime-mapping-fields.html">elasticsearch doc</a>}
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
String runtimeFieldsPath() default "";
|
||||
|
||||
enum Detection {
|
||||
DEFAULT, TRUE, FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.annotations;
|
||||
|
||||
import org.springframework.data.annotation.QueryAnnotation;
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
@@ -23,11 +24,13 @@ import java.lang.annotation.*;
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Steven Pearce
|
||||
*/
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
|
||||
@Documented
|
||||
@QueryAnnotation
|
||||
public @interface Query {
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.annotations;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.mapping.PropertyValueConverter;
|
||||
|
||||
/**
|
||||
* Annotation to put on a property of an entity to define a value converter which can convert the property to a type
|
||||
* that Elasticsearch understands and back.
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.3
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE })
|
||||
@Documented
|
||||
@Inherited
|
||||
public @interface ValueConverter {
|
||||
|
||||
/**
|
||||
* Defines the class implementing the {@link PropertyValueConverter} interface. If this is a normal class, it must
|
||||
* provide a default constructor with no arguments. If this is an enum and thus implementing a singleton by enum it
|
||||
* must only have one enum value.
|
||||
*
|
||||
* @return the class to use for conversion
|
||||
*/
|
||||
Class<? extends PropertyValueConverter> value();
|
||||
}
|
||||
+18
-14
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2021 the original author or authors.
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,22 +15,26 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.annotations;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
import org.springframework.data.annotation.Persistent;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
|
||||
/**
|
||||
* Parent
|
||||
* Defines if type hints should be written. Used by {@link Document} annotation.
|
||||
*
|
||||
* @author Philipp Jardas
|
||||
* @deprecated since 4.1, not supported anymore by Elasticsearch
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.3
|
||||
*/
|
||||
@Deprecated
|
||||
@Persistent
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface Parent {
|
||||
public enum WriteTypeHint {
|
||||
|
||||
String type();
|
||||
/**
|
||||
* Use the global settings from the {@link MappingContext}.
|
||||
*/
|
||||
DEFAULT,
|
||||
/**
|
||||
* Always write type hints for the entity.
|
||||
*/
|
||||
TRUE,
|
||||
/**
|
||||
* Never write type hints for the entity.
|
||||
*/
|
||||
FALSE
|
||||
}
|
||||
+46
-11
@@ -27,6 +27,7 @@ import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import org.elasticsearch.client.RestClientBuilder.HttpClientConfigCallback;
|
||||
import org.springframework.data.elasticsearch.client.reactive.ReactiveRestClients;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
@@ -120,16 +121,16 @@ public interface ClientConfiguration {
|
||||
boolean useSsl();
|
||||
|
||||
/**
|
||||
* Returns the {@link SSLContext} to use. Can be {@link Optional#empty()} if unconfigured.
|
||||
* Returns the {@link SSLContext} to use. Can be {@link Optional#empty()} if not configured.
|
||||
*
|
||||
* @return the {@link SSLContext} to use. Can be {@link Optional#empty()} if unconfigured.
|
||||
* @return the {@link SSLContext} to use. Can be {@link Optional#empty()} if not configured.
|
||||
*/
|
||||
Optional<SSLContext> getSslContext();
|
||||
|
||||
/**
|
||||
* Returns the {@link HostnameVerifier} to use. Can be {@link Optional#empty()} if unconfigured.
|
||||
* Returns the {@link HostnameVerifier} to use. Can be {@link Optional#empty()} if not configured.
|
||||
*
|
||||
* @return the {@link HostnameVerifier} to use. Can be {@link Optional#empty()} if unconfigured.
|
||||
* @return the {@link HostnameVerifier} to use. Can be {@link Optional#empty()} if not configured.
|
||||
*/
|
||||
Optional<HostnameVerifier> getHostNameVerifier();
|
||||
|
||||
@@ -152,7 +153,7 @@ public interface ClientConfiguration {
|
||||
|
||||
/**
|
||||
* Returns the path prefix that should be prepended to HTTP(s) requests for Elasticsearch behind a proxy.
|
||||
*
|
||||
*
|
||||
* @return the path prefix.
|
||||
* @since 4.0
|
||||
*/
|
||||
@@ -161,7 +162,7 @@ public interface ClientConfiguration {
|
||||
|
||||
/**
|
||||
* returns an optionally set proxy in the form host:port
|
||||
*
|
||||
*
|
||||
* @return the optional proxy
|
||||
* @since 4.0
|
||||
*/
|
||||
@@ -173,11 +174,19 @@ public interface ClientConfiguration {
|
||||
Function<WebClient, WebClient> getWebClientConfigurer();
|
||||
|
||||
/**
|
||||
* @return the client configuration callback.
|
||||
* @return the Rest Client configuration callback.
|
||||
* @since 4.2
|
||||
* @deprecated since 4.3 use {@link #getClientConfigurers()}
|
||||
*/
|
||||
@Deprecated
|
||||
HttpClientConfigCallback getHttpClientConfigurer();
|
||||
|
||||
/**
|
||||
* @return the client configuration callbacks
|
||||
* @since 4.3
|
||||
*/
|
||||
<T> List<ClientConfigurationCallback<?>> getClientConfigurers();
|
||||
|
||||
/**
|
||||
* @return the supplier for custom headers.
|
||||
*/
|
||||
@@ -274,7 +283,7 @@ public interface ClientConfiguration {
|
||||
TerminalClientConfigurationBuilder withDefaultHeaders(HttpHeaders defaultHeaders);
|
||||
|
||||
/**
|
||||
* Configure the {@literal milliseconds} for the connect timeout.
|
||||
* Configure the {@literal milliseconds} for the connect-timeout.
|
||||
*
|
||||
* @param millis the timeout to use.
|
||||
* @return the {@link TerminalClientConfigurationBuilder}
|
||||
@@ -327,7 +336,7 @@ public interface ClientConfiguration {
|
||||
|
||||
/**
|
||||
* Configure the path prefix that will be prepended to any HTTP(s) requests
|
||||
*
|
||||
*
|
||||
* @param pathPrefix the pathPrefix.
|
||||
* @return the {@link TerminalClientConfigurationBuilder}
|
||||
* @since 4.0
|
||||
@@ -342,21 +351,36 @@ public interface ClientConfiguration {
|
||||
|
||||
/**
|
||||
* set customization hook in case of a reactive configuration
|
||||
*
|
||||
*
|
||||
* @param webClientConfigurer function to configure the WebClient
|
||||
* @return the {@link TerminalClientConfigurationBuilder}.
|
||||
* @deprecated since 4.3, use {@link #withClientConfigurer(ClientConfigurationCallback)} with
|
||||
* {@link ReactiveRestClients.WebClientConfigurationCallback}
|
||||
*/
|
||||
@Deprecated
|
||||
TerminalClientConfigurationBuilder withWebClientConfigurer(Function<WebClient, WebClient> webClientConfigurer);
|
||||
|
||||
/**
|
||||
* Register a {HttpClientConfigCallback} to configure the non-reactive REST client.
|
||||
*
|
||||
*
|
||||
* @param httpClientConfigurer configuration callback, must not be null.
|
||||
* @return the {@link TerminalClientConfigurationBuilder}.
|
||||
* @since 4.2
|
||||
* @deprecated since 4.3, use {@link #withClientConfigurer(ClientConfigurationCallback)} with
|
||||
* {@link RestClients.RestClientConfigurationCallback}
|
||||
*/
|
||||
@Deprecated
|
||||
TerminalClientConfigurationBuilder withHttpClientConfigurer(HttpClientConfigCallback httpClientConfigurer);
|
||||
|
||||
/**
|
||||
* Register a {@link ClientConfigurationCallback} to configure the client.
|
||||
*
|
||||
* @param clientConfigurer configuration callback, must not be {@literal null}.
|
||||
* @return the {@link TerminalClientConfigurationBuilder}.
|
||||
* @since 4.3
|
||||
*/
|
||||
TerminalClientConfigurationBuilder withClientConfigurer(ClientConfigurationCallback<?> clientConfigurer);
|
||||
|
||||
/**
|
||||
* set a supplier for custom headers. This is invoked for every HTTP request to Elasticsearch to retrieve headers
|
||||
* that should be sent with the request. A common use case is passing in authentication headers that may change.
|
||||
@@ -377,4 +401,15 @@ public interface ClientConfiguration {
|
||||
*/
|
||||
ClientConfiguration build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to be executed to configure a client.
|
||||
*
|
||||
* @param <T> the type of the client configuration class.
|
||||
* @since 4.3
|
||||
*/
|
||||
@FunctionalInterface
|
||||
interface ClientConfigurationCallback<T> {
|
||||
T configure(T clientConfigurer);
|
||||
}
|
||||
}
|
||||
|
||||
+18
-3
@@ -31,6 +31,7 @@ import org.elasticsearch.client.RestClientBuilder.HttpClientConfigCallback;
|
||||
import org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationBuilderWithRequiredEndpoint;
|
||||
import org.springframework.data.elasticsearch.client.ClientConfiguration.MaybeSecureClientConfigurationBuilder;
|
||||
import org.springframework.data.elasticsearch.client.ClientConfiguration.TerminalClientConfigurationBuilder;
|
||||
import org.springframework.data.elasticsearch.client.reactive.ReactiveRestClients;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -49,7 +50,7 @@ import org.springframework.web.reactive.function.client.WebClient;
|
||||
class ClientConfigurationBuilder
|
||||
implements ClientConfigurationBuilderWithRequiredEndpoint, MaybeSecureClientConfigurationBuilder {
|
||||
|
||||
private List<InetSocketAddress> hosts = new ArrayList<>();
|
||||
private final List<InetSocketAddress> hosts = new ArrayList<>();
|
||||
private HttpHeaders headers = HttpHeaders.EMPTY;
|
||||
private boolean useSsl;
|
||||
private @Nullable SSLContext sslContext;
|
||||
@@ -62,7 +63,8 @@ class ClientConfigurationBuilder
|
||||
private @Nullable String proxy;
|
||||
private Function<WebClient, WebClient> webClientConfigurer = Function.identity();
|
||||
private Supplier<HttpHeaders> headersSupplier = () -> HttpHeaders.EMPTY;
|
||||
private HttpClientConfigCallback httpClientConfigurer = httpClientBuilder -> httpClientBuilder;
|
||||
@Deprecated private HttpClientConfigCallback httpClientConfigurer = httpClientBuilder -> httpClientBuilder;
|
||||
private List<ClientConfiguration.ClientConfigurationCallback<?>> clientConfigurers = new ArrayList<>();
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
@@ -206,6 +208,7 @@ class ClientConfigurationBuilder
|
||||
Assert.notNull(webClientConfigurer, "webClientConfigurer must not be null");
|
||||
|
||||
this.webClientConfigurer = webClientConfigurer;
|
||||
this.clientConfigurers.add(ReactiveRestClients.WebClientConfigurationCallback.from(webClientConfigurer));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -215,6 +218,18 @@ class ClientConfigurationBuilder
|
||||
Assert.notNull(httpClientConfigurer, "httpClientConfigurer must not be null");
|
||||
|
||||
this.httpClientConfigurer = httpClientConfigurer;
|
||||
this.clientConfigurers
|
||||
.add(RestClients.RestClientConfigurationCallback.from(httpClientConfigurer::customizeHttpClient));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminalClientConfigurationBuilder withClientConfigurer(
|
||||
ClientConfiguration.ClientConfigurationCallback<?> clientConfigurer) {
|
||||
|
||||
Assert.notNull(clientConfigurer, "clientConfigurer must not be null");
|
||||
|
||||
this.clientConfigurers.add(clientConfigurer);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -242,7 +257,7 @@ class ClientConfigurationBuilder
|
||||
}
|
||||
|
||||
return new DefaultClientConfiguration(hosts, headers, useSsl, sslContext, soTimeout, connectTimeout, pathPrefix,
|
||||
hostnameVerifier, proxy, webClientConfigurer, httpClientConfigurer, headersSupplier);
|
||||
hostnameVerifier, proxy, webClientConfigurer, httpClientConfigurer, clientConfigurers, headersSupplier);
|
||||
}
|
||||
|
||||
private static InetSocketAddress parse(String hostAndPort) {
|
||||
|
||||
@@ -32,7 +32,10 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @since 3.1
|
||||
* @deprecated only used in {@link TransportClientFactoryBean}.
|
||||
*/
|
||||
@SuppressWarnings("DeprecatedIsStillUsed")
|
||||
@Deprecated
|
||||
class ClusterNodes implements Streamable<TransportAddress> {
|
||||
|
||||
public static ClusterNodes DEFAULT = ClusterNodes.of("127.0.0.1:9300");
|
||||
@@ -44,7 +47,7 @@ class ClusterNodes implements Streamable<TransportAddress> {
|
||||
|
||||
/**
|
||||
* Creates a new {@link ClusterNodes} by parsing the given source.
|
||||
*
|
||||
*
|
||||
* @param source must not be {@literal null} or empty.
|
||||
*/
|
||||
private ClusterNodes(String source) {
|
||||
@@ -74,15 +77,14 @@ class ClusterNodes implements Streamable<TransportAddress> {
|
||||
/**
|
||||
* Creates a new {@link ClusterNodes} by parsing the given source. The expected format is a comma separated list of
|
||||
* host-port-combinations separated by a colon: {@code host:port,host:port,…}.
|
||||
*
|
||||
*
|
||||
* @param source must not be {@literal null} or empty.
|
||||
* @return
|
||||
*/
|
||||
public static ClusterNodes of(String source) {
|
||||
return new ClusterNodes(source);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Iterable#iterator()
|
||||
*/
|
||||
|
||||
+9
-1
@@ -55,12 +55,13 @@ class DefaultClientConfiguration implements ClientConfiguration {
|
||||
private final Function<WebClient, WebClient> webClientConfigurer;
|
||||
private final HttpClientConfigCallback httpClientConfigurer;
|
||||
private final Supplier<HttpHeaders> headersSupplier;
|
||||
private final List<ClientConfigurationCallback<?>> clientConfigurers;
|
||||
|
||||
DefaultClientConfiguration(List<InetSocketAddress> hosts, HttpHeaders headers, boolean useSsl,
|
||||
@Nullable SSLContext sslContext, Duration soTimeout, Duration connectTimeout, @Nullable String pathPrefix,
|
||||
@Nullable HostnameVerifier hostnameVerifier, @Nullable String proxy,
|
||||
Function<WebClient, WebClient> webClientConfigurer, HttpClientConfigCallback httpClientConfigurer,
|
||||
Supplier<HttpHeaders> headersSupplier) {
|
||||
List<ClientConfigurationCallback<?>> clientConfigurers, Supplier<HttpHeaders> headersSupplier) {
|
||||
|
||||
this.hosts = Collections.unmodifiableList(new ArrayList<>(hosts));
|
||||
this.headers = new HttpHeaders(headers);
|
||||
@@ -73,6 +74,7 @@ class DefaultClientConfiguration implements ClientConfiguration {
|
||||
this.proxy = proxy;
|
||||
this.webClientConfigurer = webClientConfigurer;
|
||||
this.httpClientConfigurer = httpClientConfigurer;
|
||||
this.clientConfigurers = clientConfigurers;
|
||||
this.headersSupplier = headersSupplier;
|
||||
}
|
||||
|
||||
@@ -132,6 +134,12 @@ class DefaultClientConfiguration implements ClientConfiguration {
|
||||
return httpClientConfigurer;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T> List<ClientConfigurationCallback<?>> getClientConfigurers() {
|
||||
return clientConfigurers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Supplier<HttpHeaders> getHeadersSupplier() {
|
||||
return headersSupplier;
|
||||
|
||||
-173
@@ -1,173 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.client.node.NodeClient;
|
||||
import org.elasticsearch.common.logging.LogConfigurator;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.node.InternalSettingsPreparer;
|
||||
import org.elasticsearch.node.Node;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.transport.Netty4Plugin;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.FactoryBeanNotInitializedException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* NodeClientFactoryBean
|
||||
*
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Ilkang Na
|
||||
* @author Peter-Josef Meisch
|
||||
* @deprecated since 4.1, we're not supporting embedded Node clients anymore, use the REST client
|
||||
*/
|
||||
@Deprecated
|
||||
public class NodeClientFactoryBean implements FactoryBean<Client>, InitializingBean, DisposableBean {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(NodeClientFactoryBean.class);
|
||||
private boolean local;
|
||||
private boolean enableHttp;
|
||||
private @Nullable String clusterName;
|
||||
private @Nullable Node node;
|
||||
private @Nullable NodeClient nodeClient;
|
||||
private @Nullable String pathData;
|
||||
private @Nullable String pathHome;
|
||||
private @Nullable String pathConfiguration;
|
||||
|
||||
public static class TestNode extends Node {
|
||||
|
||||
private static final String DEFAULT_NODE_NAME = "spring-data-elasticsearch-nodeclientfactorybean-test";
|
||||
|
||||
public TestNode(Settings preparedSettings, Collection<Class<? extends Plugin>> classpathPlugins) {
|
||||
|
||||
super(InternalSettingsPreparer.prepareEnvironment(preparedSettings, Collections.emptyMap(), null,
|
||||
() -> DEFAULT_NODE_NAME), classpathPlugins, false);
|
||||
}
|
||||
|
||||
protected void registerDerivedNodeNameWithLogger(String nodeName) {
|
||||
try {
|
||||
LogConfigurator.setNodeName(nodeName);
|
||||
} catch (Exception e) {
|
||||
// nagh - just forget about it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NodeClientFactoryBean() {}
|
||||
|
||||
public NodeClientFactoryBean(boolean local) {
|
||||
this.local = local;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeClient getObject() {
|
||||
|
||||
if (nodeClient == null) {
|
||||
throw new FactoryBeanNotInitializedException();
|
||||
}
|
||||
|
||||
return nodeClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Client> getObjectType() {
|
||||
return NodeClient.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
|
||||
Settings settings = Settings.builder() //
|
||||
.put(loadConfig()) //
|
||||
.put("transport.type", "netty4") //
|
||||
.put("http.type", "netty4") //
|
||||
.put("path.home", this.pathHome) //
|
||||
.put("path.data", this.pathData) //
|
||||
.put("cluster.name", this.clusterName) //
|
||||
.put("node.max_local_storage_nodes", 100) //
|
||||
.build();
|
||||
node = new TestNode(settings, Collections.singletonList(Netty4Plugin.class));
|
||||
nodeClient = (NodeClient) node.start().client();
|
||||
}
|
||||
|
||||
private Settings loadConfig() throws IOException {
|
||||
if (!StringUtils.isEmpty(pathConfiguration)) {
|
||||
InputStream stream = getClass().getClassLoader().getResourceAsStream(pathConfiguration);
|
||||
if (stream != null) {
|
||||
return Settings.builder().loadFromStream(pathConfiguration,
|
||||
getClass().getClassLoader().getResourceAsStream(pathConfiguration), false).build();
|
||||
}
|
||||
logger.error(String.format("Unable to read node configuration from file [%s]", pathConfiguration));
|
||||
}
|
||||
return Settings.builder().build();
|
||||
}
|
||||
|
||||
public void setLocal(boolean local) {
|
||||
this.local = local;
|
||||
}
|
||||
|
||||
public void setEnableHttp(boolean enableHttp) {
|
||||
this.enableHttp = enableHttp;
|
||||
}
|
||||
|
||||
public void setClusterName(String clusterName) {
|
||||
this.clusterName = clusterName;
|
||||
}
|
||||
|
||||
public void setPathData(String pathData) {
|
||||
this.pathData = pathData;
|
||||
}
|
||||
|
||||
public void setPathHome(String pathHome) {
|
||||
this.pathHome = pathHome;
|
||||
}
|
||||
|
||||
public void setPathConfiguration(String configuration) {
|
||||
this.pathConfiguration = configuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
try {
|
||||
// NodeClient.close() is a noop, no need to call it here
|
||||
nodeClient = null;
|
||||
logger.info("Closing elasticSearch node");
|
||||
if (node != null) {
|
||||
node.close();
|
||||
node = null;
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
logger.error("Error closing ElasticSearch client: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import java.net.InetSocketAddress;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -36,6 +37,7 @@ import org.apache.http.HttpResponseInterceptor;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.config.RequestConfig.Builder;
|
||||
import org.apache.http.entity.ByteArrayEntity;
|
||||
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
|
||||
import org.apache.http.message.BasicHeader;
|
||||
import org.apache.http.protocol.HttpContext;
|
||||
import org.elasticsearch.client.RestClient;
|
||||
@@ -54,6 +56,7 @@ import org.springframework.util.Assert;
|
||||
* @author Huw Ayling-Miller
|
||||
* @author Henrique Amaral
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Nic Hines
|
||||
* @since 3.2
|
||||
*/
|
||||
public final class RestClients {
|
||||
@@ -104,22 +107,27 @@ public final class RestClients {
|
||||
Duration connectTimeout = clientConfiguration.getConnectTimeout();
|
||||
|
||||
if (!connectTimeout.isNegative()) {
|
||||
|
||||
requestConfigBuilder.setConnectTimeout(Math.toIntExact(connectTimeout.toMillis()));
|
||||
requestConfigBuilder.setConnectionRequestTimeout(Math.toIntExact(connectTimeout.toMillis()));
|
||||
}
|
||||
|
||||
Duration timeout = clientConfiguration.getSocketTimeout();
|
||||
Duration socketTimeout = clientConfiguration.getSocketTimeout();
|
||||
|
||||
if (!timeout.isNegative()) {
|
||||
requestConfigBuilder.setSocketTimeout(Math.toIntExact(timeout.toMillis()));
|
||||
if (!socketTimeout.isNegative()) {
|
||||
requestConfigBuilder.setSocketTimeout(Math.toIntExact(socketTimeout.toMillis()));
|
||||
requestConfigBuilder.setConnectionRequestTimeout(Math.toIntExact(socketTimeout.toMillis()));
|
||||
}
|
||||
|
||||
clientBuilder.setDefaultRequestConfig(requestConfigBuilder.build());
|
||||
|
||||
clientConfiguration.getProxy().map(HttpHost::create).ifPresent(clientBuilder::setProxy);
|
||||
|
||||
clientBuilder = clientConfiguration.getHttpClientConfigurer().customizeHttpClient(clientBuilder);
|
||||
for (ClientConfiguration.ClientConfigurationCallback<?> clientConfigurer : clientConfiguration
|
||||
.getClientConfigurers()) {
|
||||
if (clientConfigurer instanceof RestClientConfigurationCallback) {
|
||||
RestClientConfigurationCallback restClientConfigurationCallback = (RestClientConfigurationCallback) clientConfigurer;
|
||||
clientBuilder = restClientConfigurationCallback.configure(clientBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
return clientBuilder;
|
||||
});
|
||||
@@ -198,7 +206,7 @@ public final class RestClients {
|
||||
}
|
||||
|
||||
ClientLogger.logRequest(logId, request.getRequestLine().getMethod(), request.getRequestLine().getUri(), "",
|
||||
() -> new String(buffer.toByteArray()));
|
||||
buffer::toString);
|
||||
} else {
|
||||
ClientLogger.logRequest(logId, request.getRequestLine().getMethod(), request.getRequestLine().getUri(), "");
|
||||
}
|
||||
@@ -213,7 +221,7 @@ public final class RestClients {
|
||||
|
||||
/**
|
||||
* Interceptor to inject custom supplied headers.
|
||||
*
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
private static class CustomHeaderInjector implements HttpRequestInterceptor {
|
||||
@@ -233,4 +241,23 @@ public final class RestClients {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationCallback} to configure
|
||||
* the RestClient with a {@link HttpAsyncClientBuilder}
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
public interface RestClientConfigurationCallback
|
||||
extends ClientConfiguration.ClientConfigurationCallback<HttpAsyncClientBuilder> {
|
||||
|
||||
static RestClientConfigurationCallback from(
|
||||
Function<HttpAsyncClientBuilder, HttpAsyncClientBuilder> clientBuilderCallback) {
|
||||
|
||||
Assert.notNull(clientBuilderCallback, "clientBuilderCallback must not be null");
|
||||
|
||||
// noinspection NullableProblems
|
||||
return clientBuilderCallback::apply;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+24
-11
@@ -83,11 +83,11 @@ import org.elasticsearch.action.update.UpdateResponse;
|
||||
import org.elasticsearch.client.GetAliasesResponse;
|
||||
import org.elasticsearch.client.Request;
|
||||
import org.elasticsearch.client.indices.*;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.DeprecationHandler;
|
||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.core.TimeValue;
|
||||
import org.elasticsearch.index.get.GetResult;
|
||||
import org.elasticsearch.index.reindex.BulkByScrollResponse;
|
||||
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
|
||||
@@ -101,6 +101,7 @@ import org.elasticsearch.search.SearchHits;
|
||||
import org.elasticsearch.search.aggregations.Aggregation;
|
||||
import org.elasticsearch.search.suggest.Suggest;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.elasticsearch.RestStatusException;
|
||||
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
|
||||
import org.springframework.data.elasticsearch.client.ClientConfiguration;
|
||||
import org.springframework.data.elasticsearch.client.ClientLogger;
|
||||
@@ -111,6 +112,7 @@ import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsea
|
||||
import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient.Indices;
|
||||
import org.springframework.data.elasticsearch.client.util.NamedXContents;
|
||||
import org.springframework.data.elasticsearch.client.util.ScrollState;
|
||||
import org.springframework.data.elasticsearch.core.ResponseConverter;
|
||||
import org.springframework.data.elasticsearch.core.query.ByQueryResponse;
|
||||
import org.springframework.data.util.Lazy;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@@ -287,9 +289,21 @@ public class DefaultReactiveElasticsearchClient implements ReactiveElasticsearch
|
||||
provider = provider.withPathPrefix(clientConfiguration.getPathPrefix());
|
||||
}
|
||||
|
||||
Function<WebClient, WebClient> webClientConfigurer = webClient -> {
|
||||
for (ClientConfiguration.ClientConfigurationCallback<?> clientConfigurer : clientConfiguration
|
||||
.getClientConfigurers()) {
|
||||
|
||||
if (clientConfigurer instanceof ReactiveRestClients.WebClientConfigurationCallback) {
|
||||
ReactiveRestClients.WebClientConfigurationCallback webClientConfigurationCallback = (ReactiveRestClients.WebClientConfigurationCallback) clientConfigurer;
|
||||
webClient = webClientConfigurationCallback.configure(webClient);
|
||||
}
|
||||
}
|
||||
return webClient;
|
||||
};
|
||||
|
||||
provider = provider //
|
||||
.withDefaultHeaders(clientConfiguration.getDefaultHeaders()) //
|
||||
.withWebClientConfigurer(clientConfiguration.getWebClientConfigurer()) //
|
||||
.withWebClientConfigurer(webClientConfigurer) //
|
||||
.withRequestConfigurer(requestHeadersSpec -> requestHeadersSpec.headers(httpHeaders -> {
|
||||
HttpHeaders suppliedHeaders = clientConfiguration.getHeadersSupplier().get();
|
||||
|
||||
@@ -485,7 +499,7 @@ public class DefaultReactiveElasticsearchClient implements ReactiveElasticsearch
|
||||
public Mono<ByQueryResponse> updateBy(HttpHeaders headers, UpdateByQueryRequest updateRequest) {
|
||||
return sendRequest(updateRequest, requestCreator.updateByQuery(), BulkByScrollResponse.class, headers) //
|
||||
.next() //
|
||||
.map(ByQueryResponse::of);
|
||||
.map(ResponseConverter::byQueryResponseOf);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -823,8 +837,7 @@ public class DefaultReactiveElasticsearchClient implements ReactiveElasticsearch
|
||||
return Mono.error(BytesRestResponse.errorFromXContent(createParser(mediaType, content)));
|
||||
} catch (Exception e) {
|
||||
|
||||
return Mono
|
||||
.error(new ElasticsearchStatusException(content, RestStatus.fromCode(response.statusCode().value())));
|
||||
return Mono.error(new RestStatusException(response.statusCode().value(), content));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -857,14 +870,14 @@ public class DefaultReactiveElasticsearchClient implements ReactiveElasticsearch
|
||||
String mediaType = response.headers().contentType().map(MediaType::toString).orElse(XContentType.JSON.mediaType());
|
||||
|
||||
return response.body(BodyExtractors.toMono(byte[].class)) //
|
||||
.switchIfEmpty(Mono.error(
|
||||
new ElasticsearchStatusException(String.format("%s request to %s returned error code %s and no body.",
|
||||
request.getMethod(), request.getEndpoint(), statusCode), status)))
|
||||
.switchIfEmpty(Mono.error(new RestStatusException(status.getStatus(),
|
||||
String.format("%s request to %s returned error code %s and no body.", request.getMethod(),
|
||||
request.getEndpoint(), statusCode))))
|
||||
.map(bytes -> new String(bytes, StandardCharsets.UTF_8)) //
|
||||
.flatMap(content -> contentOrError(content, mediaType, status))
|
||||
.flatMap(unused -> Mono
|
||||
.error(new ElasticsearchStatusException(String.format("%s request to %s returned error code %s.",
|
||||
request.getMethod(), request.getEndpoint(), statusCode), status)));
|
||||
.error(new RestStatusException(status.getStatus(), String.format("%s request to %s returned error code %s.",
|
||||
request.getMethod(), request.getEndpoint(), statusCode))));
|
||||
}
|
||||
|
||||
private <T> Publisher<? extends T> handleClientError(String logId, ClientResponse response, Class<T> responseType) {
|
||||
@@ -896,7 +909,7 @@ public class DefaultReactiveElasticsearchClient implements ReactiveElasticsearch
|
||||
if (exception != null) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
buildExceptionMessages(sb, exception);
|
||||
return Mono.error(new ElasticsearchStatusException(sb.toString(), status, exception));
|
||||
return Mono.error(new RestStatusException(status.getStatus(), sb.toString(), exception));
|
||||
}
|
||||
|
||||
return Mono.just(content);
|
||||
|
||||
-52
@@ -1102,58 +1102,6 @@ public interface ReactiveElasticsearchClient {
|
||||
*/
|
||||
Mono<Void> refreshIndex(HttpHeaders headers, RefreshRequest refreshRequest);
|
||||
|
||||
/**
|
||||
* Execute the given {@link org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest} against the
|
||||
* {@literal indices} API.
|
||||
*
|
||||
* @param consumer never {@literal null}.
|
||||
* @return a {@link Mono} signalling operation completion or an {@link Mono#error(Throwable) error} if eg. the index
|
||||
* does not exist.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-put-mapping.html"> Indices
|
||||
* Put Mapping API on elastic.co</a>
|
||||
* @deprecated since 4.1, use {@link #putMapping(Consumer)}
|
||||
*/
|
||||
@Deprecated
|
||||
default Mono<Boolean> updateMapping(
|
||||
Consumer<org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest> consumer) {
|
||||
return putMapping(consumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest} against the
|
||||
* {@literal indices} API.
|
||||
*
|
||||
* @param putMappingRequest must not be {@literal null}.
|
||||
* @return a {@link Mono} signalling operation completion or an {@link Mono#error(Throwable) error} if eg. the index
|
||||
* does not exist.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-put-mapping.html"> Indices
|
||||
* Put Mapping API on elastic.co</a>
|
||||
* @deprecated since 4.1, use {@link #putMapping(PutMappingRequest)}
|
||||
*/
|
||||
@Deprecated
|
||||
default Mono<Boolean> updateMapping(
|
||||
org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest putMappingRequest) {
|
||||
return putMapping(putMappingRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest} against the
|
||||
* {@literal indices} API.
|
||||
*
|
||||
* @param headers Use {@link HttpHeaders} to provide eg. authentication data. Must not be {@literal null}.
|
||||
* @param putMappingRequest must not be {@literal null}.
|
||||
* @return a {@link Mono} signalling operation completion or an {@link Mono#error(Throwable) error} if eg. the index
|
||||
* does not exist.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-put-mapping.html"> Indices
|
||||
* Put Mapping API on elastic.co</a>
|
||||
* @deprecated since 4.1, use {@link #putMapping(HttpHeaders, PutMappingRequest)}
|
||||
*/
|
||||
@Deprecated
|
||||
default Mono<Boolean> updateMapping(HttpHeaders headers,
|
||||
org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest putMappingRequest) {
|
||||
return putMapping(headers, putMappingRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest} against the
|
||||
* {@literal indices} API.
|
||||
|
||||
+20
@@ -15,8 +15,11 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.client.reactive;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.data.elasticsearch.client.ClientConfiguration;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* Utility class for common access to reactive Elasticsearch clients. {@link ReactiveRestClients} consolidates set up
|
||||
@@ -61,4 +64,21 @@ public final class ReactiveRestClients {
|
||||
|
||||
return DefaultReactiveElasticsearchClient.create(clientConfiguration, requestCreator);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationCallback} to configure
|
||||
* the ReactiveElasticsearchClient with a {@link WebClient}
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
public interface WebClientConfigurationCallback extends ClientConfiguration.ClientConfigurationCallback<WebClient> {
|
||||
|
||||
static WebClientConfigurationCallback from(Function<WebClient, WebClient> webClientCallback) {
|
||||
|
||||
Assert.notNull(webClientCallback, "webClientCallback must not be null");
|
||||
|
||||
// noinspection NullableProblems
|
||||
return webClientCallback::apply;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -26,9 +26,9 @@ import org.elasticsearch.client.analytics.ParsedStringStats;
|
||||
import org.elasticsearch.client.analytics.ParsedTopMetrics;
|
||||
import org.elasticsearch.client.analytics.StringStatsAggregationBuilder;
|
||||
import org.elasticsearch.client.analytics.TopMetricsAggregationBuilder;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.xcontent.ContextParser;
|
||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||
import org.elasticsearch.common.xcontent.ParseField;
|
||||
import org.elasticsearch.search.aggregations.Aggregation;
|
||||
import org.elasticsearch.search.aggregations.bucket.adjacency.AdjacencyMatrixAggregationBuilder;
|
||||
import org.elasticsearch.search.aggregations.bucket.adjacency.ParsedAdjacencyMatrix;
|
||||
|
||||
+20
-26
@@ -78,10 +78,8 @@ import org.elasticsearch.client.indices.PutIndexTemplateRequest;
|
||||
import org.elasticsearch.cluster.health.ClusterHealthStatus;
|
||||
import org.elasticsearch.common.Priority;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.SuppressForbidden;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.lucene.uid.Versions;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.DeprecationHandler;
|
||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
@@ -90,6 +88,8 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.core.SuppressForbidden;
|
||||
import org.elasticsearch.core.TimeValue;
|
||||
import org.elasticsearch.index.VersionType;
|
||||
import org.elasticsearch.index.mapper.MapperService;
|
||||
import org.elasticsearch.index.rankeval.RankEvalRequest;
|
||||
@@ -336,8 +336,13 @@ public class RequestConverters {
|
||||
public static Request index(IndexRequest indexRequest) {
|
||||
String method = Strings.hasLength(indexRequest.id()) ? HttpMethod.PUT.name() : HttpMethod.POST.name();
|
||||
boolean isCreate = (indexRequest.opType() == DocWriteRequest.OpType.CREATE);
|
||||
String endpoint = endpoint(indexRequest.index(), indexRequest.type(), indexRequest.id(),
|
||||
isCreate ? "_create" : null);
|
||||
String endpoint;
|
||||
if (indexRequest.opType() == DocWriteRequest.OpType.CREATE) {
|
||||
endpoint = indexRequest.type().equals("_doc") ? endpoint(indexRequest.index(), "_create", indexRequest.id())
|
||||
: endpoint(indexRequest.index(), indexRequest.type(), indexRequest.id(), "_create");
|
||||
} else {
|
||||
endpoint = endpoint(indexRequest.index(), indexRequest.type(), indexRequest.id());
|
||||
}
|
||||
Request request = new Request(method, endpoint);
|
||||
|
||||
Params parameters = new Params(request);
|
||||
@@ -362,7 +367,9 @@ public class RequestConverters {
|
||||
}
|
||||
|
||||
public static Request update(UpdateRequest updateRequest) {
|
||||
String endpoint = endpoint(updateRequest.index(), updateRequest.type(), updateRequest.id(), "_update");
|
||||
String endpoint = updateRequest.type().equals("_doc")
|
||||
? endpoint(updateRequest.index(), "_update", updateRequest.id())
|
||||
: endpoint(updateRequest.index(), updateRequest.type(), updateRequest.id(), "_update");
|
||||
Request request = new Request(HttpMethod.POST.name(), endpoint);
|
||||
|
||||
Params parameters = new Params(request);
|
||||
@@ -500,8 +507,11 @@ public class RequestConverters {
|
||||
}
|
||||
|
||||
public static Request explain(ExplainRequest explainRequest) {
|
||||
String endpoint = explainRequest.type().equals("_doc")
|
||||
? endpoint(explainRequest.index(), "_explain", explainRequest.id())
|
||||
: endpoint(explainRequest.index(), explainRequest.type(), explainRequest.id(), "_explain");
|
||||
Request request = new Request(HttpMethod.GET.name(),
|
||||
endpoint(explainRequest.index(), explainRequest.type(), explainRequest.id(), "_explain"));
|
||||
endpoint(explainRequest.index(), explainRequest.type(), explainRequest.id(), endpoint));
|
||||
|
||||
Params params = new Params(request);
|
||||
params.withStoredFields(explainRequest.storedFields());
|
||||
@@ -841,8 +851,7 @@ public class RequestConverters {
|
||||
|
||||
RequestConverters.Params parameters = new RequestConverters.Params(request) //
|
||||
.withTimeout(putMappingRequest.timeout()) //
|
||||
.withMasterTimeout(putMappingRequest.masterNodeTimeout()) //
|
||||
.withIncludeTypeName(false);
|
||||
.withMasterTimeout(putMappingRequest.masterNodeTimeout());
|
||||
request.setEntity(RequestConverters.createEntity(putMappingRequest, RequestConverters.REQUEST_BODY_CONTENT_TYPE));
|
||||
return request;
|
||||
}
|
||||
@@ -853,8 +862,7 @@ public class RequestConverters {
|
||||
|
||||
new RequestConverters.Params(request) //
|
||||
.withTimeout(putMappingRequest.timeout()) //
|
||||
.withMasterTimeout(putMappingRequest.masterNodeTimeout()) //
|
||||
.withIncludeTypeName(false);
|
||||
.withMasterTimeout(putMappingRequest.masterNodeTimeout());
|
||||
request.setEntity(RequestConverters.createEntity(putMappingRequest, RequestConverters.REQUEST_BODY_CONTENT_TYPE));
|
||||
return request;
|
||||
}
|
||||
@@ -880,7 +888,6 @@ public class RequestConverters {
|
||||
parameters.withMasterTimeout(getMappingsRequest.masterNodeTimeout());
|
||||
parameters.withIndicesOptions(getMappingsRequest.indicesOptions());
|
||||
parameters.withLocal(getMappingsRequest.local());
|
||||
parameters.withIncludeTypeName(false);
|
||||
return request;
|
||||
}
|
||||
|
||||
@@ -893,7 +900,6 @@ public class RequestConverters {
|
||||
parameters.withMasterTimeout(getMappingsRequest.masterNodeTimeout());
|
||||
parameters.withIndicesOptions(getMappingsRequest.indicesOptions());
|
||||
parameters.withLocal(getMappingsRequest.local());
|
||||
parameters.withIncludeTypeName(false);
|
||||
return request;
|
||||
}
|
||||
|
||||
@@ -1000,7 +1006,6 @@ public class RequestConverters {
|
||||
RequestConverters.Params parameters = new Params(request);
|
||||
parameters.withIndicesOptions(getFieldMappingsRequest.indicesOptions());
|
||||
parameters.withIncludeDefaults(getFieldMappingsRequest.includeDefaults());
|
||||
parameters.withIncludeTypeName(false);
|
||||
return request;
|
||||
}
|
||||
|
||||
@@ -1372,18 +1377,6 @@ public class RequestConverters {
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* sets the include_type_name parameter. Needed for Elasticsearch 7 to be used with the mapping types still
|
||||
* available. Will be removed again when Elasticsearch drops the support for this parameter in Elasticsearch 8.
|
||||
*
|
||||
* @deprecated since 4.0
|
||||
* @since 4.0
|
||||
*/
|
||||
@Deprecated
|
||||
Params withIncludeTypeName(boolean includeTypeName) {
|
||||
return putParam("include_type_name", String.valueOf(includeTypeName));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1452,7 +1445,8 @@ public class RequestConverters {
|
||||
// encode each part (e.g. index, type and id) separately before merging them into the path
|
||||
// we prepend "/" to the path part to make this path absolute, otherwise there can be issues with
|
||||
// paths that start with `-` or contain `:`
|
||||
URI uri = new URI(null, null, null, -1, '/' + pathPart, null, null);
|
||||
// the authority must be an empty string and not null, else paths that being with slashes could have them
|
||||
URI uri = new URI((String) null, "", "/" + pathPart, (String) null, (String) null);
|
||||
// manually encode any slash that each part may contain
|
||||
return uri.getRawPath().substring(1).replaceAll("/", "%2F");
|
||||
} catch (URISyntaxException e) {
|
||||
|
||||
@@ -21,13 +21,11 @@ import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.elasticsearch.action.search.SearchScrollRequest;
|
||||
import org.elasticsearch.search.Scroll;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Mutable state object holding scrollId to be used for {@link SearchScrollRequest#scroll(Scroll)}
|
||||
* Mutable state object holding scrollId to be used for scroll requests.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Peter-Josef Meisch
|
||||
|
||||
+14
-1
@@ -26,7 +26,6 @@ import org.springframework.context.annotation.ClassPathScanningCandidateComponen
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
import org.springframework.data.annotation.Persistent;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.core.RefreshPolicy;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||
@@ -72,6 +71,7 @@ public class ElasticsearchConfigurationSupport {
|
||||
mappingContext.setInitialEntitySet(getInitialEntitySet());
|
||||
mappingContext.setSimpleTypeHolder(elasticsearchCustomConversions.getSimpleTypeHolder());
|
||||
mappingContext.setFieldNamingStrategy(fieldNamingStrategy());
|
||||
mappingContext.setWriteTypeHints(writeTypeHints());
|
||||
|
||||
return mappingContext;
|
||||
}
|
||||
@@ -171,4 +171,17 @@ public class ElasticsearchConfigurationSupport {
|
||||
protected FieldNamingStrategy fieldNamingStrategy() {
|
||||
return PropertyNameFieldNamingStrategy.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag specifiying if type hints (_class fields) should be written in the index. It is strongly advised to keep the
|
||||
* default value of {@literal true}. If you need to write to an existing index that does not have a mapping defined
|
||||
* for these fields and that has a strict mapping set, then it might be necessary to disable type hints. But notice
|
||||
* that in this case reading polymorphic types may fail.
|
||||
*
|
||||
* @return flag if type hints should be written
|
||||
* @since 4.3
|
||||
*/
|
||||
protected boolean writeTypeHints() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
-1
@@ -35,7 +35,6 @@ public class ElasticsearchNamespaceHandler extends NamespaceHandlerSupport {
|
||||
RepositoryBeanDefinitionParser parser = new RepositoryBeanDefinitionParser(extension);
|
||||
|
||||
registerBeanDefinitionParser("repositories", parser);
|
||||
registerBeanDefinitionParser("node-client", new NodeClientBeanDefinitionParser());
|
||||
registerBeanDefinitionParser("transport-client", new TransportClientBeanDefinitionParser());
|
||||
registerBeanDefinitionParser("rest-client", new RestClientBeanDefinitionParser());
|
||||
}
|
||||
|
||||
-10
@@ -41,32 +41,22 @@ public @interface EnableElasticsearchAuditing {
|
||||
|
||||
/**
|
||||
* Configures the {@link AuditorAware} bean to be used to lookup the current principal.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String auditorAwareRef() default "";
|
||||
|
||||
/**
|
||||
* Configures whether the creation and modification dates are set. Defaults to {@literal true}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean setDates() default true;
|
||||
|
||||
/**
|
||||
* Configures whether the entity shall be marked as modified on creation. Defaults to {@literal true}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean modifyOnCreate() default true;
|
||||
|
||||
/**
|
||||
* Configures a {@link DateTimeProvider} bean name that allows customizing the {@link org.joda.time.DateTime} to be
|
||||
* used for setting creation and modification dates.
|
||||
*
|
||||
* @return
|
||||
* @deprecated since 4.1
|
||||
*/
|
||||
@Deprecated
|
||||
String dateTimeProviderRef() default "";
|
||||
}
|
||||
|
||||
-10
@@ -41,32 +41,22 @@ public @interface EnableReactiveElasticsearchAuditing {
|
||||
|
||||
/**
|
||||
* Configures the {@link AuditorAware} bean to be used to lookup the current principal.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String auditorAwareRef() default "";
|
||||
|
||||
/**
|
||||
* Configures whether the creation and modification dates are set. Defaults to {@literal true}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean setDates() default true;
|
||||
|
||||
/**
|
||||
* Configures whether the entity shall be marked as modified on creation. Defaults to {@literal true}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean modifyOnCreate() default true;
|
||||
|
||||
/**
|
||||
* Configures a {@link DateTimeProvider} bean name that allows customizing the {@link org.joda.time.DateTime} to be
|
||||
* used for setting creation and modification dates.
|
||||
*
|
||||
* @return
|
||||
* @deprecated since 4.1
|
||||
*/
|
||||
@Deprecated
|
||||
String dateTimeProviderRef() default "";
|
||||
}
|
||||
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.elasticsearch.config;
|
||||
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.data.elasticsearch.client.NodeClientFactoryBean;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* NodeClientBeanDefinitionParser
|
||||
*
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
*/
|
||||
|
||||
public class NodeClientBeanDefinitionParser extends AbstractBeanDefinitionParser {
|
||||
|
||||
@Override
|
||||
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(NodeClientFactoryBean.class);
|
||||
setLocalSettings(element, builder);
|
||||
return getSourcedBeanDefinition(builder, element, parserContext);
|
||||
}
|
||||
|
||||
private void setLocalSettings(Element element, BeanDefinitionBuilder builder) {
|
||||
builder.addPropertyValue("local", Boolean.valueOf(element.getAttribute("local")));
|
||||
builder.addPropertyValue("clusterName", element.getAttribute("cluster-name"));
|
||||
builder.addPropertyValue("enableHttp", Boolean.valueOf(element.getAttribute("http-enabled")));
|
||||
builder.addPropertyValue("pathData", element.getAttribute("path-data"));
|
||||
builder.addPropertyValue("pathHome", element.getAttribute("path-home"));
|
||||
builder.addPropertyValue("pathConfiguration", element.getAttribute("path-configuration"));
|
||||
}
|
||||
|
||||
private AbstractBeanDefinition getSourcedBeanDefinition(BeanDefinitionBuilder builder, Element source,
|
||||
ParserContext context) {
|
||||
AbstractBeanDefinition definition = builder.getBeanDefinition();
|
||||
definition.setSource(context.extractSource(source));
|
||||
return definition;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -29,7 +29,7 @@ import org.springframework.data.mapping.context.PersistentEntities;
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.1
|
||||
*/
|
||||
class PersistentEntitiesFactoryBean implements FactoryBean<PersistentEntities> {
|
||||
public class PersistentEntitiesFactoryBean implements FactoryBean<PersistentEntities> {
|
||||
|
||||
private final MappingElasticsearchConverter converter;
|
||||
|
||||
|
||||
+225
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.action.DocWriteResponse;
|
||||
import org.elasticsearch.action.bulk.BulkItemResponse;
|
||||
import org.elasticsearch.action.bulk.BulkResponse;
|
||||
import org.elasticsearch.action.search.MultiSearchRequest;
|
||||
import org.elasticsearch.action.search.MultiSearchResponse;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.index.query.MoreLikeThisQueryBuilder;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.search.suggest.SuggestBuilder;
|
||||
import org.springframework.data.elasticsearch.BulkFailureException;
|
||||
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.query.MoreLikeThisQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* This class contains methods that are common the implementations derived from {@link AbstractElasticsearchTemplate}
|
||||
* using either the {@link org.elasticsearch.client.transport.TransportClient} or the
|
||||
* {@link org.elasticsearch.client.RestHighLevelClient} and that use Elasticsearch specific libraries.
|
||||
* <p>
|
||||
* <strong>Note:</strong> Although this class is public, it is not considered to be part of the official Spring Data
|
||||
* Elasticsearch API and so might change at any time.
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
public abstract class AbstractElasticsearchRestTransportTemplate extends AbstractElasticsearchTemplate {
|
||||
|
||||
// region DocumentOperations
|
||||
/**
|
||||
* @param bulkResponse
|
||||
* @return the list of the item id's
|
||||
*/
|
||||
protected List<IndexedObjectInformation> checkForBulkOperationFailure(BulkResponse bulkResponse) {
|
||||
|
||||
if (bulkResponse.hasFailures()) {
|
||||
Map<String, String> failedDocuments = new HashMap<>();
|
||||
for (BulkItemResponse item : bulkResponse.getItems()) {
|
||||
|
||||
if (item.isFailed())
|
||||
failedDocuments.put(item.getId(), item.getFailureMessage());
|
||||
}
|
||||
throw new BulkFailureException(
|
||||
"Bulk operation has failures. Use ElasticsearchException.getFailedDocuments() for detailed messages ["
|
||||
+ failedDocuments + ']',
|
||||
failedDocuments);
|
||||
}
|
||||
|
||||
return Stream.of(bulkResponse.getItems()).map(bulkItemResponse -> {
|
||||
DocWriteResponse response = bulkItemResponse.getResponse();
|
||||
if (response != null) {
|
||||
return IndexedObjectInformation.of(response.getId(), response.getSeqNo(), response.getPrimaryTerm(),
|
||||
response.getVersion());
|
||||
} else {
|
||||
return IndexedObjectInformation.of(bulkItemResponse.getId(), null, null, null);
|
||||
}
|
||||
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region SearchOperations
|
||||
protected <T> SearchHits<T> doSearch(MoreLikeThisQuery query, Class<T> clazz, IndexCoordinates index) {
|
||||
MoreLikeThisQueryBuilder moreLikeThisQueryBuilder = requestFactory.moreLikeThisQueryBuilder(query, index);
|
||||
return search(
|
||||
new NativeSearchQueryBuilder().withQuery(moreLikeThisQueryBuilder).withPageable(query.getPageable()).build(),
|
||||
clazz, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<SearchHits<T>> multiSearch(List<? extends Query> queries, Class<T> clazz, IndexCoordinates index) {
|
||||
MultiSearchRequest request = new MultiSearchRequest();
|
||||
for (Query query : queries) {
|
||||
request.add(requestFactory.searchRequest(query, clazz, index));
|
||||
}
|
||||
|
||||
MultiSearchResponse.Item[] items = getMultiSearchResult(request);
|
||||
|
||||
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<T>(elasticsearchConverter, clazz, index);
|
||||
SearchDocumentResponseCallback<SearchHits<T>> callback = new ReadSearchDocumentResponseCallback<>(clazz, index);
|
||||
List<SearchHits<T>> res = new ArrayList<>(queries.size());
|
||||
int c = 0;
|
||||
for (Query query : queries) {
|
||||
res.add(
|
||||
callback.doWith(SearchDocumentResponse.from(items[c++].getResponse(), getEntityCreator(documentCallback))));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SearchHits<?>> multiSearch(List<? extends Query> queries, List<Class<?>> classes) {
|
||||
|
||||
Assert.notNull(queries, "queries must not be null");
|
||||
Assert.notNull(classes, "classes must not be null");
|
||||
Assert.isTrue(queries.size() == classes.size(), "queries and classes must have the same size");
|
||||
|
||||
MultiSearchRequest request = new MultiSearchRequest();
|
||||
Iterator<Class<?>> it = classes.iterator();
|
||||
for (Query query : queries) {
|
||||
Class<?> clazz = it.next();
|
||||
request.add(requestFactory.searchRequest(query, clazz, getIndexCoordinatesFor(clazz)));
|
||||
}
|
||||
|
||||
MultiSearchResponse.Item[] items = getMultiSearchResult(request);
|
||||
|
||||
List<SearchHits<?>> res = new ArrayList<>(queries.size());
|
||||
int c = 0;
|
||||
Iterator<Class<?>> it1 = classes.iterator();
|
||||
for (Query query : queries) {
|
||||
Class entityClass = it1.next();
|
||||
|
||||
IndexCoordinates index = getIndexCoordinatesFor(entityClass);
|
||||
ReadDocumentCallback<?> documentCallback = new ReadDocumentCallback<>(elasticsearchConverter, entityClass, index);
|
||||
SearchDocumentResponseCallback<SearchHits<?>> callback = new ReadSearchDocumentResponseCallback<>(entityClass,
|
||||
index);
|
||||
|
||||
SearchResponse response = items[c++].getResponse();
|
||||
res.add(callback.doWith(SearchDocumentResponse.from(response, getEntityCreator(documentCallback))));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SearchHits<?>> multiSearch(List<? extends Query> queries, List<Class<?>> classes,
|
||||
IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(queries, "queries must not be null");
|
||||
Assert.notNull(classes, "classes must not be null");
|
||||
Assert.notNull(index, "index must not be null");
|
||||
Assert.isTrue(queries.size() == classes.size(), "queries and classes must have the same size");
|
||||
|
||||
MultiSearchRequest request = new MultiSearchRequest();
|
||||
Iterator<Class<?>> it = classes.iterator();
|
||||
for (Query query : queries) {
|
||||
request.add(requestFactory.searchRequest(query, it.next(), index));
|
||||
}
|
||||
|
||||
MultiSearchResponse.Item[] items = getMultiSearchResult(request);
|
||||
|
||||
List<SearchHits<?>> res = new ArrayList<>(queries.size());
|
||||
int c = 0;
|
||||
Iterator<Class<?>> it1 = classes.iterator();
|
||||
for (Query query : queries) {
|
||||
Class entityClass = it1.next();
|
||||
|
||||
ReadDocumentCallback<?> documentCallback = new ReadDocumentCallback<>(elasticsearchConverter, entityClass, index);
|
||||
SearchDocumentResponseCallback<SearchHits<?>> callback = new ReadSearchDocumentResponseCallback<>(entityClass,
|
||||
index);
|
||||
|
||||
SearchResponse response = items[c++].getResponse();
|
||||
res.add(callback.doWith(SearchDocumentResponse.from(response, getEntityCreator(documentCallback))));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
abstract protected MultiSearchResponse.Item[] getMultiSearchResult(MultiSearchRequest request);
|
||||
|
||||
// endregion
|
||||
|
||||
// region helper
|
||||
@Override
|
||||
public Query matchAllQuery() {
|
||||
return new NativeSearchQueryBuilder().withQuery(QueryBuilders.matchAllQuery()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query idsQuery(List<String> ids) {
|
||||
|
||||
Assert.notNull(ids, "ids must not be null");
|
||||
|
||||
return new NativeSearchQueryBuilder().withQuery(QueryBuilders.idsQuery().addIds(ids.toArray(new String[] {})))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getVendor() {
|
||||
return "Elasticsearch";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getRuntimeLibraryVersion() {
|
||||
return Version.CURRENT.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public SearchResponse suggest(SuggestBuilder suggestion, Class<?> clazz) {
|
||||
return suggest(suggestion, getIndexCoordinatesFor(clazz));
|
||||
}
|
||||
|
||||
protected <T> SearchDocumentResponse.EntityCreator<T> getEntityCreator(ReadDocumentCallback<T> documentCallback) {
|
||||
return searchDocument -> CompletableFuture.completedFuture(documentCallback.doWith(searchDocument));
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
+76
-188
@@ -15,32 +15,17 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
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 java.util.stream.Stream;
|
||||
|
||||
import org.elasticsearch.action.DocWriteResponse;
|
||||
import org.elasticsearch.action.bulk.BulkItemResponse;
|
||||
import org.elasticsearch.action.bulk.BulkResponse;
|
||||
import org.elasticsearch.action.search.MultiSearchRequest;
|
||||
import org.elasticsearch.action.search.MultiSearchResponse;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.support.WriteRequest;
|
||||
import org.elasticsearch.action.support.WriteRequestBuilder;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.index.query.MoreLikeThisQueryBuilder;
|
||||
import org.elasticsearch.search.suggest.SuggestBuilder;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.data.convert.EntityReader;
|
||||
import org.springframework.data.elasticsearch.BulkFailureException;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.document.Document;
|
||||
@@ -57,7 +42,6 @@ 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;
|
||||
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm;
|
||||
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
|
||||
@@ -73,20 +57,28 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* AbstractElasticsearchTemplate
|
||||
* This class contains methods that are common to different implementations of the {@link ElasticsearchOperations}
|
||||
* interface that use different clients, like TransportClient, RestHighLevelClient and the next Java client from
|
||||
* Elasticsearch or some future implementation that might use an Opensearch client. This class must not contain imports
|
||||
* or use classes that are specific to one of these implementations.
|
||||
* <p>
|
||||
* <strong>Note:</strong> Although this class is public, it is not considered to be part of the official Spring Data
|
||||
* Elasticsearch API and so might change at any time.
|
||||
*
|
||||
* @author Sascha Woo
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Roman Puchkovskiy
|
||||
* @author Subhobrata Dey
|
||||
* @author Steven Pearce
|
||||
* @author Anton Naydenov
|
||||
*/
|
||||
public abstract class AbstractElasticsearchTemplate implements ElasticsearchOperations, ApplicationContextAware {
|
||||
|
||||
@Nullable protected ElasticsearchConverter elasticsearchConverter;
|
||||
@Nullable protected RequestFactory requestFactory;
|
||||
@Nullable private EntityOperations entityOperations;
|
||||
@Nullable private EntityCallbacks entityCallbacks;
|
||||
@Nullable private RefreshPolicy refreshPolicy;
|
||||
@Nullable protected EntityOperations entityOperations;
|
||||
@Nullable protected EntityCallbacks entityCallbacks;
|
||||
@Nullable protected RefreshPolicy refreshPolicy;
|
||||
@Nullable protected RoutingResolver routingResolver;
|
||||
|
||||
// region Initialization
|
||||
@@ -101,7 +93,10 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
this.routingResolver = new DefaultRoutingResolver((SimpleElasticsearchMappingContext) mappingContext);
|
||||
|
||||
requestFactory = new RequestFactory(elasticsearchConverter);
|
||||
VersionInfo.logVersions(getClusterVersion());
|
||||
|
||||
// initialize the VersionInfo class in the initialization phase
|
||||
// noinspection ResultOfMethodCallIgnored
|
||||
VersionInfo.versionProperties();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,6 +111,7 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
}
|
||||
|
||||
copy.setRoutingResolver(routingResolver);
|
||||
copy.setRefreshPolicy(refreshPolicy);
|
||||
|
||||
return copy;
|
||||
}
|
||||
@@ -166,6 +162,16 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
public RefreshPolicy getRefreshPolicy() {
|
||||
return refreshPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* logs the versions of the different Elasticsearch components.
|
||||
*
|
||||
* @since 4.3
|
||||
*/
|
||||
public void logVersions() {
|
||||
VersionInfo.logVersions(getVendor(), getRuntimeLibraryVersion(), getClusterVersion());
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region DocumentOperations
|
||||
@@ -188,7 +194,7 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
IndexQuery query = getIndexQuery(entityAfterBeforeConvert);
|
||||
doIndex(query, index);
|
||||
|
||||
T entityAfterAfterSave = maybeCallbackAfterSave(entityAfterBeforeConvert, index);
|
||||
T entityAfterAfterSave = (T) maybeCallbackAfterSave(query.getObject(), index);
|
||||
|
||||
return entityAfterAfterSave;
|
||||
}
|
||||
@@ -215,13 +221,18 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
List<IndexQuery> indexQueries = Streamable.of(entities).stream().map(this::getIndexQuery)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (!indexQueries.isEmpty()) {
|
||||
List<IndexedObjectInformation> indexedObjectInformations = bulkIndex(indexQueries, index);
|
||||
Iterator<IndexedObjectInformation> iterator = indexedObjectInformations.iterator();
|
||||
entities.forEach(entity -> updateIndexedObject(entity, iterator.next()));
|
||||
if (indexQueries.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return indexQueries.stream().map(IndexQuery::getObject).map(entity -> (T) entity).collect(Collectors.toList());
|
||||
List<IndexedObjectInformation> indexedObjectInformations = bulkIndex(indexQueries, index);
|
||||
Iterator<IndexedObjectInformation> iterator = indexedObjectInformations.iterator();
|
||||
|
||||
// noinspection unchecked
|
||||
return indexQueries.stream() //
|
||||
.map(IndexQuery::getObject) //
|
||||
.map(entity -> (T) updateIndexedObject(entity, iterator.next())) //
|
||||
.collect(Collectors.toList()); //
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -346,40 +357,6 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
public abstract List<IndexedObjectInformation> doBulkOperation(List<?> queries, BulkOptions bulkOptions,
|
||||
IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* Pre process the write request before it is sent to the server, eg. by setting the
|
||||
* {@link WriteRequest#setRefreshPolicy(String) refresh policy} if applicable.
|
||||
*
|
||||
* @param request must not be {@literal null}.
|
||||
* @param <R>
|
||||
* @return the processed {@link WriteRequest}.
|
||||
*/
|
||||
protected <R extends WriteRequest<R>> R prepareWriteRequest(R request) {
|
||||
|
||||
if (refreshPolicy == null) {
|
||||
return request;
|
||||
}
|
||||
|
||||
return request.setRefreshPolicy(RequestFactory.toElasticsearchRefreshPolicy(refreshPolicy));
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre process the write request before it is sent to the server, eg. by setting the
|
||||
* {@link WriteRequest#setRefreshPolicy(String) refresh policy} if applicable.
|
||||
*
|
||||
* @param requestBuilder must not be {@literal null}.
|
||||
* @param <R>
|
||||
* @return the processed {@link WriteRequest}.
|
||||
*/
|
||||
protected <R extends WriteRequestBuilder<R>> R prepareWriteRequestBuilder(R requestBuilder) {
|
||||
|
||||
if (refreshPolicy == null) {
|
||||
return requestBuilder;
|
||||
}
|
||||
|
||||
return requestBuilder.setRefreshPolicy(RequestFactory.toElasticsearchRefreshPolicy(refreshPolicy));
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region SearchOperations
|
||||
@@ -396,8 +373,8 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
@Override
|
||||
public <T> SearchHitsIterator<T> searchForStream(Query query, Class<T> clazz, IndexCoordinates index) {
|
||||
|
||||
long scrollTimeInMillis = TimeValue.timeValueMinutes(1).millis();
|
||||
|
||||
Duration scrollTime = query.getScrollTime() != null ? query.getScrollTime() : Duration.ofMinutes(1);
|
||||
long scrollTimeInMillis = scrollTime.toMillis();
|
||||
// noinspection ConstantConditions
|
||||
int maxCount = query.isLimiting() ? query.getMaxResults() : 0;
|
||||
|
||||
@@ -418,96 +395,16 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
|
||||
Assert.notNull(query.getId(), "No document id defined for MoreLikeThisQuery");
|
||||
|
||||
MoreLikeThisQueryBuilder moreLikeThisQueryBuilder = requestFactory.moreLikeThisQueryBuilder(query, index);
|
||||
return search(new NativeSearchQueryBuilder().withQuery(moreLikeThisQueryBuilder).withPageable(query.getPageable()).build(), clazz, index);
|
||||
return doSearch(query, clazz, index);
|
||||
}
|
||||
|
||||
protected abstract <T> SearchHits<T> doSearch(MoreLikeThisQuery query, Class<T> clazz, IndexCoordinates index);
|
||||
|
||||
@Override
|
||||
public <T> List<SearchHits<T>> multiSearch(List<? extends Query> queries, Class<T> clazz) {
|
||||
return multiSearch(queries, clazz, getIndexCoordinatesFor(clazz));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<SearchHits<T>> multiSearch(List<? extends Query> queries, Class<T> clazz, IndexCoordinates index) {
|
||||
MultiSearchRequest request = new MultiSearchRequest();
|
||||
for (Query query : queries) {
|
||||
request.add(requestFactory.searchRequest(query, clazz, index));
|
||||
}
|
||||
|
||||
MultiSearchResponse.Item[] items = getMultiSearchResult(request);
|
||||
|
||||
SearchDocumentResponseCallback<SearchHits<T>> callback = new ReadSearchDocumentResponseCallback<>(clazz, index);
|
||||
List<SearchHits<T>> res = new ArrayList<>(queries.size());
|
||||
int c = 0;
|
||||
for (Query query : queries) {
|
||||
res.add(callback.doWith(SearchDocumentResponse.from(items[c++].getResponse())));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SearchHits<?>> multiSearch(List<? extends Query> queries, List<Class<?>> classes) {
|
||||
|
||||
Assert.notNull(queries, "queries must not be null");
|
||||
Assert.notNull(classes, "classes must not be null");
|
||||
Assert.isTrue(queries.size() == classes.size(), "queries and classes must have the same size");
|
||||
|
||||
MultiSearchRequest request = new MultiSearchRequest();
|
||||
Iterator<Class<?>> it = classes.iterator();
|
||||
for (Query query : queries) {
|
||||
Class<?> clazz = it.next();
|
||||
request.add(requestFactory.searchRequest(query, clazz, getIndexCoordinatesFor(clazz)));
|
||||
}
|
||||
|
||||
MultiSearchResponse.Item[] items = getMultiSearchResult(request);
|
||||
|
||||
List<SearchHits<?>> res = new ArrayList<>(queries.size());
|
||||
int c = 0;
|
||||
Iterator<Class<?>> it1 = classes.iterator();
|
||||
for (Query query : queries) {
|
||||
Class entityClass = it1.next();
|
||||
|
||||
SearchDocumentResponseCallback<SearchHits<?>> callback = new ReadSearchDocumentResponseCallback<>(entityClass,
|
||||
getIndexCoordinatesFor(entityClass));
|
||||
|
||||
SearchResponse response = items[c++].getResponse();
|
||||
res.add(callback.doWith(SearchDocumentResponse.from(response)));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SearchHits<?>> multiSearch(List<? extends Query> queries, List<Class<?>> classes,
|
||||
IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(queries, "queries must not be null");
|
||||
Assert.notNull(classes, "classes must not be null");
|
||||
Assert.notNull(index, "index must not be null");
|
||||
Assert.isTrue(queries.size() == classes.size(), "queries and classes must have the same size");
|
||||
|
||||
MultiSearchRequest request = new MultiSearchRequest();
|
||||
Iterator<Class<?>> it = classes.iterator();
|
||||
for (Query query : queries) {
|
||||
request.add(requestFactory.searchRequest(query, it.next(), index));
|
||||
}
|
||||
|
||||
MultiSearchResponse.Item[] items = getMultiSearchResult(request);
|
||||
|
||||
List<SearchHits<?>> res = new ArrayList<>(queries.size());
|
||||
int c = 0;
|
||||
Iterator<Class<?>> it1 = classes.iterator();
|
||||
for (Query query : queries) {
|
||||
Class entityClass = it1.next();
|
||||
|
||||
SearchDocumentResponseCallback<SearchHits<?>> callback = new ReadSearchDocumentResponseCallback<>(entityClass,
|
||||
index);
|
||||
|
||||
SearchResponse response = items[c++].getResponse();
|
||||
res.add(callback.doWith(SearchDocumentResponse.from(response)));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> SearchHits<T> search(Query query, Class<T> clazz) {
|
||||
return search(query, clazz, getIndexCoordinatesFor(clazz));
|
||||
@@ -537,13 +434,6 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
*/
|
||||
abstract protected void searchScrollClear(List<String> scrollIds);
|
||||
|
||||
abstract protected MultiSearchResponse.Item[] getMultiSearchResult(MultiSearchRequest request);
|
||||
|
||||
@Override
|
||||
public SearchResponse suggest(SuggestBuilder suggestion, Class<?> clazz) {
|
||||
return suggest(suggestion, getIndexCoordinatesFor(clazz));
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region Helper methods
|
||||
@@ -580,38 +470,7 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
return getRequiredPersistentEntity(clazz).getIndexCoordinates();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bulkResponse
|
||||
* @return the list of the item id's
|
||||
*/
|
||||
protected List<IndexedObjectInformation> checkForBulkOperationFailure(BulkResponse bulkResponse) {
|
||||
|
||||
if (bulkResponse.hasFailures()) {
|
||||
Map<String, String> failedDocuments = new HashMap<>();
|
||||
for (BulkItemResponse item : bulkResponse.getItems()) {
|
||||
|
||||
if (item.isFailed())
|
||||
failedDocuments.put(item.getId(), item.getFailureMessage());
|
||||
}
|
||||
throw new BulkFailureException(
|
||||
"Bulk operation has failures. Use ElasticsearchException.getFailedDocuments() for detailed messages ["
|
||||
+ failedDocuments + ']',
|
||||
failedDocuments);
|
||||
}
|
||||
|
||||
return Stream.of(bulkResponse.getItems()).map(bulkItemResponse -> {
|
||||
DocWriteResponse response = bulkItemResponse.getResponse();
|
||||
if (response != null) {
|
||||
return IndexedObjectInformation.of(response.getId(), response.getSeqNo(), response.getPrimaryTerm(),
|
||||
response.getVersion());
|
||||
} else {
|
||||
return IndexedObjectInformation.of(bulkItemResponse.getId(), null, null, null);
|
||||
}
|
||||
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
protected void updateIndexedObject(Object entity, IndexedObjectInformation indexedObjectInformation) {
|
||||
protected <T> T updateIndexedObject(T entity, IndexedObjectInformation indexedObjectInformation) {
|
||||
|
||||
ElasticsearchPersistentEntity<?> persistentEntity = elasticsearchConverter.getMappingContext()
|
||||
.getPersistentEntity(entity.getClass());
|
||||
@@ -621,22 +480,30 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
ElasticsearchPersistentProperty idProperty = persistentEntity.getIdProperty();
|
||||
|
||||
// Only deal with text because ES generated Ids are strings!
|
||||
if (idProperty != null && idProperty.getType().isAssignableFrom(String.class)) {
|
||||
if (indexedObjectInformation.getId() != null && idProperty != null
|
||||
&& idProperty.getType().isAssignableFrom(String.class)) {
|
||||
propertyAccessor.setProperty(idProperty, indexedObjectInformation.getId());
|
||||
}
|
||||
|
||||
if (indexedObjectInformation.getSeqNo() != null && indexedObjectInformation.getPrimaryTerm() != null
|
||||
&& persistentEntity.hasSeqNoPrimaryTermProperty()) {
|
||||
ElasticsearchPersistentProperty seqNoPrimaryTermProperty = persistentEntity.getSeqNoPrimaryTermProperty();
|
||||
// noinspection ConstantConditions
|
||||
propertyAccessor.setProperty(seqNoPrimaryTermProperty,
|
||||
new SeqNoPrimaryTerm(indexedObjectInformation.getSeqNo(), indexedObjectInformation.getPrimaryTerm()));
|
||||
}
|
||||
|
||||
if (indexedObjectInformation.getVersion() != null && persistentEntity.hasVersionProperty()) {
|
||||
ElasticsearchPersistentProperty versionProperty = persistentEntity.getVersionProperty();
|
||||
// noinspection ConstantConditions
|
||||
propertyAccessor.setProperty(versionProperty, indexedObjectInformation.getVersion());
|
||||
}
|
||||
|
||||
// noinspection unchecked
|
||||
T updatedEntity = (T) propertyAccessor.getBean();
|
||||
return updatedEntity;
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
ElasticsearchPersistentEntity<?> getRequiredPersistentEntity(Class<?> clazz) {
|
||||
@@ -721,6 +588,18 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
@Nullable
|
||||
abstract protected String getClusterVersion();
|
||||
|
||||
/**
|
||||
* @return the vendor name of the used cluster and client library
|
||||
* @since 4.3
|
||||
*/
|
||||
abstract protected String getVendor();
|
||||
|
||||
/**
|
||||
* @return the version of the used client runtime library.
|
||||
* @since 4.3
|
||||
*/
|
||||
abstract protected String getRuntimeLibraryVersion();
|
||||
|
||||
// endregion
|
||||
|
||||
// region Entity callbacks
|
||||
@@ -807,13 +686,16 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
|
||||
protected void updateIndexedObjectsWithQueries(List<?> queries,
|
||||
List<IndexedObjectInformation> indexedObjectInformations) {
|
||||
|
||||
for (int i = 0; i < queries.size(); i++) {
|
||||
Object query = queries.get(i);
|
||||
|
||||
if (query instanceof IndexQuery) {
|
||||
IndexQuery indexQuery = (IndexQuery) query;
|
||||
Object queryObject = indexQuery.getObject();
|
||||
|
||||
if (queryObject != null) {
|
||||
updateIndexedObject(queryObject, indexedObjectInformations.get(i));
|
||||
indexQuery.setObject(updateIndexedObject(queryObject, indexedObjectInformations.get(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -848,6 +730,10 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
}
|
||||
|
||||
T entity = reader.read(type, document);
|
||||
IndexedObjectInformation indexedObjectInformation = IndexedObjectInformation.of(
|
||||
document.hasId() ? document.getId() : null, document.getSeqNo(), document.getPrimaryTerm(),
|
||||
document.getVersion());
|
||||
entity = updateIndexedObject(entity, indexedObjectInformation);
|
||||
return maybeCallbackAfterConvert(entity, document, index);
|
||||
}
|
||||
}
|
||||
@@ -869,6 +755,7 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public SearchHits<T> doWith(SearchDocumentResponse response) {
|
||||
List<T> entities = response.getSearchDocuments().stream().map(delegate::doWith).collect(Collectors.toList());
|
||||
@@ -889,6 +776,7 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public SearchScrollHits<T> doWith(SearchDocumentResponse response) {
|
||||
List<T> entities = response.getSearchDocuments().stream().map(delegate::doWith).collect(Collectors.toList());
|
||||
|
||||
+4
-37
@@ -17,14 +17,10 @@ package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import static org.springframework.util.StringUtils.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.elasticsearch.cluster.metadata.AliasMetadata;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
|
||||
@@ -36,7 +32,6 @@ import org.springframework.data.elasticsearch.core.index.MappingBuilder;
|
||||
import org.springframework.data.elasticsearch.core.index.Settings;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.query.AliasQuery;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -47,9 +42,7 @@ import org.springframework.util.Assert;
|
||||
* @author Sascha Woo
|
||||
* @since 4.0
|
||||
*/
|
||||
abstract class AbstractDefaultIndexOperations implements IndexOperations {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractDefaultIndexOperations.class);
|
||||
abstract class AbstractIndexTemplate implements IndexOperations {
|
||||
|
||||
protected final ElasticsearchConverter elasticsearchConverter;
|
||||
protected final RequestFactory requestFactory;
|
||||
@@ -57,7 +50,7 @@ abstract class AbstractDefaultIndexOperations implements IndexOperations {
|
||||
@Nullable protected final Class<?> boundClass;
|
||||
@Nullable private final IndexCoordinates boundIndex;
|
||||
|
||||
public AbstractDefaultIndexOperations(ElasticsearchConverter elasticsearchConverter, Class<?> boundClass) {
|
||||
public AbstractIndexTemplate(ElasticsearchConverter elasticsearchConverter, Class<?> boundClass) {
|
||||
|
||||
Assert.notNull(boundClass, "boundClass may not be null");
|
||||
|
||||
@@ -67,7 +60,7 @@ abstract class AbstractDefaultIndexOperations implements IndexOperations {
|
||||
this.boundIndex = null;
|
||||
}
|
||||
|
||||
public AbstractDefaultIndexOperations(ElasticsearchConverter elasticsearchConverter, IndexCoordinates boundIndex) {
|
||||
public AbstractIndexTemplate(ElasticsearchConverter elasticsearchConverter, IndexCoordinates boundIndex) {
|
||||
|
||||
Assert.notNull(boundIndex, "boundIndex may not be null");
|
||||
|
||||
@@ -176,31 +169,6 @@ abstract class AbstractDefaultIndexOperations implements IndexOperations {
|
||||
|
||||
protected abstract void doRefresh(IndexCoordinates indexCoordinates);
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public boolean addAlias(AliasQuery query) {
|
||||
return doAddAlias(query, getIndexCoordinates());
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
protected abstract boolean doAddAlias(AliasQuery query, IndexCoordinates index);
|
||||
|
||||
@Override
|
||||
public List<AliasMetadata> queryForAlias() {
|
||||
return doQueryForAlias(getIndexCoordinates());
|
||||
}
|
||||
|
||||
protected abstract List<AliasMetadata> doQueryForAlias(IndexCoordinates index);
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public boolean removeAlias(AliasQuery query) {
|
||||
return doRemoveAlias(query, getIndexCoordinates());
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
protected abstract boolean doRemoveAlias(AliasQuery query, IndexCoordinates index);
|
||||
|
||||
@Override
|
||||
public Map<String, Set<AliasData>> getAliases(String... aliasNames) {
|
||||
|
||||
@@ -234,6 +202,7 @@ abstract class AbstractDefaultIndexOperations implements IndexOperations {
|
||||
|
||||
// load mapping specified in Mapping annotation if present
|
||||
Mapping mappingAnnotation = AnnotatedElementUtils.findMergedAnnotation(clazz, Mapping.class);
|
||||
|
||||
if (mappingAnnotation != null) {
|
||||
String mappingPath = mappingAnnotation.mappingPath();
|
||||
|
||||
@@ -243,8 +212,6 @@ abstract class AbstractDefaultIndexOperations implements IndexOperations {
|
||||
if (hasText(mappings)) {
|
||||
return Document.parse(mappings);
|
||||
}
|
||||
} else {
|
||||
LOGGER.info("mappingPath in @Mapping has to be defined. Building mappings using @Field");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
/**
|
||||
* Class corresponding to the Elasticsearch class, but in the org.springframework.data.elasticsearch package
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
public class ActiveShardCount {
|
||||
private static final int ACTIVE_SHARD_COUNT_DEFAULT = -2;
|
||||
private static final int ALL_ACTIVE_SHARDS = -1;
|
||||
|
||||
public static final ActiveShardCount DEFAULT = new ActiveShardCount(ACTIVE_SHARD_COUNT_DEFAULT);
|
||||
public static final ActiveShardCount ALL = new ActiveShardCount(ALL_ACTIVE_SHARDS);
|
||||
public static final ActiveShardCount NONE = new ActiveShardCount(0);
|
||||
public static final ActiveShardCount ONE = new ActiveShardCount(1);
|
||||
|
||||
private final int value;
|
||||
|
||||
public ActiveShardCount(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
/**
|
||||
* Aggregation container used in the Spring Data Elasticsearch API for a single aggregation. The concrete
|
||||
* implementations must be provided by the code handling the direct communication with Elasticsearch.
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @param <T> the aggregation class from the used client implementation.
|
||||
* @since 4.3
|
||||
*/
|
||||
public interface AggregationContainer<T> {
|
||||
/**
|
||||
* @return the concrete aggregations implementation
|
||||
*/
|
||||
T aggregation();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
/**
|
||||
* Aggregations container used in the Spring Data Elasticsearch API. The concrete implementations must be provided by
|
||||
* the code handling the direct communication with Elasticsearch.
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @param <T> the aggregations class from the used client implementation.
|
||||
* @since 4.3
|
||||
*/
|
||||
public interface AggregationsContainer<T> {
|
||||
/**
|
||||
* @return the concrete aggregations implementation
|
||||
*/
|
||||
T aggregations();
|
||||
}
|
||||
+19
-4
@@ -165,20 +165,35 @@ class CriteriaQueryProcessor {
|
||||
@Nullable
|
||||
private QueryBuilder queryFor(Criteria.CriteriaEntry entry, Field field) {
|
||||
|
||||
QueryBuilder query = null;
|
||||
String fieldName = field.getName();
|
||||
boolean isKeywordField = FieldType.Keyword == field.getFieldType();
|
||||
|
||||
OperationKey key = entry.getKey();
|
||||
|
||||
if (key == OperationKey.EXISTS) {
|
||||
return existsQuery(fieldName);
|
||||
// operations without a value
|
||||
switch (key) {
|
||||
case EXISTS:
|
||||
query = existsQuery(fieldName);
|
||||
break;
|
||||
case EMPTY:
|
||||
query = boolQuery().must(existsQuery(fieldName)).mustNot(wildcardQuery(fieldName, "*"));
|
||||
break;
|
||||
case NOT_EMPTY:
|
||||
query = wildcardQuery(fieldName, "*");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (query != null) {
|
||||
return query;
|
||||
}
|
||||
|
||||
// now operation keys with a value
|
||||
Object value = entry.getValue();
|
||||
String searchText = QueryParserUtil.escape(value.toString());
|
||||
|
||||
QueryBuilder query = null;
|
||||
|
||||
switch (key) {
|
||||
case EQUALS:
|
||||
query = queryStringQuery(searchText).field(fieldName).defaultOperator(AND);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
@@ -121,6 +122,8 @@ public interface DocumentOperations {
|
||||
* @param query the query defining the ids of the objects to get
|
||||
* @param clazz the type of the object to be returned
|
||||
* @return list of {@link MultiGetItem}s
|
||||
* @see Query#multiGetQuery(Collection)
|
||||
* @see Query#multiGetQueryWithRouting(List)
|
||||
* @since 4.1
|
||||
*/
|
||||
<T> List<MultiGetItem<T>> multiGet(Query query, Class<T> clazz);
|
||||
@@ -132,6 +135,8 @@ public interface DocumentOperations {
|
||||
* @param clazz the type of the object to be returned
|
||||
* @param index the index(es) from which the objects are read.
|
||||
* @return list of {@link MultiGetItem}s
|
||||
* @see Query#multiGetQuery(Collection)
|
||||
* @see Query#multiGetQueryWithRouting(List)
|
||||
*/
|
||||
<T> List<MultiGetItem<T>> multiGet(Query query, Class<T> clazz, IndexCoordinates index);
|
||||
|
||||
@@ -159,6 +164,7 @@ public interface DocumentOperations {
|
||||
* @param queries the queries to execute in bulk
|
||||
* @param clazz the entity class
|
||||
* @return the information about the indexed objects
|
||||
* @throws org.springframework.data.elasticsearch.BulkFailureException with information about the failed operation
|
||||
* @since 4.1
|
||||
*/
|
||||
default List<IndexedObjectInformation> bulkIndex(List<IndexQuery> queries, Class<?> clazz) {
|
||||
@@ -170,6 +176,7 @@ public interface DocumentOperations {
|
||||
*
|
||||
* @param queries the queries to execute in bulk
|
||||
* @return the information about of the indexed objects
|
||||
* @throws org.springframework.data.elasticsearch.BulkFailureException with information about the failed operation
|
||||
*/
|
||||
default List<IndexedObjectInformation> bulkIndex(List<IndexQuery> queries, IndexCoordinates index) {
|
||||
return bulkIndex(queries, BulkOptions.defaultOptions(), index);
|
||||
@@ -182,6 +189,7 @@ public interface DocumentOperations {
|
||||
* @param bulkOptions options to be added to the bulk request
|
||||
* @param clazz the entity class
|
||||
* @return the information about of the indexed objects
|
||||
* @throws org.springframework.data.elasticsearch.BulkFailureException with information about the failed operation
|
||||
* @since 4.1
|
||||
*/
|
||||
List<IndexedObjectInformation> bulkIndex(List<IndexQuery> queries, BulkOptions bulkOptions, Class<?> clazz);
|
||||
@@ -192,6 +200,7 @@ public interface DocumentOperations {
|
||||
* @param queries the queries to execute in bulk
|
||||
* @param bulkOptions options to be added to the bulk request
|
||||
* @return the information about of the indexed objects
|
||||
* @throws org.springframework.data.elasticsearch.BulkFailureException with information about the failed operation
|
||||
*/
|
||||
List<IndexedObjectInformation> bulkIndex(List<IndexQuery> queries, BulkOptions bulkOptions, IndexCoordinates index);
|
||||
|
||||
@@ -199,6 +208,7 @@ public interface DocumentOperations {
|
||||
* Bulk update all objects. Will do update.
|
||||
*
|
||||
* @param queries the queries to execute in bulk
|
||||
* @throws org.springframework.data.elasticsearch.BulkFailureException with information about the failed operation
|
||||
*/
|
||||
default void bulkUpdate(List<UpdateQuery> queries, IndexCoordinates index) {
|
||||
bulkUpdate(queries, BulkOptions.defaultOptions(), index);
|
||||
@@ -209,6 +219,7 @@ public interface DocumentOperations {
|
||||
*
|
||||
* @param clazz the entity class
|
||||
* @param queries the queries to execute in bulk
|
||||
* @throws org.springframework.data.elasticsearch.BulkFailureException with information about the failed operation
|
||||
* @since 4.1
|
||||
*/
|
||||
void bulkUpdate(List<UpdateQuery> queries, Class<?> clazz);
|
||||
@@ -218,6 +229,7 @@ public interface DocumentOperations {
|
||||
*
|
||||
* @param queries the queries to execute in bulk
|
||||
* @param bulkOptions options to be added to the bulk request
|
||||
* @throws org.springframework.data.elasticsearch.BulkFailureException with information about the failed operation
|
||||
*/
|
||||
void bulkUpdate(List<UpdateQuery> queries, BulkOptions bulkOptions, IndexCoordinates index);
|
||||
|
||||
@@ -283,7 +295,7 @@ public interface DocumentOperations {
|
||||
|
||||
/**
|
||||
* Delete all records matching the query.
|
||||
*
|
||||
*
|
||||
* @param query query defining the objects
|
||||
* @param clazz The entity class, must be annotated with
|
||||
* {@link org.springframework.data.elasticsearch.annotations.Document}
|
||||
|
||||
+37
-5
@@ -22,13 +22,13 @@ import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.ElasticsearchStatusException;
|
||||
import org.elasticsearch.common.ValidationException;
|
||||
import org.elasticsearch.index.engine.VersionConflictEngineException;
|
||||
import org.elasticsearch.rest.RestStatus;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.dao.OptimisticLockingFailureException;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.data.elasticsearch.NoSuchIndexException;
|
||||
import org.springframework.data.elasticsearch.RestStatusException;
|
||||
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
@@ -38,7 +38,7 @@ import org.springframework.util.StringUtils;
|
||||
* Simple {@link PersistenceExceptionTranslator} for Elasticsearch. Convert the given runtime exception to an
|
||||
* appropriate exception from the {@code org.springframework.dao} hierarchy. Return {@literal null} if no translation is
|
||||
* appropriate: any other exception may have resulted from user code, and should not be translated.
|
||||
*
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Roman Puchkovskiy
|
||||
@@ -63,9 +63,28 @@ public class ElasticsearchExceptionTranslator implements PersistenceExceptionTra
|
||||
ex);
|
||||
}
|
||||
|
||||
if (elasticsearchException instanceof ElasticsearchStatusException) {
|
||||
ElasticsearchStatusException elasticsearchStatusException = (ElasticsearchStatusException) elasticsearchException;
|
||||
return new RestStatusException(elasticsearchStatusException.status().getStatus(),
|
||||
elasticsearchStatusException.getMessage(), elasticsearchStatusException);
|
||||
}
|
||||
|
||||
return new UncategorizedElasticsearchException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
if (ex instanceof RestStatusException) {
|
||||
RestStatusException restStatusException = (RestStatusException) ex;
|
||||
Throwable cause = restStatusException.getCause();
|
||||
if (cause instanceof ElasticsearchException) {
|
||||
ElasticsearchException elasticsearchException = (ElasticsearchException) cause;
|
||||
|
||||
if (!indexAvailable(elasticsearchException)) {
|
||||
return new NoSuchIndexException(ObjectUtils.nullSafeToString(elasticsearchException.getMetadata("es.index")),
|
||||
ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ex instanceof ValidationException) {
|
||||
return new DataIntegrityViolationException(ex.getMessage(), ex);
|
||||
}
|
||||
@@ -80,13 +99,26 @@ public class ElasticsearchExceptionTranslator implements PersistenceExceptionTra
|
||||
|
||||
private boolean isSeqNoConflict(Exception exception) {
|
||||
|
||||
Integer status = null;
|
||||
String message = null;
|
||||
|
||||
if (exception instanceof ElasticsearchStatusException) {
|
||||
|
||||
ElasticsearchStatusException statusException = (ElasticsearchStatusException) exception;
|
||||
status = statusException.status().getStatus();
|
||||
message = statusException.getMessage();
|
||||
}
|
||||
|
||||
return statusException.status() == RestStatus.CONFLICT && statusException.getMessage() != null
|
||||
&& statusException.getMessage().contains("type=version_conflict_engine_exception")
|
||||
&& statusException.getMessage().contains("version conflict, required seqNo");
|
||||
if (exception instanceof RestStatusException) {
|
||||
|
||||
RestStatusException statusException = (RestStatusException) exception;
|
||||
status = statusException.getStatus();
|
||||
message = statusException.getMessage();
|
||||
}
|
||||
|
||||
if (status != null && message != null) {
|
||||
return status == 409 && message.contains("type=version_conflict_engine_exception")
|
||||
&& message.contains("version conflict, required seqNo");
|
||||
}
|
||||
|
||||
if (exception instanceof VersionConflictEngineException) {
|
||||
|
||||
+41
-16
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
* Copyright 2013-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -33,10 +33,11 @@ import org.elasticsearch.action.search.MultiSearchResponse;
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.search.SearchScrollRequest;
|
||||
import org.elasticsearch.action.support.WriteRequest;
|
||||
import org.elasticsearch.action.update.UpdateRequest;
|
||||
import org.elasticsearch.client.RequestOptions;
|
||||
import org.elasticsearch.client.RestHighLevelClient;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.core.TimeValue;
|
||||
import org.elasticsearch.index.reindex.BulkByScrollResponse;
|
||||
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
|
||||
import org.elasticsearch.index.reindex.UpdateByQueryRequest;
|
||||
@@ -92,7 +93,7 @@ import org.springframework.util.Assert;
|
||||
* @author Massimiliano Poggi
|
||||
* @author Farid Faoudi
|
||||
*/
|
||||
public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
|
||||
public class ElasticsearchRestTemplate extends AbstractElasticsearchRestTransportTemplate {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ElasticsearchRestTemplate.class);
|
||||
|
||||
@@ -131,7 +132,7 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
|
||||
|
||||
Assert.notNull(clazz, "clazz must not be null");
|
||||
|
||||
return new DefaultIndexOperations(this, clazz);
|
||||
return new RestIndexTemplate(this, clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -139,7 +140,7 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
|
||||
|
||||
Assert.notNull(index, "index must not be null");
|
||||
|
||||
return new DefaultIndexOperations(this, index);
|
||||
return new RestIndexTemplate(this, index);
|
||||
}
|
||||
// endregion
|
||||
|
||||
@@ -157,9 +158,10 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
|
||||
IndexResponse indexResponse = execute(client -> client.index(request, RequestOptions.DEFAULT));
|
||||
|
||||
Object queryObject = query.getObject();
|
||||
|
||||
if (queryObject != null) {
|
||||
updateIndexedObject(queryObject, IndexedObjectInformation.of(indexResponse.getId(), indexResponse.getSeqNo(),
|
||||
indexResponse.getPrimaryTerm(), indexResponse.getVersion()));
|
||||
query.setObject(updateIndexedObject(queryObject, IndexedObjectInformation.of(indexResponse.getId(),
|
||||
indexResponse.getSeqNo(), indexResponse.getPrimaryTerm(), indexResponse.getVersion())));
|
||||
}
|
||||
|
||||
return indexResponse.getId();
|
||||
@@ -168,6 +170,7 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
|
||||
@Override
|
||||
@Nullable
|
||||
public <T> T get(String id, Class<T> clazz, IndexCoordinates index) {
|
||||
|
||||
GetRequest request = requestFactory.getRequest(id, routingResolver.getRouting(), index);
|
||||
GetResponse response = execute(client -> client.get(request, RequestOptions.DEFAULT));
|
||||
|
||||
@@ -179,7 +182,6 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
|
||||
public <T> List<MultiGetItem<T>> multiGet(Query query, Class<T> clazz, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(index, "index must not be null");
|
||||
Assert.notEmpty(query.getIds(), "No Id defined for Query");
|
||||
|
||||
MultiGetRequest request = requestFactory.multiGetRequest(query, clazz, index);
|
||||
MultiGetResponse result = execute(client -> client.mget(request, RequestOptions.DEFAULT));
|
||||
@@ -221,7 +223,8 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
|
||||
@Override
|
||||
public ByQueryResponse delete(Query query, Class<?> clazz, IndexCoordinates index) {
|
||||
DeleteByQueryRequest deleteByQueryRequest = requestFactory.deleteByQueryRequest(query, clazz, index);
|
||||
return ByQueryResponse.of(execute(client -> client.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT)));
|
||||
return ResponseConverter
|
||||
.byQueryResponseOf(execute(client -> client.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -259,7 +262,7 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
|
||||
|
||||
final BulkByScrollResponse bulkByScrollResponse = execute(
|
||||
client -> client.updateByQuery(updateByQueryRequest, RequestOptions.DEFAULT));
|
||||
return ByQueryResponse.of(bulkByScrollResponse);
|
||||
return ResponseConverter.byQueryResponseOf(bulkByScrollResponse);
|
||||
}
|
||||
|
||||
public List<IndexedObjectInformation> doBulkOperation(List<?> queries, BulkOptions bulkOptions,
|
||||
@@ -270,6 +273,24 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
|
||||
updateIndexedObjectsWithQueries(queries, indexedObjectInformationList);
|
||||
return indexedObjectInformationList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre process the write request before it is sent to the server, eg. by setting the
|
||||
* {@link WriteRequest#setRefreshPolicy(String) refresh policy} if applicable.
|
||||
*
|
||||
* @param request must not be {@literal null}.
|
||||
* @param <R>
|
||||
* @return the processed {@link WriteRequest}.
|
||||
*/
|
||||
protected <R extends WriteRequest<R>> R prepareWriteRequest(R request) {
|
||||
|
||||
if (refreshPolicy == null) {
|
||||
return request;
|
||||
}
|
||||
|
||||
return request.setRefreshPolicy(RequestFactory.toElasticsearchRefreshPolicy(refreshPolicy));
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region SearchOperations
|
||||
@@ -295,8 +316,10 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
|
||||
SearchRequest searchRequest = requestFactory.searchRequest(query, clazz, index);
|
||||
SearchResponse response = execute(client -> client.search(searchRequest, RequestOptions.DEFAULT));
|
||||
|
||||
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<T>(elasticsearchConverter, clazz, index);
|
||||
SearchDocumentResponseCallback<SearchHits<T>> callback = new ReadSearchDocumentResponseCallback<>(clazz, index);
|
||||
return callback.doWith(SearchDocumentResponse.from(response));
|
||||
|
||||
return callback.doWith(SearchDocumentResponse.from(response, getEntityCreator(documentCallback)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -310,9 +333,10 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
|
||||
|
||||
SearchResponse response = execute(client -> client.search(searchRequest, RequestOptions.DEFAULT));
|
||||
|
||||
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<T>(elasticsearchConverter, clazz, index);
|
||||
SearchDocumentResponseCallback<SearchScrollHits<T>> callback = new ReadSearchScrollDocumentResponseCallback<>(clazz,
|
||||
index);
|
||||
return callback.doWith(SearchDocumentResponse.from(response));
|
||||
return callback.doWith(SearchDocumentResponse.from(response, getEntityCreator(documentCallback)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -324,9 +348,10 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
|
||||
|
||||
SearchResponse response = execute(client -> client.scroll(request, RequestOptions.DEFAULT));
|
||||
|
||||
SearchDocumentResponseCallback<SearchScrollHits<T>> callback = //
|
||||
new ReadSearchScrollDocumentResponseCallback<>(clazz, index);
|
||||
return callback.doWith(SearchDocumentResponse.from(response));
|
||||
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<T>(elasticsearchConverter, clazz, index);
|
||||
SearchDocumentResponseCallback<SearchScrollHits<T>> callback = new ReadSearchScrollDocumentResponseCallback<>(clazz,
|
||||
index);
|
||||
return callback.doWith(SearchDocumentResponse.from(response, getEntityCreator(documentCallback)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -353,8 +378,8 @@ public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
|
||||
Assert.isTrue(items.length == request.requests().size(), "Response should has same length with queries");
|
||||
return items;
|
||||
}
|
||||
// endregion
|
||||
|
||||
// endregion
|
||||
// region ClientCallback
|
||||
/**
|
||||
* Callback interface to be used with {@link #execute(ClientCallback)} for operating directly on
|
||||
|
||||
+58
-18
@@ -15,7 +15,10 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.elasticsearch.action.ActionFuture;
|
||||
@@ -33,9 +36,11 @@ import org.elasticsearch.action.search.MultiSearchRequest;
|
||||
import org.elasticsearch.action.search.MultiSearchResponse;
|
||||
import org.elasticsearch.action.search.SearchRequestBuilder;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.support.WriteRequest;
|
||||
import org.elasticsearch.action.support.WriteRequestBuilder;
|
||||
import org.elasticsearch.action.update.UpdateRequestBuilder;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.core.TimeValue;
|
||||
import org.elasticsearch.index.reindex.BulkByScrollResponse;
|
||||
import org.elasticsearch.index.reindex.UpdateByQueryRequestBuilder;
|
||||
import org.elasticsearch.search.suggest.SuggestBuilder;
|
||||
@@ -87,7 +92,7 @@ import org.springframework.util.Assert;
|
||||
* @deprecated as of 4.0
|
||||
*/
|
||||
@Deprecated
|
||||
public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
public class ElasticsearchTemplate extends AbstractElasticsearchRestTransportTemplate {
|
||||
private static final Logger QUERY_LOGGER = LoggerFactory
|
||||
.getLogger("org.springframework.data.elasticsearch.core.QUERY");
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ElasticsearchTemplate.class);
|
||||
@@ -131,7 +136,7 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
|
||||
Assert.notNull(clazz, "clazz must not be null");
|
||||
|
||||
return new DefaultTransportIndexOperations(client, elasticsearchConverter, clazz);
|
||||
return new TransportIndexTemplate(client, elasticsearchConverter, clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -139,7 +144,7 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
|
||||
Assert.notNull(index, "index must not be null");
|
||||
|
||||
return new DefaultTransportIndexOperations(client, elasticsearchConverter, index);
|
||||
return new TransportIndexTemplate(client, elasticsearchConverter, index);
|
||||
}
|
||||
// endregion
|
||||
|
||||
@@ -177,8 +182,8 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
|
||||
Object queryObject = query.getObject();
|
||||
if (queryObject != null) {
|
||||
updateIndexedObject(queryObject, IndexedObjectInformation.of(documentId, response.getSeqNo(),
|
||||
response.getPrimaryTerm(), response.getVersion()));
|
||||
query.setObject(updateIndexedObject(queryObject, IndexedObjectInformation.of(documentId, response.getSeqNo(),
|
||||
response.getPrimaryTerm(), response.getVersion())));
|
||||
}
|
||||
|
||||
return documentId;
|
||||
@@ -200,7 +205,6 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
public <T> List<MultiGetItem<T>> multiGet(Query query, Class<T> clazz, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(index, "index must not be null");
|
||||
Assert.notEmpty(query.getIds(), "No Ids defined for Query");
|
||||
|
||||
MultiGetRequestBuilder builder = requestFactory.multiGetRequestBuilder(client, query, clazz, index);
|
||||
|
||||
@@ -243,7 +247,8 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
|
||||
@Override
|
||||
public ByQueryResponse delete(Query query, Class<?> clazz, IndexCoordinates index) {
|
||||
return ByQueryResponse.of(requestFactory.deleteByQueryRequestBuilder(client, query, clazz, index).get());
|
||||
return ResponseConverter
|
||||
.byQueryResponseOf(requestFactory.deleteByQueryRequestBuilder(client, query, clazz, index).get());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -285,18 +290,45 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
// UpdateByQueryRequestBuilder has not parameters to set a routing value
|
||||
|
||||
final BulkByScrollResponse bulkByScrollResponse = updateByQueryRequestBuilder.execute().actionGet();
|
||||
return ByQueryResponse.of(bulkByScrollResponse);
|
||||
return ResponseConverter.byQueryResponseOf(bulkByScrollResponse);
|
||||
}
|
||||
|
||||
public List<IndexedObjectInformation> doBulkOperation(List<?> queries, BulkOptions bulkOptions,
|
||||
IndexCoordinates index) {
|
||||
BulkRequestBuilder bulkRequestBuilder = requestFactory.bulkRequestBuilder(client, queries, bulkOptions, index);
|
||||
bulkRequestBuilder = prepareWriteRequestBuilder(bulkRequestBuilder);
|
||||
final List<IndexedObjectInformation> indexedObjectInformations = checkForBulkOperationFailure(
|
||||
bulkRequestBuilder.execute().actionGet());
|
||||
updateIndexedObjectsWithQueries(queries, indexedObjectInformations);
|
||||
return indexedObjectInformations;
|
||||
|
||||
// do it in batches; test code on some machines kills the transport node when the size gets too much
|
||||
Collection<? extends List<?>> queryLists = partitionBasedOnSize(queries, 2500);
|
||||
List<IndexedObjectInformation> allIndexedObjectInformations = new ArrayList<>(queries.size());
|
||||
|
||||
queryLists.forEach(queryList -> {
|
||||
BulkRequestBuilder bulkRequestBuilder = requestFactory.bulkRequestBuilder(client, queryList, bulkOptions, index);
|
||||
bulkRequestBuilder = prepareWriteRequestBuilder(bulkRequestBuilder);
|
||||
final List<IndexedObjectInformation> indexedObjectInformations = checkForBulkOperationFailure(
|
||||
bulkRequestBuilder.execute().actionGet());
|
||||
updateIndexedObjectsWithQueries(queryList, indexedObjectInformations);
|
||||
allIndexedObjectInformations.addAll(indexedObjectInformations);
|
||||
});
|
||||
|
||||
return allIndexedObjectInformations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre process the write request before it is sent to the server, eg. by setting the
|
||||
* {@link WriteRequest#setRefreshPolicy(String) refresh policy} if applicable.
|
||||
*
|
||||
* @param requestBuilder must not be {@literal null}.
|
||||
* @param <R>
|
||||
* @return the processed {@link WriteRequest}.
|
||||
*/
|
||||
protected <R extends WriteRequestBuilder<R>> R prepareWriteRequestBuilder(R requestBuilder) {
|
||||
|
||||
if (refreshPolicy == null) {
|
||||
return requestBuilder;
|
||||
}
|
||||
|
||||
return requestBuilder.setRefreshPolicy(RequestFactory.toElasticsearchRefreshPolicy(refreshPolicy));
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region SearchOperations
|
||||
@@ -320,8 +352,9 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
SearchRequestBuilder searchRequestBuilder = requestFactory.searchRequestBuilder(client, query, clazz, index);
|
||||
SearchResponse response = getSearchResponse(searchRequestBuilder);
|
||||
|
||||
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<T>(elasticsearchConverter, clazz, index);
|
||||
SearchDocumentResponseCallback<SearchHits<T>> callback = new ReadSearchDocumentResponseCallback<>(clazz, index);
|
||||
return callback.doWith(SearchDocumentResponse.from(response));
|
||||
return callback.doWith(SearchDocumentResponse.from(response, getEntityCreator(documentCallback)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -336,9 +369,10 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
|
||||
SearchResponse response = getSearchResponseWithTimeout(action);
|
||||
|
||||
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<T>(elasticsearchConverter, clazz, index);
|
||||
SearchDocumentResponseCallback<SearchScrollHits<T>> callback = new ReadSearchScrollDocumentResponseCallback<>(clazz,
|
||||
index);
|
||||
return callback.doWith(SearchDocumentResponse.from(response));
|
||||
return callback.doWith(SearchDocumentResponse.from(response, getEntityCreator(documentCallback)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -352,9 +386,10 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
|
||||
SearchResponse response = getSearchResponseWithTimeout(action);
|
||||
|
||||
ReadDocumentCallback<T> documentCallback = new ReadDocumentCallback<T>(elasticsearchConverter, clazz, index);
|
||||
SearchDocumentResponseCallback<SearchScrollHits<T>> callback = new ReadSearchScrollDocumentResponseCallback<>(clazz,
|
||||
index);
|
||||
return callback.doWith(SearchDocumentResponse.from(response));
|
||||
return callback.doWith(SearchDocumentResponse.from(response, getEntityCreator(documentCallback)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -411,6 +446,11 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
public Client getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
<T> Collection<List<T>> partitionBasedOnSize(List<T> inputList, int size) {
|
||||
final AtomicInteger counter = new AtomicInteger(0);
|
||||
return inputList.stream().collect(Collectors.groupingBy(s -> counter.getAndIncrement() / size)).values();
|
||||
}
|
||||
// endregion
|
||||
|
||||
/**
|
||||
|
||||
@@ -93,22 +93,6 @@ class EntityOperations {
|
||||
return AdaptibleMappedEntity.of(entity, context, conversionService, routingResolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine index name and type name from {@link Entity} with {@code index} and {@code type} overrides. Allows using
|
||||
* preferred values for index and type if provided, otherwise fall back to index and type defined on entity level.
|
||||
*
|
||||
* @param entity the entity to determine the index name. Can be {@literal null} if {@code index} and {@literal type}
|
||||
* are provided.
|
||||
* @param index index name override can be {@literal null}.
|
||||
* @param type index type override can be {@literal null}.
|
||||
* @return the {@link IndexCoordinates} containing index name and index type.
|
||||
* @deprecated since 4.1, use {@link EntityOperations#determineIndex(Entity, String)}
|
||||
*/
|
||||
@Deprecated
|
||||
IndexCoordinates determineIndex(Entity<?> entity, @Nullable String index, @Nullable String type) {
|
||||
return determineIndex(entity.getPersistentEntity(), index, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine index name and type name from {@link Entity} with {@code index} and {@code type} overrides. Allows using
|
||||
* preferred values for index and type if provided, otherwise fall back to index and type defined on entity level.
|
||||
@@ -122,24 +106,6 @@ class EntityOperations {
|
||||
return determineIndex(entity.getPersistentEntity(), index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine index name and type name from {@link ElasticsearchPersistentEntity} with {@code index} and {@code type}
|
||||
* overrides. Allows using preferred values for index and type if provided, otherwise fall back to index and type
|
||||
* defined on entity level.
|
||||
*
|
||||
* @param persistentEntity the entity to determine the index name. Can be {@literal null} if {@code index} and
|
||||
* {@literal type} are provided.
|
||||
* @param index index name override can be {@literal null}.
|
||||
* @param type index type override can be {@literal null}.
|
||||
* @return the {@link IndexCoordinates} containing index name and index type.
|
||||
* @deprecated since 4.1, use {@link EntityOperations#determineIndex(ElasticsearchPersistentEntity, String)}
|
||||
*/
|
||||
@Deprecated
|
||||
IndexCoordinates determineIndex(ElasticsearchPersistentEntity<?> persistentEntity, @Nullable String index,
|
||||
@Nullable String type) {
|
||||
return determineIndex(persistentEntity, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine index name and type name from {@link ElasticsearchPersistentEntity} with {@code index} and {@code type}
|
||||
* overrides. Allows using preferred values for index and type if provided, otherwise fall back to index and type
|
||||
@@ -234,25 +200,6 @@ class EntityOperations {
|
||||
*/
|
||||
interface AdaptibleEntity<T> extends Entity<T> {
|
||||
|
||||
/**
|
||||
* Returns whether the entity has a parent.
|
||||
*
|
||||
* @return {@literal true} if the entity has a parent that has an {@literal id}.
|
||||
* @deprecated since 4.1, not supported anymore by Elasticsearch
|
||||
*/
|
||||
@Deprecated
|
||||
boolean hasParent();
|
||||
|
||||
/**
|
||||
* Returns the parent Id. Can be {@literal null}.
|
||||
*
|
||||
* @return can be {@literal null}.
|
||||
* @deprecated since 4.1, not supported anymore by Elasticsearch
|
||||
*/
|
||||
@Deprecated
|
||||
@Nullable
|
||||
Object getParentId();
|
||||
|
||||
/**
|
||||
* Populates the identifier of the backing entity if it has an identifier property and there's no identifier
|
||||
* currently present.
|
||||
@@ -339,24 +286,6 @@ class EntityOperations {
|
||||
return map.get(ID_FIELD);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.EntityOperations.AdaptibleEntity#hasParent()
|
||||
*/
|
||||
@Override
|
||||
public boolean hasParent() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.EntityOperations.AdaptibleEntity#getParentId()
|
||||
*/
|
||||
@Override
|
||||
public Entity<?> getParentId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.EntityOperations.AdaptibleEntity#populateIdIfNecessary(java.lang.Object)
|
||||
@@ -548,7 +477,6 @@ class EntityOperations {
|
||||
*/
|
||||
private static class AdaptibleMappedEntity<T> extends MappedEntity<T> implements AdaptibleEntity<T> {
|
||||
|
||||
private final T bean;
|
||||
private final ElasticsearchPersistentEntity<?> entity;
|
||||
private final ConvertingPropertyAccessor<T> propertyAccessor;
|
||||
private final IdentifierAccessor identifierAccessor;
|
||||
@@ -561,7 +489,6 @@ class EntityOperations {
|
||||
|
||||
super(entity, identifierAccessor, propertyAccessor);
|
||||
|
||||
this.bean = bean;
|
||||
this.entity = entity;
|
||||
this.propertyAccessor = propertyAccessor;
|
||||
this.identifierAccessor = identifierAccessor;
|
||||
@@ -582,16 +509,8 @@ class EntityOperations {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasParent() {
|
||||
return getRequiredPersistentEntity().getParentIdProperty() != null;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public Object getParentId() {
|
||||
|
||||
ElasticsearchPersistentProperty parentProperty = getRequiredPersistentEntity().getParentIdProperty();
|
||||
return propertyAccessor.getProperty(parentProperty);
|
||||
public T getBean() {
|
||||
return propertyAccessor.getBean();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -668,7 +587,7 @@ class EntityOperations {
|
||||
@Override
|
||||
public String getRouting() {
|
||||
|
||||
String routing = routingResolver.getRouting(bean);
|
||||
String routing = routingResolver.getRouting(propertyAccessor.getBean());
|
||||
|
||||
if (routing != null) {
|
||||
return routing;
|
||||
|
||||
@@ -19,7 +19,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.elasticsearch.cluster.metadata.AliasMetadata;
|
||||
import org.springframework.data.elasticsearch.core.document.Document;
|
||||
import org.springframework.data.elasticsearch.core.index.AliasActions;
|
||||
import org.springframework.data.elasticsearch.core.index.AliasData;
|
||||
@@ -30,7 +29,6 @@ import org.springframework.data.elasticsearch.core.index.PutTemplateRequest;
|
||||
import org.springframework.data.elasticsearch.core.index.Settings;
|
||||
import org.springframework.data.elasticsearch.core.index.TemplateData;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.query.AliasQuery;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
@@ -190,35 +188,6 @@ public interface IndexOperations {
|
||||
// endregion
|
||||
|
||||
// region aliases
|
||||
/**
|
||||
* Add an alias.
|
||||
*
|
||||
* @param query query defining the alias
|
||||
* @return true if the alias was created
|
||||
* @deprecated since 4.1 use {@link #alias(AliasActions)}
|
||||
*/
|
||||
@Deprecated
|
||||
boolean addAlias(AliasQuery query);
|
||||
|
||||
/**
|
||||
* Get the alias information for a specified index.
|
||||
*
|
||||
* @return alias information
|
||||
* @deprecated since 4.1, use {@link #getAliases(String...)} or {@link #getAliasesForIndex(String...)}.
|
||||
*/
|
||||
@Deprecated
|
||||
List<AliasMetadata> queryForAlias();
|
||||
|
||||
/**
|
||||
* Remove an alias.
|
||||
*
|
||||
* @param query query defining the alias
|
||||
* @return true if the alias was removed
|
||||
* @deprecated since 4.1 use {@link #alias(AliasActions)}
|
||||
*/
|
||||
@Deprecated
|
||||
boolean removeAlias(AliasQuery query);
|
||||
|
||||
/**
|
||||
* Executes the given {@link AliasActions}.
|
||||
*
|
||||
|
||||
+4
-3
@@ -25,12 +25,12 @@ import org.springframework.lang.Nullable;
|
||||
* @since 4.1
|
||||
*/
|
||||
public class IndexedObjectInformation {
|
||||
private final String id;
|
||||
@Nullable private final String id;
|
||||
@Nullable private final Long seqNo;
|
||||
@Nullable private final Long primaryTerm;
|
||||
@Nullable private final Long version;
|
||||
|
||||
private IndexedObjectInformation(String id, @Nullable Long seqNo, @Nullable Long primaryTerm,
|
||||
private IndexedObjectInformation(@Nullable String id, @Nullable Long seqNo, @Nullable Long primaryTerm,
|
||||
@Nullable Long version) {
|
||||
this.id = id;
|
||||
this.seqNo = seqNo;
|
||||
@@ -38,11 +38,12 @@ public class IndexedObjectInformation {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public static IndexedObjectInformation of(String id, @Nullable Long seqNo, @Nullable Long primaryTerm,
|
||||
public static IndexedObjectInformation of(@Nullable String id, @Nullable Long seqNo, @Nullable Long primaryTerm,
|
||||
@Nullable Long version) {
|
||||
return new IndexedObjectInformation(id, seqNo, primaryTerm, version);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,442 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Simple value object to work with ranges and boundaries.
|
||||
*
|
||||
* @author Sascha Woo
|
||||
* @since 4.3
|
||||
*/
|
||||
public class Range<T> {
|
||||
|
||||
private final static Range<?> UNBOUNDED = Range.of(Bound.unbounded(), Bound.UNBOUNDED);
|
||||
|
||||
/**
|
||||
* The lower bound of the range.
|
||||
*/
|
||||
private final Bound<T> lowerBound;
|
||||
|
||||
/**
|
||||
* The upper bound of the range.
|
||||
*/
|
||||
private final Bound<T> upperBound;
|
||||
|
||||
/**
|
||||
* Creates a new {@link Range} with inclusive bounds for both values.
|
||||
*
|
||||
* @param <T>
|
||||
* @param from must not be {@literal null}.
|
||||
* @param to must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static <T> Range<T> closed(T from, T to) {
|
||||
return new Range<>(Bound.inclusive(from), Bound.inclusive(to));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Range with the given value as sole member.
|
||||
*
|
||||
* @param <T>
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
* @see Range#closed(T, T)
|
||||
*/
|
||||
public static <T> Range<T> just(T value) {
|
||||
return Range.closed(value, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new left-open {@link Range}, i.e. left exclusive, right inclusive.
|
||||
*
|
||||
* @param <T>
|
||||
* @param from must not be {@literal null}.
|
||||
* @param to must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static <T> Range<T> leftOpen(T from, T to) {
|
||||
return new Range<>(Bound.exclusive(from), Bound.inclusive(to));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a left-unbounded {@link Range} (the left bound set to {@link Bound#unbounded()}) with the given right
|
||||
* bound.
|
||||
*
|
||||
* @param <T>
|
||||
* @param to the right {@link Bound}, must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static <T> Range<T> leftUnbounded(Bound<T> to) {
|
||||
return new Range<>(Bound.unbounded(), to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Range} with the given lower and upper bound.
|
||||
*
|
||||
* @param lowerBound must not be {@literal null}.
|
||||
* @param upperBound must not be {@literal null}.
|
||||
*/
|
||||
public static <T> Range<T> of(Bound<T> lowerBound, Bound<T> upperBound) {
|
||||
return new Range<>(lowerBound, upperBound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Range} with exclusive bounds for both values.
|
||||
*
|
||||
* @param <T>
|
||||
* @param from must not be {@literal null}.
|
||||
* @param to must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static <T> Range<T> open(T from, T to) {
|
||||
return new Range<>(Bound.exclusive(from), Bound.exclusive(to));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new right-open {@link Range}, i.e. left inclusive, right exclusive.
|
||||
*
|
||||
* @param <T>
|
||||
* @param from must not be {@literal null}.
|
||||
* @param to must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static <T> Range<T> rightOpen(T from, T to) {
|
||||
return new Range<>(Bound.inclusive(from), Bound.exclusive(to));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a right-unbounded {@link Range} (the right bound set to {@link Bound#unbounded()}) with the given left
|
||||
* bound.
|
||||
*
|
||||
* @param <T>
|
||||
* @param from the left {@link Bound}, must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static <T> Range<T> rightUnbounded(Bound<T> from) {
|
||||
return new Range<>(from, Bound.unbounded());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unbounded {@link Range}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Range<T> unbounded() {
|
||||
return (Range<T>) UNBOUNDED;
|
||||
}
|
||||
|
||||
private Range(Bound<T> lowerBound, Bound<T> upperBound) {
|
||||
|
||||
Assert.notNull(lowerBound, "Lower bound must not be null!");
|
||||
Assert.notNull(upperBound, "Upper bound must not be null!");
|
||||
|
||||
this.lowerBound = lowerBound;
|
||||
this.upperBound = upperBound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the {@link Range} contains the given value.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean contains(T value) {
|
||||
|
||||
Assert.notNull(value, "Reference value must not be null!");
|
||||
Assert.isInstanceOf(Comparable.class, value, "value must implements Comparable!");
|
||||
|
||||
boolean greaterThanLowerBound = lowerBound.getValue() //
|
||||
.map(it -> lowerBound.isInclusive() ? ((Comparable<? super T>) it).compareTo(value) <= 0
|
||||
: ((Comparable<? super T>) it).compareTo(value) < 0) //
|
||||
.orElse(true);
|
||||
|
||||
boolean lessThanUpperBound = upperBound.getValue() //
|
||||
.map(it -> upperBound.isInclusive() ? ((Comparable<? super T>) it).compareTo(value) >= 0
|
||||
: ((Comparable<? super T>) it).compareTo(value) > 0) //
|
||||
.orElse(true);
|
||||
|
||||
return greaterThanLowerBound && lessThanUpperBound;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(o instanceof Range)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Range<?> range = (Range<?>) o;
|
||||
|
||||
if (!ObjectUtils.nullSafeEquals(lowerBound, range.lowerBound)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ObjectUtils.nullSafeEquals(upperBound, range.upperBound);
|
||||
}
|
||||
|
||||
public Range.Bound<T> getLowerBound() {
|
||||
return this.lowerBound;
|
||||
}
|
||||
|
||||
public Range.Bound<T> getUpperBound() {
|
||||
return this.upperBound;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = ObjectUtils.nullSafeHashCode(lowerBound);
|
||||
result = 31 * result + ObjectUtils.nullSafeHashCode(upperBound);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s-%s", lowerBound.toPrefixString(), upperBound.toSuffixString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Value object representing a boundary. A boundary can either be {@link #unbounded() unbounded}, {@link #inclusive(T)
|
||||
* including its value} or {@link #exclusive(T) its value}.
|
||||
*/
|
||||
public static final class Bound<T> {
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" }) //
|
||||
private static final Bound<?> UNBOUNDED = new Bound(Optional.empty(), true);
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
private final Optional<T> value;
|
||||
private final boolean inclusive;
|
||||
|
||||
/**
|
||||
* Creates a boundary excluding {@code value}.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Bound<Double> exclusive(double value) {
|
||||
return exclusive((Double) value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a boundary excluding {@code value}.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Bound<Float> exclusive(float value) {
|
||||
return exclusive((Float) value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a boundary excluding {@code value}.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Bound<Integer> exclusive(int value) {
|
||||
return exclusive((Integer) value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a boundary excluding {@code value}.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Bound<Long> exclusive(long value) {
|
||||
return exclusive((Long) value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a boundary excluding {@code value}.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static <T> Bound<T> exclusive(T value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
Assert.isInstanceOf(Comparable.class, value, "value must implements Comparable!");
|
||||
return new Bound<>(Optional.of(value), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a boundary including {@code value}.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Bound<Double> inclusive(double value) {
|
||||
return inclusive((Double) value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a boundary including {@code value}.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Bound<Float> inclusive(float value) {
|
||||
return inclusive((Float) value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a boundary including {@code value}.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Bound<Integer> inclusive(int value) {
|
||||
return inclusive((Integer) value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a boundary including {@code value}.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static Bound<Long> inclusive(long value) {
|
||||
return inclusive((Long) value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a boundary including {@code value}.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static <T> Bound<T> inclusive(T value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
Assert.isInstanceOf(Comparable.class, value, "value must implements Comparable!");
|
||||
return new Bound<>(Optional.of(value), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an unbounded {@link Bound}.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Bound<T> unbounded() {
|
||||
return (Bound<T>) UNBOUNDED;
|
||||
}
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
private Bound(Optional<T> value, boolean inclusive) {
|
||||
this.value = value;
|
||||
this.inclusive = inclusive;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(o instanceof Bound)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Bound<?> bound = (Bound<?>) o;
|
||||
|
||||
if (inclusive != bound.inclusive)
|
||||
return false;
|
||||
|
||||
return ObjectUtils.nullSafeEquals(value, bound.value);
|
||||
}
|
||||
|
||||
public Optional<T> getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = ObjectUtils.nullSafeHashCode(value);
|
||||
result = 31 * result + (inclusive ? 1 : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this boundary is bounded.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isBounded() {
|
||||
return value.isPresent();
|
||||
}
|
||||
|
||||
public boolean isInclusive() {
|
||||
return this.inclusive;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return value.map(Object::toString).orElse("unbounded");
|
||||
}
|
||||
|
||||
String toPrefixString() {
|
||||
|
||||
return getValue() //
|
||||
.map(Object::toString) //
|
||||
.map(it -> isInclusive() ? "[".concat(it) : "(".concat(it)) //
|
||||
.orElse("unbounded");
|
||||
}
|
||||
|
||||
String toSuffixString() {
|
||||
|
||||
return getValue() //
|
||||
.map(Object::toString) //
|
||||
.map(it -> isInclusive() ? it.concat("]") : it.concat(")")) //
|
||||
.orElse("unbounded");
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-2
@@ -106,7 +106,8 @@ public interface ReactiveDocumentOperations {
|
||||
|
||||
/**
|
||||
* Index entities in the given {@literal index}. If the {@literal index} is {@literal null} or empty the index name
|
||||
* provided via entity metadata is used.
|
||||
* provided via entity metadata is used. On errors returns with
|
||||
* {@link org.springframework.data.elasticsearch.BulkFailureException} with information about the failed operation
|
||||
*
|
||||
* @param entities must not be {@literal null}.
|
||||
* @param index the target index, must not be {@literal null}
|
||||
@@ -148,6 +149,8 @@ public interface ReactiveDocumentOperations {
|
||||
* @param query the query defining the ids of the objects to get
|
||||
* @param clazz the type of the object to be returned, used to determine the index
|
||||
* @return flux with list of {@link MultiGetItem}s that contain the entities
|
||||
* @see Query#multiGetQuery(Collection)
|
||||
* @see Query#multiGetQueryWithRouting(List)
|
||||
* @since 4.1
|
||||
*/
|
||||
<T> Flux<MultiGetItem<T>> multiGet(Query query, Class<T> clazz);
|
||||
@@ -159,12 +162,15 @@ public interface ReactiveDocumentOperations {
|
||||
* @param clazz the type of the object to be returned
|
||||
* @param index the index(es) from which the objects are read.
|
||||
* @return flux with list of {@link MultiGetItem}s that contain the entities
|
||||
* @see Query#multiGetQuery(Collection)
|
||||
* @see Query#multiGetQueryWithRouting(List)
|
||||
* @since 4.0
|
||||
*/
|
||||
<T> Flux<MultiGetItem<T>> multiGet(Query query, Class<T> clazz, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* Bulk update all objects. Will do update.
|
||||
* Bulk update all objects. Will do update. On errors returns with
|
||||
* {@link org.springframework.data.elasticsearch.BulkFailureException} with information about the failed operation
|
||||
*
|
||||
* @param queries the queries to execute in bulk
|
||||
* @since 4.0
|
||||
@@ -178,6 +184,7 @@ public interface ReactiveDocumentOperations {
|
||||
*
|
||||
* @param queries the queries to execute in bulk
|
||||
* @param bulkOptions options to be added to the bulk request
|
||||
* @throws org.springframework.data.elasticsearch.BulkFailureException with information about the failed operation
|
||||
* @since 4.0
|
||||
*/
|
||||
Mono<Void> bulkUpdate(List<UpdateQuery> queries, BulkOptions bulkOptions, IndexCoordinates index);
|
||||
|
||||
+174
-77
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2018-2021 the original author or authors.
|
||||
* Copyright 2018-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,6 +25,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.action.DocWriteResponse;
|
||||
import org.elasticsearch.action.bulk.BulkItemResponse;
|
||||
import org.elasticsearch.action.bulk.BulkRequest;
|
||||
@@ -39,11 +40,10 @@ import org.elasticsearch.action.support.IndicesOptions;
|
||||
import org.elasticsearch.action.support.WriteRequest;
|
||||
import org.elasticsearch.action.update.UpdateRequest;
|
||||
import org.elasticsearch.index.get.GetResult;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.index.reindex.BulkByScrollResponse;
|
||||
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
|
||||
import org.elasticsearch.index.reindex.UpdateByQueryRequest;
|
||||
import org.elasticsearch.search.aggregations.Aggregation;
|
||||
import org.elasticsearch.search.suggest.Suggest;
|
||||
import org.elasticsearch.search.suggest.SuggestBuilder;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.slf4j.Logger;
|
||||
@@ -57,6 +57,7 @@ import org.springframework.data.elasticsearch.NoSuchIndexException;
|
||||
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
|
||||
import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient;
|
||||
import org.springframework.data.elasticsearch.core.EntityOperations.AdaptibleEntity;
|
||||
import org.springframework.data.elasticsearch.core.clients.elasticsearch7.ElasticsearchAggregation;
|
||||
import org.springframework.data.elasticsearch.core.cluster.DefaultReactiveClusterOperations;
|
||||
import org.springframework.data.elasticsearch.core.cluster.ReactiveClusterOperations;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||
@@ -75,12 +76,14 @@ import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMa
|
||||
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.NativeSearchQueryBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.core.query.SeqNoPrimaryTerm;
|
||||
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.UpdateResponse;
|
||||
import org.springframework.data.elasticsearch.core.routing.DefaultRoutingResolver;
|
||||
import org.springframework.data.elasticsearch.core.routing.RoutingResolver;
|
||||
import org.springframework.data.elasticsearch.core.suggest.response.Suggest;
|
||||
import org.springframework.data.elasticsearch.support.VersionInfo;
|
||||
import org.springframework.data.mapping.PersistentPropertyAccessor;
|
||||
import org.springframework.data.mapping.callback.ReactiveEntityCallbacks;
|
||||
@@ -142,7 +145,9 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
this.operations = new EntityOperations(this.mappingContext);
|
||||
this.requestFactory = new RequestFactory(converter);
|
||||
|
||||
logVersions();
|
||||
// initialize the VersionInfo class in the initialization phase
|
||||
// noinspection ResultOfMethodCallIgnored
|
||||
VersionInfo.versionProperties();
|
||||
}
|
||||
|
||||
private ReactiveElasticsearchTemplate copy() {
|
||||
@@ -155,11 +160,18 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
return copy;
|
||||
}
|
||||
|
||||
private void logVersions() {
|
||||
getClusterVersion() //
|
||||
.doOnSuccess(VersionInfo::logVersions) //
|
||||
.doOnError(e -> VersionInfo.logVersions(null)) //
|
||||
.subscribe();
|
||||
/**
|
||||
* logs the versions of the different Elasticsearch components.
|
||||
*
|
||||
* @return a Mono signalling finished execution
|
||||
* @since 4.3
|
||||
*/
|
||||
public Mono<Void> logVersions() {
|
||||
return getVendor() //
|
||||
.doOnNext(vendor -> getRuntimeLibraryVersion() //
|
||||
.doOnNext(runtimeLibraryVersion -> getClusterVersion() //
|
||||
.doOnNext(clusterVersion -> VersionInfo.logVersions(vendor, runtimeLibraryVersion, clusterVersion)))) //
|
||||
.then(); //
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -275,27 +287,42 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
}
|
||||
|
||||
private <T> T updateIndexedObject(T entity, IndexedObjectInformation indexedObjectInformation) {
|
||||
AdaptibleEntity<T> adaptibleEntity = operations.forEntity(entity, converter.getConversionService(),
|
||||
routingResolver);
|
||||
adaptibleEntity.populateIdIfNecessary(indexedObjectInformation.getId());
|
||||
|
||||
ElasticsearchPersistentEntity<?> persistentEntity = getPersistentEntityFor(entity.getClass());
|
||||
ElasticsearchPersistentEntity<?> persistentEntity = converter.getMappingContext()
|
||||
.getPersistentEntity(entity.getClass());
|
||||
|
||||
if (persistentEntity != null) {
|
||||
PersistentPropertyAccessor<Object> propertyAccessor = persistentEntity.getPropertyAccessor(entity);
|
||||
ElasticsearchPersistentProperty idProperty = persistentEntity.getIdProperty();
|
||||
|
||||
// Only deal with text because ES generated Ids are strings!
|
||||
if (indexedObjectInformation.getId() != null && idProperty != null
|
||||
&& idProperty.getType().isAssignableFrom(String.class)) {
|
||||
propertyAccessor.setProperty(idProperty, indexedObjectInformation.getId());
|
||||
}
|
||||
|
||||
if (indexedObjectInformation.getSeqNo() != null && indexedObjectInformation.getPrimaryTerm() != null
|
||||
&& persistentEntity.hasSeqNoPrimaryTermProperty()) {
|
||||
ElasticsearchPersistentProperty seqNoPrimaryTermProperty = persistentEntity.getSeqNoPrimaryTermProperty();
|
||||
// noinspection ConstantConditions
|
||||
propertyAccessor.setProperty(seqNoPrimaryTermProperty,
|
||||
new SeqNoPrimaryTerm(indexedObjectInformation.getSeqNo(), indexedObjectInformation.getPrimaryTerm()));
|
||||
}
|
||||
|
||||
if (indexedObjectInformation.getVersion() != null && persistentEntity.hasVersionProperty()) {
|
||||
ElasticsearchPersistentProperty versionProperty = persistentEntity.getVersionProperty();
|
||||
// noinspection ConstantConditions
|
||||
propertyAccessor.setProperty(versionProperty, indexedObjectInformation.getVersion());
|
||||
}
|
||||
}
|
||||
|
||||
// noinspection unchecked
|
||||
T updatedEntity = (T) propertyAccessor.getBean();
|
||||
return updatedEntity;
|
||||
} else {
|
||||
AdaptibleEntity<T> adaptibleEntity = operations.forEntity(entity, converter.getConversionService(),
|
||||
routingResolver);
|
||||
adaptibleEntity.populateIdIfNecessary(indexedObjectInformation.getId());
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
@@ -310,7 +337,6 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
Assert.notNull(index, "Index must not be null");
|
||||
Assert.notNull(clazz, "Class must not be null");
|
||||
Assert.notNull(query, "Query must not be null");
|
||||
Assert.notEmpty(query.getIds(), "No Id define for Query");
|
||||
|
||||
DocumentCallback<T> callback = new ReadDocumentCallback<>(converter, clazz, index);
|
||||
|
||||
@@ -348,8 +374,7 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
protected Flux<BulkItemResponse> doBulkOperation(List<?> queries, BulkOptions bulkOptions, IndexCoordinates index) {
|
||||
BulkRequest bulkRequest = prepareWriteRequest(requestFactory.bulkRequest(queries, bulkOptions, index));
|
||||
return client.bulk(bulkRequest) //
|
||||
.onErrorMap(
|
||||
e -> new UncategorizedElasticsearchException("Error while bulk for request: " + bulkRequest.toString(), e)) //
|
||||
.onErrorMap(e -> new UncategorizedElasticsearchException("Error while bulk for request: " + bulkRequest, e)) //
|
||||
.flatMap(this::checkForBulkOperationFailure) //
|
||||
.flatMapMany(response -> Flux.fromArray(response.getItems()));
|
||||
}
|
||||
@@ -457,7 +482,7 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
|
||||
DocumentCallback<T> callback = new ReadDocumentCallback<>(converter, entityType, index);
|
||||
|
||||
return doGet(id, index).flatMap(it -> callback.toEntity(DocumentAdapters.from(it)));
|
||||
return doGet(id, index).flatMap(response -> callback.toEntity(DocumentAdapters.from(response)));
|
||||
}
|
||||
|
||||
private Mono<GetResult> doGet(String id, IndexCoordinates index) {
|
||||
@@ -537,7 +562,7 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
|
||||
Assert.notNull(query, "Query must not be null!");
|
||||
|
||||
return doDeleteBy(query, entityType, index).map(ByQueryResponse::of);
|
||||
return doDeleteBy(query, entityType, index).map(ResponseConverter::byQueryResponseOf);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -631,7 +656,7 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
}
|
||||
|
||||
/**
|
||||
* Customization hook to modify a generated {@link DeleteRequest} prior to its execution. Eg. by setting the
|
||||
* Customization hook to modify a generated {@link DeleteRequest} prior to its execution. E.g. by setting the
|
||||
* {@link WriteRequest#setRefreshPolicy(String) refresh policy} if applicable.
|
||||
*
|
||||
* @param request the generated {@link DeleteRequest}.
|
||||
@@ -642,7 +667,7 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
}
|
||||
|
||||
/**
|
||||
* Customization hook to modify a generated {@link DeleteByQueryRequest} prior to its execution. Eg. by setting the
|
||||
* Customization hook to modify a generated {@link DeleteByQueryRequest} prior to its execution. E.g. by setting the
|
||||
* {@link WriteRequest#setRefreshPolicy(String) refresh policy} if applicable.
|
||||
*
|
||||
* @param request the generated {@link DeleteByQueryRequest}.
|
||||
@@ -667,7 +692,7 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
}
|
||||
|
||||
/**
|
||||
* Customization hook to modify a generated {@link IndexRequest} prior to its execution. Eg. by setting the
|
||||
* Customization hook to modify a generated {@link IndexRequest} prior to its execution. E.g. by setting the
|
||||
* {@link WriteRequest#setRefreshPolicy(String) refresh policy} if applicable.
|
||||
*
|
||||
* @param source the source object the {@link IndexRequest} was derived from.
|
||||
@@ -679,7 +704,7 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre process the write request before it is sent to the server, eg. by setting the
|
||||
* Preprocess the write request before it is sent to the server, e.g. by setting the
|
||||
* {@link WriteRequest#setRefreshPolicy(String) refresh policy} if applicable.
|
||||
*
|
||||
* @param request must not be {@literal null}.
|
||||
@@ -732,61 +757,108 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
private Flux<SearchDocument> doFind(Query query, Class<?> clazz, IndexCoordinates index) {
|
||||
|
||||
return Flux.defer(() -> {
|
||||
SearchRequest request = requestFactory.searchRequest(query, clazz, index);
|
||||
request = prepareSearchRequest(request);
|
||||
|
||||
if (query.getPageable().isPaged() || query.isLimiting()) {
|
||||
return doFind(request);
|
||||
} else {
|
||||
SearchRequest request = requestFactory.searchRequest(query, clazz, index);
|
||||
boolean useScroll = !(query.getPageable().isPaged() || query.isLimiting());
|
||||
request = prepareSearchRequest(request, useScroll);
|
||||
|
||||
if (useScroll) {
|
||||
return doScroll(request);
|
||||
} else {
|
||||
return doFind(request);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Mono<SearchDocumentResponse> doFindForResponse(Query query, Class<?> clazz, IndexCoordinates index) {
|
||||
private <T> Mono<SearchDocumentResponse> doFindForResponse(Query query, Class<?> clazz, IndexCoordinates index) {
|
||||
|
||||
return Mono.defer(() -> {
|
||||
SearchRequest request = requestFactory.searchRequest(query, clazz, index);
|
||||
request = prepareSearchRequest(request);
|
||||
return doFindForResponse(request);
|
||||
request = prepareSearchRequest(request, false);
|
||||
|
||||
SearchDocumentCallback<?> documentCallback = new ReadSearchDocumentCallback<>(clazz, index);
|
||||
// noinspection unchecked
|
||||
SearchDocumentResponse.EntityCreator<T> entityCreator = searchDocument -> ((Mono<T>) documentCallback
|
||||
.toEntity(searchDocument)).toFuture();
|
||||
|
||||
return doFindForResponse(request, entityCreator);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<Aggregation> aggregate(Query query, Class<?> entityType) {
|
||||
public Flux<AggregationContainer<?>> aggregate(Query query, Class<?> entityType) {
|
||||
return aggregate(query, entityType, getIndexCoordinatesFor(entityType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<Aggregation> aggregate(Query query, Class<?> entityType, IndexCoordinates index) {
|
||||
return doAggregate(query, entityType, index);
|
||||
public Flux<AggregationContainer<?>> aggregate(Query query, Class<?> entityType, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(query, "query must not be null");
|
||||
Assert.notNull(entityType, "entityType must not be null");
|
||||
Assert.notNull(index, "index must not be null");
|
||||
|
||||
return Flux.defer(() -> {
|
||||
SearchRequest request = requestFactory.searchRequest(query, entityType, index);
|
||||
request = prepareSearchRequest(request, false);
|
||||
return doAggregate(request);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Customization hook on the actual execution result {@link Publisher}. <br />
|
||||
*
|
||||
* @param request the already prepared {@link SearchRequest} ready to be executed.
|
||||
* @return a {@link Flux} emitting the result of the operation.
|
||||
*/
|
||||
protected Flux<AggregationContainer<?>> doAggregate(SearchRequest request) {
|
||||
|
||||
if (QUERY_LOGGER.isDebugEnabled()) {
|
||||
QUERY_LOGGER.debug("Executing doCount: {}", request);
|
||||
}
|
||||
|
||||
return Flux.from(execute(client -> client.aggregate(request))) //
|
||||
.onErrorResume(NoSuchIndexException.class, it -> Flux.empty()).map(ElasticsearchAggregation::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<Suggest> suggest(SuggestBuilder suggestion, Class<?> entityType) {
|
||||
public Mono<Suggest> suggest(Query query, Class<?> entityType) {
|
||||
return suggest(query, entityType, getIndexCoordinatesFor(entityType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Suggest> suggest(Query query, Class<?> entityType, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(query, "query must not be null");
|
||||
Assert.notNull(entityType, "entityType must not be null");
|
||||
Assert.notNull(index, "index must not be null");
|
||||
|
||||
return doFindForResponse(query, entityType, index).mapNotNull(searchDocumentResponse -> {
|
||||
Suggest suggest = searchDocumentResponse.getSuggest();
|
||||
SearchHitMapping.mappingFor(entityType, converter).mapHitsInCompletionSuggestion(suggest);
|
||||
return suggest;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public Flux<org.elasticsearch.search.suggest.Suggest> suggest(SuggestBuilder suggestion, Class<?> entityType) {
|
||||
return doSuggest(suggestion, getIndexCoordinatesFor(entityType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<Suggest> suggest(SuggestBuilder suggestion, IndexCoordinates index) {
|
||||
@Deprecated
|
||||
public Flux<org.elasticsearch.search.suggest.Suggest> suggest(SuggestBuilder suggestion, IndexCoordinates index) {
|
||||
return doSuggest(suggestion, index);
|
||||
}
|
||||
|
||||
private Flux<Suggest> doSuggest(SuggestBuilder suggestion, IndexCoordinates index) {
|
||||
@Deprecated
|
||||
private Flux<org.elasticsearch.search.suggest.Suggest> doSuggest(SuggestBuilder suggestion, IndexCoordinates index) {
|
||||
return Flux.defer(() -> {
|
||||
SearchRequest request = requestFactory.searchRequest(suggestion, index);
|
||||
return Flux.from(execute(client -> client.suggest(request)));
|
||||
});
|
||||
}
|
||||
|
||||
private Flux<Aggregation> doAggregate(Query query, Class<?> entityType, IndexCoordinates index) {
|
||||
return Flux.defer(() -> {
|
||||
SearchRequest request = requestFactory.searchRequest(query, entityType, index);
|
||||
request = prepareSearchRequest(request);
|
||||
return doAggregate(request);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Long> count(Query query, Class<?> entityType) {
|
||||
return count(query, entityType, getIndexCoordinatesFor(entityType));
|
||||
@@ -801,7 +873,7 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
return Mono.defer(() -> {
|
||||
|
||||
SearchRequest request = requestFactory.searchRequest(query, entityType, index);
|
||||
request = prepareSearchRequest(request);
|
||||
request = prepareSearchRequest(request, false);
|
||||
return doCount(request);
|
||||
});
|
||||
}
|
||||
@@ -826,31 +898,18 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
* Customization hook on the actual execution result {@link Mono}. <br />
|
||||
*
|
||||
* @param request the already prepared {@link SearchRequest} ready to be executed.
|
||||
* @param entityCreator
|
||||
* @return a {@link Mono} emitting the result of the operation converted to s {@link SearchDocumentResponse}.
|
||||
*/
|
||||
protected Mono<SearchDocumentResponse> doFindForResponse(SearchRequest request) {
|
||||
protected <T> Mono<SearchDocumentResponse> doFindForResponse(SearchRequest request,
|
||||
SearchDocumentResponse.EntityCreator<T> entityCreator) {
|
||||
|
||||
if (QUERY_LOGGER.isDebugEnabled()) {
|
||||
QUERY_LOGGER.debug("Executing doFindForResponse: {}", request);
|
||||
}
|
||||
|
||||
return Mono.from(execute(client1 -> client1.searchForResponse(request))).map(SearchDocumentResponse::from);
|
||||
}
|
||||
|
||||
/**
|
||||
* Customization hook on the actual execution result {@link Publisher}. <br />
|
||||
*
|
||||
* @param request the already prepared {@link SearchRequest} ready to be executed.
|
||||
* @return a {@link Flux} emitting the result of the operation.
|
||||
*/
|
||||
protected Flux<Aggregation> doAggregate(SearchRequest request) {
|
||||
|
||||
if (QUERY_LOGGER.isDebugEnabled()) {
|
||||
QUERY_LOGGER.debug("Executing doCount: {}", request);
|
||||
}
|
||||
|
||||
return Flux.from(execute(client -> client.aggregate(request))) //
|
||||
.onErrorResume(NoSuchIndexException.class, it -> Flux.empty());
|
||||
return Mono.from(execute(client -> client.searchForResponse(request)))
|
||||
.map(searchResponse -> SearchDocumentResponse.from(searchResponse, entityCreator));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -886,19 +945,25 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
}
|
||||
|
||||
/**
|
||||
* Customization hook to modify a generated {@link SearchRequest} prior to its execution. Eg. by setting the
|
||||
* Customization hook to modify a generated {@link SearchRequest} prior to its execution. E.g. by setting the
|
||||
* {@link SearchRequest#indicesOptions(IndicesOptions) indices options} if applicable.
|
||||
*
|
||||
* @param request the generated {@link SearchRequest}.
|
||||
* @param useScroll
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
protected SearchRequest prepareSearchRequest(SearchRequest request) {
|
||||
protected SearchRequest prepareSearchRequest(SearchRequest request, boolean useScroll) {
|
||||
|
||||
if (indicesOptions == null) {
|
||||
return request;
|
||||
if (indicesOptions != null) {
|
||||
request = request.indicesOptions(indicesOptions);
|
||||
}
|
||||
|
||||
return request.indicesOptions(indicesOptions);
|
||||
// request_cache is not allowed on scroll requests.
|
||||
if (useScroll) {
|
||||
request = request.requestCache(null);
|
||||
}
|
||||
return request;
|
||||
|
||||
}
|
||||
|
||||
// endregion
|
||||
@@ -906,11 +971,41 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
// region Helper methods
|
||||
protected Mono<String> getClusterVersion() {
|
||||
try {
|
||||
return Mono.from(execute(client -> client.info())).map(mainResponse -> mainResponse.getVersion().toString());
|
||||
return Mono.from(execute(ReactiveElasticsearchClient::info))
|
||||
.map(mainResponse -> mainResponse.getVersion().toString());
|
||||
} catch (Exception ignored) {}
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the vendor name of the used cluster and client library
|
||||
* @since 4.3
|
||||
*/
|
||||
protected Mono<String> getVendor() {
|
||||
return Mono.just("Elasticsearch");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the version of the used client runtime library.
|
||||
* @since 4.3
|
||||
*/
|
||||
protected Mono<String> getRuntimeLibraryVersion() {
|
||||
return Mono.just(Version.CURRENT.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query matchAllQuery() {
|
||||
return new NativeSearchQueryBuilder().withQuery(QueryBuilders.matchAllQuery()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query idsQuery(List<String> ids) {
|
||||
|
||||
Assert.notNull(ids, "ids must not be null");
|
||||
|
||||
return new NativeSearchQueryBuilder().withQuery(QueryBuilders.idsQuery().addIds(ids.toArray(new String[] {})))
|
||||
.build();
|
||||
}
|
||||
// endregion
|
||||
|
||||
@Override
|
||||
@@ -935,12 +1030,12 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
|
||||
@Override
|
||||
public ReactiveIndexOperations indexOps(IndexCoordinates index) {
|
||||
return new DefaultReactiveIndexOperations(this, index);
|
||||
return new ReactiveIndexTemplate(this, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveIndexOperations indexOps(Class<?> clazz) {
|
||||
return new DefaultReactiveIndexOperations(this, clazz);
|
||||
return new ReactiveIndexTemplate(this, clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1089,17 +1184,19 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
}
|
||||
|
||||
T entity = reader.read(type, document);
|
||||
IndexedObjectInformation indexedObjectInformation = IndexedObjectInformation.of(
|
||||
document.hasId() ? document.getId() : null, document.getSeqNo(), document.getPrimaryTerm(),
|
||||
document.getVersion());
|
||||
entity = updateIndexedObject(entity, indexedObjectInformation);
|
||||
return maybeCallAfterConvert(entity, document, index);
|
||||
}
|
||||
}
|
||||
|
||||
protected interface SearchDocumentCallback<T> {
|
||||
|
||||
@NonNull
|
||||
Mono<T> toEntity(@NonNull SearchDocument response);
|
||||
Mono<T> toEntity(SearchDocument response);
|
||||
|
||||
@NonNull
|
||||
Mono<SearchHit<T>> toSearchHit(@NonNull SearchDocument response);
|
||||
Mono<SearchHit<T>> toSearchHit(SearchDocument response);
|
||||
}
|
||||
|
||||
protected class ReadSearchDocumentCallback<T> implements SearchDocumentCallback<T> {
|
||||
@@ -1142,7 +1239,7 @@ public class ReactiveElasticsearchTemplate implements ReactiveElasticsearchOpera
|
||||
}
|
||||
|
||||
private T entityAt(long index) {
|
||||
// it's safe to cast to int because the original indexed colleciton was fitting in memory
|
||||
// it's safe to cast to int because the original indexed collection was fitting in memory
|
||||
int intIndex = (int) index;
|
||||
return entities.get(intIndex);
|
||||
}
|
||||
|
||||
+11
-8
@@ -49,8 +49,8 @@ import org.springframework.data.elasticsearch.core.index.AliasData;
|
||||
import org.springframework.data.elasticsearch.core.index.DeleteTemplateRequest;
|
||||
import org.springframework.data.elasticsearch.core.index.ExistsTemplateRequest;
|
||||
import org.springframework.data.elasticsearch.core.index.GetTemplateRequest;
|
||||
import org.springframework.data.elasticsearch.core.index.MappingBuilder;
|
||||
import org.springframework.data.elasticsearch.core.index.PutTemplateRequest;
|
||||
import org.springframework.data.elasticsearch.core.index.ReactiveMappingBuilder;
|
||||
import org.springframework.data.elasticsearch.core.index.Settings;
|
||||
import org.springframework.data.elasticsearch.core.index.TemplateData;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
|
||||
@@ -63,9 +63,9 @@ import org.springframework.util.Assert;
|
||||
* @author George Popides
|
||||
* @since 4.1
|
||||
*/
|
||||
class DefaultReactiveIndexOperations implements ReactiveIndexOperations {
|
||||
class ReactiveIndexTemplate implements ReactiveIndexOperations {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultReactiveIndexOperations.class);
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ReactiveIndexTemplate.class);
|
||||
|
||||
@Nullable private final Class<?> boundClass;
|
||||
private final IndexCoordinates boundIndex;
|
||||
@@ -73,7 +73,7 @@ class DefaultReactiveIndexOperations implements ReactiveIndexOperations {
|
||||
private final ReactiveElasticsearchOperations operations;
|
||||
private final ElasticsearchConverter converter;
|
||||
|
||||
public DefaultReactiveIndexOperations(ReactiveElasticsearchOperations operations, IndexCoordinates index) {
|
||||
public ReactiveIndexTemplate(ReactiveElasticsearchOperations operations, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(operations, "operations must not be null");
|
||||
Assert.notNull(index, "index must not be null");
|
||||
@@ -85,7 +85,7 @@ class DefaultReactiveIndexOperations implements ReactiveIndexOperations {
|
||||
this.boundIndex = index;
|
||||
}
|
||||
|
||||
public DefaultReactiveIndexOperations(ReactiveElasticsearchOperations operations, Class<?> clazz) {
|
||||
public ReactiveIndexTemplate(ReactiveElasticsearchOperations operations, Class<?> clazz) {
|
||||
|
||||
Assert.notNull(operations, "operations must not be null");
|
||||
Assert.notNull(clazz, "clazz must not be null");
|
||||
@@ -183,11 +183,14 @@ class DefaultReactiveIndexOperations implements ReactiveIndexOperations {
|
||||
Mapping mappingAnnotation = AnnotatedElementUtils.findMergedAnnotation(clazz, Mapping.class);
|
||||
|
||||
if (mappingAnnotation != null) {
|
||||
return loadDocument(mappingAnnotation.mappingPath(), "@Mapping");
|
||||
String mappingPath = mappingAnnotation.mappingPath();
|
||||
|
||||
if (hasText(mappingPath)) {
|
||||
return loadDocument(mappingAnnotation.mappingPath(), "@Mapping");
|
||||
}
|
||||
}
|
||||
|
||||
String mapping = new MappingBuilder(converter).buildPropertyMapping(clazz);
|
||||
return Mono.just(Document.parse(mapping));
|
||||
return new ReactiveMappingBuilder(converter).buildReactivePropertyMapping(clazz).map(Document::parse);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -63,7 +63,7 @@ public abstract class ReactiveResourceUtil {
|
||||
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
sb.append(line);
|
||||
sb.append(line).append('\n');
|
||||
}
|
||||
|
||||
sink.next(sb.toString());
|
||||
|
||||
+60
-15
@@ -18,14 +18,13 @@ package org.springframework.data.elasticsearch.core;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.search.aggregations.Aggregation;
|
||||
import org.elasticsearch.search.suggest.Suggest;
|
||||
import java.util.List;
|
||||
|
||||
import org.elasticsearch.search.suggest.SuggestBuilder;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.core.query.StringQuery;
|
||||
import org.springframework.data.elasticsearch.core.suggest.response.Suggest;
|
||||
|
||||
/**
|
||||
* The reactive operations for the
|
||||
@@ -45,7 +44,7 @@ public interface ReactiveSearchOperations {
|
||||
* @return a {@link Mono} emitting the nr of matching documents.
|
||||
*/
|
||||
default Mono<Long> count(Class<?> entityType) {
|
||||
return count(new StringQuery(QueryBuilders.matchAllQuery().toString()), entityType);
|
||||
return count(matchAllQuery(), entityType);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,7 +123,6 @@ public interface ReactiveSearchOperations {
|
||||
/**
|
||||
* Search the index for entities matching the given {@link Query query}.
|
||||
*
|
||||
* @param <T>
|
||||
* @param query must not be {@literal null}.
|
||||
* @param entityType must not be {@literal null}.
|
||||
* @param <T>
|
||||
@@ -138,7 +136,6 @@ public interface ReactiveSearchOperations {
|
||||
/**
|
||||
* Search the index for entities matching the given {@link Query query}.
|
||||
*
|
||||
* @param <T>
|
||||
* @param query must not be {@literal null}.
|
||||
* @param entityType must not be {@literal null}.
|
||||
* @param resultType the projection result type.
|
||||
@@ -151,7 +148,6 @@ public interface ReactiveSearchOperations {
|
||||
/**
|
||||
* Search the index for entities matching the given {@link Query query}.
|
||||
*
|
||||
* @param <T>
|
||||
* @param query must not be {@literal null}.
|
||||
* @param entityType must not be {@literal null}.
|
||||
* @param index the target index, must not be {@literal null}
|
||||
@@ -166,7 +162,6 @@ public interface ReactiveSearchOperations {
|
||||
/**
|
||||
* Search the index for entities matching the given {@link Query query}.
|
||||
*
|
||||
* @param <T>
|
||||
* @param query must not be {@literal null}.
|
||||
* @param entityType must not be {@literal null}.
|
||||
* @param resultType the projection result type.
|
||||
@@ -185,7 +180,7 @@ public interface ReactiveSearchOperations {
|
||||
* @return a {@link Flux} emitting matching aggregations one by one.
|
||||
* @since 4.0
|
||||
*/
|
||||
Flux<Aggregation> aggregate(Query query, Class<?> entityType);
|
||||
Flux<AggregationContainer<?>> aggregate(Query query, Class<?> entityType);
|
||||
|
||||
/**
|
||||
* Perform an aggregation specified by the given {@link Query query}. <br />
|
||||
@@ -196,23 +191,73 @@ public interface ReactiveSearchOperations {
|
||||
* @return a {@link Flux} emitting matching aggregations one by one.
|
||||
* @since 4.0
|
||||
*/
|
||||
Flux<Aggregation> aggregate(Query query, Class<?> entityType, IndexCoordinates index);
|
||||
Flux<AggregationContainer<?>> aggregate(Query query, Class<?> entityType, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* Does a suggest query
|
||||
*
|
||||
* @param suggestion the query
|
||||
* @param entityType must not be {@literal null}.
|
||||
* @return the suggest response
|
||||
* @return the suggest response (Elasticsearch library classes)
|
||||
* @deprecated since 4.3, use {@link #suggest(Query, Class)}
|
||||
*/
|
||||
Flux<Suggest> suggest(SuggestBuilder suggestion, Class<?> entityType);
|
||||
@Deprecated
|
||||
Flux<org.elasticsearch.search.suggest.Suggest> suggest(SuggestBuilder suggestion, Class<?> entityType);
|
||||
|
||||
/**
|
||||
* Does a suggest query
|
||||
*
|
||||
* @param suggestion the query
|
||||
* @param index the index to run the query against
|
||||
* @return the suggest response
|
||||
* @return the suggest response (Elasticsearch library classes)
|
||||
* @deprecated since 4.3, use {@link #suggest(Query, Class, IndexCoordinates)}
|
||||
*/
|
||||
Flux<Suggest> suggest(SuggestBuilder suggestion, IndexCoordinates index);
|
||||
@Deprecated
|
||||
Flux<org.elasticsearch.search.suggest.Suggest> suggest(SuggestBuilder suggestion, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* Does a suggest query.
|
||||
*
|
||||
* @param query the Query containing the suggest definition. Must be currently a
|
||||
* {@link org.springframework.data.elasticsearch.core.query.NativeSearchQuery}, must not be {@literal null}.
|
||||
* @param entityType the type of the entities that might be returned for a completion suggestion, must not be
|
||||
* {@literal null}.
|
||||
* @return suggest data
|
||||
* @since 4.3
|
||||
*/
|
||||
Mono<Suggest> suggest(Query query, Class<?> entityType);
|
||||
|
||||
/**
|
||||
* Does a suggest query.
|
||||
*
|
||||
* @param query the Query containing the suggest definition. Must be currently a
|
||||
* {@link org.springframework.data.elasticsearch.core.query.NativeSearchQuery}, must not be {@literal null}.
|
||||
* @param entityType the type of the entities that might be returned for a completion suggestion, must not be
|
||||
* {@literal null}.
|
||||
* @param index the index to run the query against, must not be {@literal null}.
|
||||
* @return suggest data
|
||||
* @since 4.3
|
||||
*/
|
||||
Mono<Suggest> suggest(Query query, Class<?> entityType, IndexCoordinates index);
|
||||
|
||||
// region helper
|
||||
/**
|
||||
* Creates a {@link Query} to find all documents. Must be implemented by the concrete implementations to provide an
|
||||
* appropriate query using the respective client.
|
||||
*
|
||||
* @return a query to find all documents
|
||||
* @since 4.3
|
||||
*/
|
||||
Query matchAllQuery();
|
||||
|
||||
/**
|
||||
* Creates a {@link Query} to find get all documents with given ids. Must be implemented by the concrete
|
||||
* implementations to provide an appropriate query using the respective client.
|
||||
*
|
||||
* @param ids the list of ids must not be {@literal null}
|
||||
* @return query returning the documents with the given ids
|
||||
* @since 4.3
|
||||
*/
|
||||
Query idsQuery(List<String> ids);
|
||||
// endregion
|
||||
}
|
||||
|
||||
@@ -18,14 +18,17 @@ package org.springframework.data.elasticsearch.core;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.*;
|
||||
import static org.springframework.util.CollectionUtils.*;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.elasticsearch.action.DocWriteRequest;
|
||||
import org.elasticsearch.action.admin.indices.alias.Alias;
|
||||
@@ -51,6 +54,7 @@ import org.elasticsearch.action.index.IndexRequest;
|
||||
import org.elasticsearch.action.index.IndexRequestBuilder;
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.search.SearchRequestBuilder;
|
||||
import org.elasticsearch.action.search.SearchType;
|
||||
import org.elasticsearch.action.support.ActiveShardCount;
|
||||
import org.elasticsearch.action.support.WriteRequest;
|
||||
import org.elasticsearch.action.update.UpdateRequest;
|
||||
@@ -66,7 +70,7 @@ import org.elasticsearch.client.indices.PutIndexTemplateRequest;
|
||||
import org.elasticsearch.client.indices.PutMappingRequest;
|
||||
import org.elasticsearch.common.geo.GeoDistance;
|
||||
import org.elasticsearch.common.unit.DistanceUnit;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.core.TimeValue;
|
||||
import org.elasticsearch.index.VersionType;
|
||||
import org.elasticsearch.index.query.MoreLikeThisQueryBuilder;
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
@@ -78,7 +82,6 @@ import org.elasticsearch.index.reindex.UpdateByQueryAction;
|
||||
import org.elasticsearch.index.reindex.UpdateByQueryRequest;
|
||||
import org.elasticsearch.index.reindex.UpdateByQueryRequestBuilder;
|
||||
import org.elasticsearch.script.Script;
|
||||
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
|
||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
|
||||
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
|
||||
@@ -136,37 +139,6 @@ class RequestFactory {
|
||||
}
|
||||
|
||||
// region alias
|
||||
@Deprecated
|
||||
public IndicesAliasesRequest.AliasActions aliasAction(AliasQuery query, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(index, "No index defined for Alias");
|
||||
Assert.notNull(query.getAliasName(), "No alias defined");
|
||||
|
||||
String[] indexNames = index.getIndexNames();
|
||||
IndicesAliasesRequest.AliasActions aliasAction = IndicesAliasesRequest.AliasActions.add()
|
||||
.alias(query.getAliasName()).indices(indexNames);
|
||||
|
||||
if (query.getFilterBuilder() != null) {
|
||||
aliasAction.filter(query.getFilterBuilder());
|
||||
} else if (query.getFilter() != null) {
|
||||
aliasAction.filter(query.getFilter());
|
||||
}
|
||||
|
||||
if (!StringUtils.isEmpty(query.getRouting())) {
|
||||
aliasAction.routing(query.getRouting());
|
||||
}
|
||||
|
||||
if (!StringUtils.isEmpty(query.getSearchRouting())) {
|
||||
aliasAction.searchRouting(query.getSearchRouting());
|
||||
}
|
||||
|
||||
if (!StringUtils.isEmpty(query.getIndexRouting())) {
|
||||
aliasAction.indexRouting(query.getIndexRouting());
|
||||
}
|
||||
|
||||
return aliasAction;
|
||||
}
|
||||
|
||||
public GetAliasesRequest getAliasesRequest(IndexCoordinates index) {
|
||||
|
||||
String[] indexNames = index.getIndexNames();
|
||||
@@ -182,14 +154,6 @@ class RequestFactory {
|
||||
return getAliasesRequest;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public IndicesAliasesRequest indicesAddAliasesRequest(AliasQuery query, IndexCoordinates index) {
|
||||
IndicesAliasesRequest.AliasActions aliasAction = aliasAction(query, index);
|
||||
IndicesAliasesRequest request = new IndicesAliasesRequest();
|
||||
request.addAliasAction(aliasAction);
|
||||
return request;
|
||||
}
|
||||
|
||||
public IndicesAliasesRequest indicesAliasesRequest(AliasActions aliasActions) {
|
||||
|
||||
IndicesAliasesRequest request = new IndicesAliasesRequest();
|
||||
@@ -257,27 +221,6 @@ class RequestFactory {
|
||||
indicesAliasesRequest(aliasActions).getAliasActions().forEach(requestBuilder::addAliasAction);
|
||||
return requestBuilder;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public IndicesAliasesRequest indicesRemoveAliasesRequest(AliasQuery query, IndexCoordinates index) {
|
||||
|
||||
String[] indexNames = index.getIndexNames();
|
||||
IndicesAliasesRequest.AliasActions aliasAction = IndicesAliasesRequest.AliasActions.remove() //
|
||||
.indices(indexNames) //
|
||||
.alias(query.getAliasName());
|
||||
|
||||
return Requests.indexAliasesRequest() //
|
||||
.addAliasAction(aliasAction);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
IndicesAliasesRequestBuilder indicesRemoveAliasesRequestBuilder(Client client, AliasQuery query,
|
||||
IndexCoordinates index) {
|
||||
|
||||
String indexName = index.getIndexName();
|
||||
return client.admin().indices().prepareAliases().removeAlias(indexName, query.getAliasName());
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region bulk
|
||||
@@ -285,15 +228,15 @@ class RequestFactory {
|
||||
BulkRequest bulkRequest = new BulkRequest();
|
||||
|
||||
if (bulkOptions.getTimeout() != null) {
|
||||
bulkRequest.timeout(bulkOptions.getTimeout());
|
||||
bulkRequest.timeout(TimeValue.timeValueMillis(bulkOptions.getTimeout().toMillis()));
|
||||
}
|
||||
|
||||
if (bulkOptions.getRefreshPolicy() != null) {
|
||||
bulkRequest.setRefreshPolicy(bulkOptions.getRefreshPolicy());
|
||||
bulkRequest.setRefreshPolicy(toElasticsearchRefreshPolicy(bulkOptions.getRefreshPolicy()));
|
||||
}
|
||||
|
||||
if (bulkOptions.getWaitForActiveShards() != null) {
|
||||
bulkRequest.waitForActiveShards(bulkOptions.getWaitForActiveShards());
|
||||
bulkRequest.waitForActiveShards(ActiveShardCount.from(bulkOptions.getWaitForActiveShards().getValue()));
|
||||
}
|
||||
|
||||
if (bulkOptions.getPipeline() != null) {
|
||||
@@ -320,15 +263,15 @@ class RequestFactory {
|
||||
BulkRequestBuilder bulkRequestBuilder = client.prepareBulk();
|
||||
|
||||
if (bulkOptions.getTimeout() != null) {
|
||||
bulkRequestBuilder.setTimeout(bulkOptions.getTimeout());
|
||||
bulkRequestBuilder.setTimeout(TimeValue.timeValueMillis(bulkOptions.getTimeout().toMillis()));
|
||||
}
|
||||
|
||||
if (bulkOptions.getRefreshPolicy() != null) {
|
||||
bulkRequestBuilder.setRefreshPolicy(bulkOptions.getRefreshPolicy());
|
||||
bulkRequestBuilder.setRefreshPolicy(toElasticsearchRefreshPolicy(bulkOptions.getRefreshPolicy()));
|
||||
}
|
||||
|
||||
if (bulkOptions.getWaitForActiveShards() != null) {
|
||||
bulkRequestBuilder.setWaitForActiveShards(bulkOptions.getWaitForActiveShards());
|
||||
bulkRequestBuilder.setWaitForActiveShards(ActiveShardCount.from(bulkOptions.getWaitForActiveShards().getValue()));
|
||||
}
|
||||
|
||||
if (bulkOptions.getPipeline() != null) {
|
||||
@@ -355,7 +298,8 @@ class RequestFactory {
|
||||
|
||||
// region index management
|
||||
|
||||
public CreateIndexRequest createIndexRequest(IndexCoordinates index, Map<String, Object> settings, @Nullable Document mapping) {
|
||||
public CreateIndexRequest createIndexRequest(IndexCoordinates index, Map<String, Object> settings,
|
||||
@Nullable Document mapping) {
|
||||
|
||||
Assert.notNull(index, "index must not be null");
|
||||
Assert.notNull(settings, "settings must not be null");
|
||||
@@ -733,15 +677,16 @@ class RequestFactory {
|
||||
|
||||
FetchSourceContext fetchSourceContext = getFetchSourceContext(searchQuery);
|
||||
|
||||
if (!isEmpty(searchQuery.getIds())) {
|
||||
if (!isEmpty(searchQuery.getIdsWithRouting())) {
|
||||
String indexName = index.getIndexName();
|
||||
for (String id : searchQuery.getIds()) {
|
||||
MultiGetRequest.Item item = new MultiGetRequest.Item(indexName, id);
|
||||
|
||||
if (searchQuery.getRoute() != null) {
|
||||
item = item.routing(searchQuery.getRoute());
|
||||
for (Query.IdWithRouting idWithRouting : searchQuery.getIdsWithRouting()) {
|
||||
MultiGetRequest.Item item = new MultiGetRequest.Item(indexName, idWithRouting.getId());
|
||||
if (idWithRouting.getRouting() != null) {
|
||||
item = item.routing(idWithRouting.getRouting());
|
||||
}
|
||||
|
||||
// note: multiGet does not have fields, need to set sourceContext to filter
|
||||
if (fetchSourceContext != null) {
|
||||
item.fetchSourceContext(fetchSourceContext);
|
||||
}
|
||||
@@ -760,15 +705,17 @@ class RequestFactory {
|
||||
String indexName = index.getIndexName();
|
||||
IndexRequest indexRequest;
|
||||
|
||||
if (query.getObject() != null) {
|
||||
String id = StringUtils.isEmpty(query.getId()) ? getPersistentEntityId(query.getObject()) : query.getId();
|
||||
Object queryObject = query.getObject();
|
||||
|
||||
if (queryObject != null) {
|
||||
String id = StringUtils.isEmpty(query.getId()) ? getPersistentEntityId(queryObject) : query.getId();
|
||||
// If we have a query id and a document id, do not ask ES to generate one.
|
||||
if (id != null) {
|
||||
indexRequest = new IndexRequest(indexName).id(id);
|
||||
} else {
|
||||
indexRequest = new IndexRequest(indexName);
|
||||
}
|
||||
indexRequest.source(elasticsearchConverter.mapObject(query.getObject()).toJson(), Requests.INDEX_CONTENT_TYPE);
|
||||
indexRequest.source(elasticsearchConverter.mapObject(queryObject).toJson(), Requests.INDEX_CONTENT_TYPE);
|
||||
} else if (query.getSource() != null) {
|
||||
indexRequest = new IndexRequest(indexName).id(query.getId()).source(query.getSource(),
|
||||
Requests.INDEX_CONTENT_TYPE);
|
||||
@@ -779,7 +726,8 @@ class RequestFactory {
|
||||
|
||||
if (query.getVersion() != null) {
|
||||
indexRequest.version(query.getVersion());
|
||||
VersionType versionType = retrieveVersionTypeFromPersistentEntity(query.getObject().getClass());
|
||||
VersionType versionType = retrieveVersionTypeFromPersistentEntity(
|
||||
queryObject != null ? queryObject.getClass() : null);
|
||||
indexRequest.versionType(versionType);
|
||||
}
|
||||
|
||||
@@ -814,15 +762,16 @@ class RequestFactory {
|
||||
|
||||
IndexRequestBuilder indexRequestBuilder;
|
||||
|
||||
if (query.getObject() != null) {
|
||||
String id = StringUtils.isEmpty(query.getId()) ? getPersistentEntityId(query.getObject()) : query.getId();
|
||||
Object queryObject = query.getObject();
|
||||
if (queryObject != null) {
|
||||
String id = StringUtils.isEmpty(query.getId()) ? getPersistentEntityId(queryObject) : query.getId();
|
||||
// If we have a query id and a document id, do not ask ES to generate one.
|
||||
if (id != null) {
|
||||
indexRequestBuilder = client.prepareIndex(indexName, type, id);
|
||||
} else {
|
||||
indexRequestBuilder = client.prepareIndex(indexName, type);
|
||||
}
|
||||
indexRequestBuilder.setSource(elasticsearchConverter.mapObject(query.getObject()).toJson(),
|
||||
indexRequestBuilder.setSource(elasticsearchConverter.mapObject(queryObject).toJson(),
|
||||
Requests.INDEX_CONTENT_TYPE);
|
||||
} else if (query.getSource() != null) {
|
||||
indexRequestBuilder = client.prepareIndex(indexName, type, query.getId()).setSource(query.getSource(),
|
||||
@@ -834,7 +783,8 @@ class RequestFactory {
|
||||
|
||||
if (query.getVersion() != null) {
|
||||
indexRequestBuilder.setVersion(query.getVersion());
|
||||
VersionType versionType = retrieveVersionTypeFromPersistentEntity(query.getObject().getClass());
|
||||
VersionType versionType = retrieveVersionTypeFromPersistentEntity(
|
||||
queryObject != null ? queryObject.getClass() : null);
|
||||
indexRequestBuilder.setVersionType(versionType);
|
||||
}
|
||||
|
||||
@@ -857,14 +807,18 @@ class RequestFactory {
|
||||
// region search
|
||||
@Nullable
|
||||
public HighlightBuilder highlightBuilder(Query query) {
|
||||
HighlightBuilder highlightBuilder = query.getHighlightQuery().map(HighlightQuery::getHighlightBuilder).orElse(null);
|
||||
HighlightBuilder highlightBuilder = query.getHighlightQuery()
|
||||
.map(highlightQuery -> new HighlightQueryBuilder(elasticsearchConverter.getMappingContext())
|
||||
.getHighlightBuilder(highlightQuery.getHighlight(), highlightQuery.getType()))
|
||||
.orElse(null);
|
||||
|
||||
if (highlightBuilder == null) {
|
||||
|
||||
if (query instanceof NativeSearchQuery) {
|
||||
NativeSearchQuery searchQuery = (NativeSearchQuery) query;
|
||||
|
||||
if (searchQuery.getHighlightFields() != null || searchQuery.getHighlightBuilder() != null) {
|
||||
if ((searchQuery.getHighlightFields() != null && searchQuery.getHighlightFields().length > 0)
|
||||
|| searchQuery.getHighlightBuilder() != null) {
|
||||
highlightBuilder = searchQuery.getHighlightBuilder();
|
||||
|
||||
if (highlightBuilder == null) {
|
||||
@@ -990,11 +944,6 @@ class RequestFactory {
|
||||
sourceBuilder.seqNoAndPrimaryTerm(true);
|
||||
}
|
||||
|
||||
if (query.getSourceFilter() != null) {
|
||||
SourceFilter sourceFilter = query.getSourceFilter();
|
||||
sourceBuilder.fetchSource(sourceFilter.getIncludes(), sourceFilter.getExcludes());
|
||||
}
|
||||
|
||||
if (query.getPageable().isPaged()) {
|
||||
sourceBuilder.from((int) query.getPageable().getOffset());
|
||||
sourceBuilder.size(query.getPageable().getPageSize());
|
||||
@@ -1003,12 +952,18 @@ class RequestFactory {
|
||||
sourceBuilder.size(INDEX_MAX_RESULT_WINDOW);
|
||||
}
|
||||
|
||||
if (query.getSourceFilter() != null) {
|
||||
sourceBuilder.fetchSource(getFetchSourceContext(query));
|
||||
SourceFilter sourceFilter = query.getSourceFilter();
|
||||
sourceBuilder.fetchSource(sourceFilter.getIncludes(), sourceFilter.getExcludes());
|
||||
}
|
||||
|
||||
if (!query.getFields().isEmpty()) {
|
||||
sourceBuilder.fetchSource(query.getFields().toArray(new String[0]), null);
|
||||
query.getFields().forEach(sourceBuilder::fetchField);
|
||||
}
|
||||
|
||||
if (query.getIndicesOptions() != null) {
|
||||
request.indicesOptions(query.getIndicesOptions());
|
||||
request.indicesOptions(toElasticsearchIndicesOptions(query.getIndicesOptions()));
|
||||
}
|
||||
|
||||
if (query.isLimiting()) {
|
||||
@@ -1024,7 +979,7 @@ class RequestFactory {
|
||||
request.preference(query.getPreference());
|
||||
}
|
||||
|
||||
request.searchType(query.getSearchType());
|
||||
request.searchType(SearchType.fromString(query.getSearchType().name().toLowerCase()));
|
||||
|
||||
prepareSort(query, sourceBuilder, getPersistentEntity(clazz));
|
||||
|
||||
@@ -1048,9 +1003,9 @@ class RequestFactory {
|
||||
request.routing(query.getRoute());
|
||||
}
|
||||
|
||||
TimeValue timeout = query.getTimeout();
|
||||
Duration timeout = query.getTimeout();
|
||||
if (timeout != null) {
|
||||
sourceBuilder.timeout(timeout);
|
||||
sourceBuilder.timeout(new TimeValue(timeout.toMillis()));
|
||||
}
|
||||
|
||||
sourceBuilder.explain(query.getExplain());
|
||||
@@ -1061,7 +1016,25 @@ class RequestFactory {
|
||||
|
||||
query.getRescorerQueries().forEach(rescorer -> sourceBuilder.addRescorer(getQueryRescorerBuilder(rescorer)));
|
||||
|
||||
if (query.getRequestCache() != null) {
|
||||
request.requestCache(query.getRequestCache());
|
||||
}
|
||||
|
||||
if (!query.getRuntimeFields().isEmpty()) {
|
||||
|
||||
Map<String, Object> runtimeMappings = new HashMap<>();
|
||||
query.getRuntimeFields().forEach(runtimeField -> {
|
||||
runtimeMappings.put(runtimeField.getName(), runtimeField.getMapping());
|
||||
});
|
||||
sourceBuilder.runtimeMappings(runtimeMappings);
|
||||
}
|
||||
|
||||
if (query.getScrollTime() != null) {
|
||||
request.scroll(TimeValue.timeValueMillis(query.getScrollTime().toMillis()));
|
||||
}
|
||||
|
||||
request.source(sourceBuilder);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
@@ -1073,18 +1046,13 @@ class RequestFactory {
|
||||
Assert.notEmpty(indexNames, "No index defined for Query");
|
||||
|
||||
SearchRequestBuilder searchRequestBuilder = client.prepareSearch(indexNames) //
|
||||
.setSearchType(query.getSearchType()) //
|
||||
.setSearchType(SearchType.fromString(query.getSearchType().name().toLowerCase())) //
|
||||
.setVersion(true) //
|
||||
.setTrackScores(query.getTrackScores());
|
||||
if (hasSeqNoPrimaryTermProperty(clazz)) {
|
||||
searchRequestBuilder.seqNoAndPrimaryTerm(true);
|
||||
}
|
||||
|
||||
if (query.getSourceFilter() != null) {
|
||||
SourceFilter sourceFilter = query.getSourceFilter();
|
||||
searchRequestBuilder.setFetchSource(sourceFilter.getIncludes(), sourceFilter.getExcludes());
|
||||
}
|
||||
|
||||
if (query.getPageable().isPaged()) {
|
||||
searchRequestBuilder.setFrom((int) query.getPageable().getOffset());
|
||||
searchRequestBuilder.setSize(query.getPageable().getPageSize());
|
||||
@@ -1093,12 +1061,17 @@ class RequestFactory {
|
||||
searchRequestBuilder.setSize(INDEX_MAX_RESULT_WINDOW);
|
||||
}
|
||||
|
||||
if (query.getSourceFilter() != null) {
|
||||
SourceFilter sourceFilter = query.getSourceFilter();
|
||||
searchRequestBuilder.setFetchSource(sourceFilter.getIncludes(), sourceFilter.getExcludes());
|
||||
}
|
||||
|
||||
if (!query.getFields().isEmpty()) {
|
||||
searchRequestBuilder.setFetchSource(query.getFields().toArray(new String[0]), null);
|
||||
query.getFields().forEach(searchRequestBuilder::addFetchField);
|
||||
}
|
||||
|
||||
if (query.getIndicesOptions() != null) {
|
||||
searchRequestBuilder.setIndicesOptions(query.getIndicesOptions());
|
||||
searchRequestBuilder.setIndicesOptions(toElasticsearchIndicesOptions(query.getIndicesOptions()));
|
||||
}
|
||||
|
||||
if (query.isLimiting()) {
|
||||
@@ -1136,9 +1109,9 @@ class RequestFactory {
|
||||
searchRequestBuilder.setRouting(query.getRoute());
|
||||
}
|
||||
|
||||
TimeValue timeout = query.getTimeout();
|
||||
Duration timeout = query.getTimeout();
|
||||
if (timeout != null) {
|
||||
searchRequestBuilder.setTimeout(timeout);
|
||||
searchRequestBuilder.setTimeout(new TimeValue(timeout.toMillis()));
|
||||
}
|
||||
|
||||
searchRequestBuilder.setExplain(query.getExplain());
|
||||
@@ -1149,6 +1122,23 @@ class RequestFactory {
|
||||
|
||||
query.getRescorerQueries().forEach(rescorer -> searchRequestBuilder.addRescorer(getQueryRescorerBuilder(rescorer)));
|
||||
|
||||
if (query.getRequestCache() != null) {
|
||||
searchRequestBuilder.setRequestCache(query.getRequestCache());
|
||||
}
|
||||
|
||||
if (!query.getRuntimeFields().isEmpty()) {
|
||||
|
||||
Map<String, Object> runtimeMappings = new HashMap<>();
|
||||
query.getRuntimeFields().forEach(runtimeField -> {
|
||||
runtimeMappings.put(runtimeField.getName(), runtimeField.getMapping());
|
||||
});
|
||||
searchRequestBuilder.setRuntimeMappings(runtimeMappings);
|
||||
}
|
||||
|
||||
if (query.getScrollTime() != null) {
|
||||
searchRequestBuilder.setScroll(TimeValue.timeValueMillis(query.getScrollTime().toMillis()));
|
||||
}
|
||||
|
||||
return searchRequestBuilder;
|
||||
}
|
||||
|
||||
@@ -1171,11 +1161,16 @@ class RequestFactory {
|
||||
}
|
||||
|
||||
if (!isEmpty(query.getAggregations())) {
|
||||
for (AbstractAggregationBuilder<?> aggregationBuilder : query.getAggregations()) {
|
||||
sourceBuilder.aggregation(aggregationBuilder);
|
||||
}
|
||||
query.getAggregations().forEach(sourceBuilder::aggregation);
|
||||
}
|
||||
|
||||
if (!isEmpty(query.getPipelineAggregations())) {
|
||||
query.getPipelineAggregations().forEach(sourceBuilder::aggregation);
|
||||
}
|
||||
|
||||
if (query.getSuggestBuilder() != null) {
|
||||
sourceBuilder.suggest(query.getSuggestBuilder());
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareNativeSearch(SearchRequestBuilder searchRequestBuilder, NativeSearchQuery nativeSearchQuery) {
|
||||
@@ -1196,9 +1191,15 @@ class RequestFactory {
|
||||
}
|
||||
|
||||
if (!isEmpty(nativeSearchQuery.getAggregations())) {
|
||||
for (AbstractAggregationBuilder<?> aggregationBuilder : nativeSearchQuery.getAggregations()) {
|
||||
searchRequestBuilder.addAggregation(aggregationBuilder);
|
||||
}
|
||||
nativeSearchQuery.getAggregations().forEach(searchRequestBuilder::addAggregation);
|
||||
}
|
||||
|
||||
if (!isEmpty(nativeSearchQuery.getPipelineAggregations())) {
|
||||
nativeSearchQuery.getPipelineAggregations().forEach(searchRequestBuilder::addAggregation);
|
||||
}
|
||||
|
||||
if (nativeSearchQuery.getSuggestBuilder() != null) {
|
||||
searchRequestBuilder.suggest(nativeSearchQuery.getSuggestBuilder());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1238,6 +1239,15 @@ class RequestFactory {
|
||||
private SortBuilder<?> getSortBuilder(Sort.Order order, @Nullable ElasticsearchPersistentEntity<?> entity) {
|
||||
SortOrder sortOrder = order.getDirection().isDescending() ? SortOrder.DESC : SortOrder.ASC;
|
||||
|
||||
Order.Mode mode = Order.DEFAULT_MODE;
|
||||
String unmappedType = null;
|
||||
|
||||
if (order instanceof Order) {
|
||||
Order o = (Order) order;
|
||||
mode = o.getMode();
|
||||
unmappedType = o.getUnmappedType();
|
||||
}
|
||||
|
||||
if (ScoreSortBuilder.NAME.equals(order.getProperty())) {
|
||||
return SortBuilders //
|
||||
.scoreSort() //
|
||||
@@ -1255,14 +1265,23 @@ class RequestFactory {
|
||||
geoDistanceOrder.getGeoPoint().getLon());
|
||||
|
||||
sort.geoDistance(GeoDistance.fromString(geoDistanceOrder.getDistanceType().name()));
|
||||
sort.ignoreUnmapped(geoDistanceOrder.getIgnoreUnmapped());
|
||||
sort.sortMode(SortMode.fromString(geoDistanceOrder.getMode().name()));
|
||||
sort.sortMode(SortMode.fromString(mode.name()));
|
||||
sort.unit(DistanceUnit.fromString(geoDistanceOrder.getUnit()));
|
||||
|
||||
if (geoDistanceOrder.getIgnoreUnmapped() != GeoDistanceOrder.DEFAULT_IGNORE_UNMAPPED) {
|
||||
sort.ignoreUnmapped(geoDistanceOrder.getIgnoreUnmapped());
|
||||
}
|
||||
|
||||
return sort;
|
||||
} else {
|
||||
FieldSortBuilder sort = SortBuilders //
|
||||
.fieldSort(fieldName) //
|
||||
.order(sortOrder);
|
||||
.order(sortOrder) //
|
||||
.sortMode(SortMode.fromString(mode.name()));
|
||||
|
||||
if (unmappedType != null) {
|
||||
sort.unmappedType(unmappedType);
|
||||
}
|
||||
|
||||
if (order.getNullHandling() == Sort.NullHandling.NULLS_FIRST) {
|
||||
sort.missing("_first");
|
||||
@@ -1276,10 +1295,10 @@ class RequestFactory {
|
||||
|
||||
private QueryRescorerBuilder getQueryRescorerBuilder(RescorerQuery rescorerQuery) {
|
||||
|
||||
QueryBuilder queryBuilder = getQuery(rescorerQuery.getQuery());
|
||||
Assert.notNull("queryBuilder", "Could not build query for rescorerQuery");
|
||||
QueryBuilder queryBuilder = getQuery(rescorerQuery.getQuery());
|
||||
Assert.notNull("queryBuilder", "Could not build query for rescorerQuery");
|
||||
|
||||
QueryRescorerBuilder builder = new QueryRescorerBuilder(queryBuilder);
|
||||
QueryRescorerBuilder builder = new QueryRescorerBuilder(queryBuilder);
|
||||
|
||||
if (rescorerQuery.getScoreMode() != ScoreMode.Default) {
|
||||
builder.setScoreMode(QueryRescoreMode.valueOf(rescorerQuery.getScoreMode().name()));
|
||||
@@ -1453,8 +1472,7 @@ class RequestFactory {
|
||||
|
||||
public UpdateByQueryRequest updateByQueryRequest(UpdateQuery query, IndexCoordinates index) {
|
||||
|
||||
String indexName = index.getIndexName();
|
||||
final UpdateByQueryRequest updateByQueryRequest = new UpdateByQueryRequest(indexName);
|
||||
final UpdateByQueryRequest updateByQueryRequest = new UpdateByQueryRequest(index.getIndexNames());
|
||||
updateByQueryRequest.setScript(getScript(query));
|
||||
|
||||
if (query.getAbortOnVersionConflict() != null) {
|
||||
@@ -1471,7 +1489,7 @@ class RequestFactory {
|
||||
updateByQueryRequest.setQuery(getQuery(queryQuery));
|
||||
|
||||
if (queryQuery.getIndicesOptions() != null) {
|
||||
updateByQueryRequest.setIndicesOptions(queryQuery.getIndicesOptions());
|
||||
updateByQueryRequest.setIndicesOptions(toElasticsearchIndicesOptions(queryQuery.getIndicesOptions()));
|
||||
}
|
||||
|
||||
if (queryQuery.getScrollTime() != null) {
|
||||
@@ -1546,7 +1564,8 @@ class RequestFactory {
|
||||
updateByQueryRequestBuilder.filter(getQuery(queryQuery));
|
||||
|
||||
if (queryQuery.getIndicesOptions() != null) {
|
||||
updateByQueryRequestBuilder.source().setIndicesOptions(queryQuery.getIndicesOptions());
|
||||
updateByQueryRequestBuilder.source()
|
||||
.setIndicesOptions(toElasticsearchIndicesOptions(queryQuery.getIndicesOptions()));
|
||||
}
|
||||
|
||||
if (queryQuery.getScrollTime() != null) {
|
||||
@@ -1648,26 +1667,33 @@ class RequestFactory {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private FetchSourceContext getFetchSourceContext(Query searchQuery) {
|
||||
FetchSourceContext fetchSourceContext = null;
|
||||
|
||||
SourceFilter sourceFilter = searchQuery.getSourceFilter();
|
||||
|
||||
if (!isEmpty(searchQuery.getFields())) {
|
||||
if (sourceFilter == null) {
|
||||
sourceFilter = new FetchSourceFilter(toArray(searchQuery.getFields()), null);
|
||||
} else {
|
||||
ArrayList<String> arrayList = new ArrayList<>();
|
||||
Collections.addAll(arrayList, sourceFilter.getIncludes());
|
||||
sourceFilter = new FetchSourceFilter(toArray(arrayList), null);
|
||||
}
|
||||
|
||||
fetchSourceContext = new FetchSourceContext(true, sourceFilter.getIncludes(), sourceFilter.getExcludes());
|
||||
} else if (sourceFilter != null) {
|
||||
fetchSourceContext = new FetchSourceContext(true, sourceFilter.getIncludes(), sourceFilter.getExcludes());
|
||||
if (sourceFilter != null) {
|
||||
return new FetchSourceContext(true, sourceFilter.getIncludes(), sourceFilter.getExcludes());
|
||||
}
|
||||
return fetchSourceContext;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public org.elasticsearch.action.support.IndicesOptions toElasticsearchIndicesOptions(IndicesOptions indicesOptions) {
|
||||
|
||||
Assert.notNull(indicesOptions, "indicesOptions must not be null");
|
||||
|
||||
Set<org.elasticsearch.action.support.IndicesOptions.Option> options = indicesOptions.getOptions().stream()
|
||||
.map(it -> org.elasticsearch.action.support.IndicesOptions.Option.valueOf(it.name().toUpperCase()))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
Set<org.elasticsearch.action.support.IndicesOptions.WildcardStates> wildcardStates = indicesOptions
|
||||
.getExpandWildcards().stream()
|
||||
.map(it -> org.elasticsearch.action.support.IndicesOptions.WildcardStates.valueOf(it.name().toUpperCase()))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
return new org.elasticsearch.action.support.IndicesOptions(EnumSet.copyOf(options), EnumSet.copyOf(wildcardStates));
|
||||
}
|
||||
// endregion
|
||||
|
||||
@Nullable
|
||||
@@ -1695,17 +1721,23 @@ class RequestFactory {
|
||||
return null;
|
||||
}
|
||||
|
||||
private VersionType retrieveVersionTypeFromPersistentEntity(Class<?> clazz) {
|
||||
private VersionType retrieveVersionTypeFromPersistentEntity(@Nullable Class<?> clazz) {
|
||||
|
||||
MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext = elasticsearchConverter
|
||||
.getMappingContext();
|
||||
|
||||
ElasticsearchPersistentEntity<?> persistentEntity = mappingContext.getPersistentEntity(clazz);
|
||||
ElasticsearchPersistentEntity<?> persistentEntity = clazz != null ? mappingContext.getPersistentEntity(clazz)
|
||||
: null;
|
||||
|
||||
VersionType versionType = null;
|
||||
|
||||
if (persistentEntity != null) {
|
||||
versionType = persistentEntity.getVersionType();
|
||||
org.springframework.data.elasticsearch.annotations.Document.VersionType entityVersionType = persistentEntity
|
||||
.getVersionType();
|
||||
|
||||
if (entityVersionType != null) {
|
||||
versionType = VersionType.fromString(entityVersionType.name().toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
return versionType != null ? versionType : VersionType.EXTERNAL;
|
||||
|
||||
@@ -28,6 +28,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
|
||||
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
|
||||
import org.elasticsearch.action.bulk.BulkItemResponse;
|
||||
import org.elasticsearch.action.get.MultiGetItemResponse;
|
||||
import org.elasticsearch.action.get.MultiGetResponse;
|
||||
import org.elasticsearch.client.indices.GetIndexResponse;
|
||||
@@ -37,11 +38,14 @@ import org.elasticsearch.cluster.metadata.AliasMetadata;
|
||||
import org.elasticsearch.cluster.metadata.MappingMetadata;
|
||||
import org.elasticsearch.common.collect.ImmutableOpenMap;
|
||||
import org.elasticsearch.common.compress.CompressedXContent;
|
||||
import org.elasticsearch.index.reindex.BulkByScrollResponse;
|
||||
import org.elasticsearch.index.reindex.ScrollableHitSource;
|
||||
import org.springframework.data.elasticsearch.core.cluster.ClusterHealth;
|
||||
import org.springframework.data.elasticsearch.core.document.Document;
|
||||
import org.springframework.data.elasticsearch.core.index.AliasData;
|
||||
import org.springframework.data.elasticsearch.core.index.Settings;
|
||||
import org.springframework.data.elasticsearch.core.index.TemplateData;
|
||||
import org.springframework.data.elasticsearch.core.query.ByQueryResponse;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -313,4 +317,71 @@ public class ResponseConverter {
|
||||
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region byQueryResponse
|
||||
public static ByQueryResponse byQueryResponseOf(BulkByScrollResponse bulkByScrollResponse) {
|
||||
final List<ByQueryResponse.Failure> failures = bulkByScrollResponse.getBulkFailures() //
|
||||
.stream() //
|
||||
.map(ResponseConverter::byQueryResponseFailureOf) //
|
||||
.collect(Collectors.toList()); //
|
||||
|
||||
final List<ByQueryResponse.SearchFailure> searchFailures = bulkByScrollResponse.getSearchFailures() //
|
||||
.stream() //
|
||||
.map(ResponseConverter::byQueryResponseSearchFailureOf) //
|
||||
.collect(Collectors.toList());//
|
||||
|
||||
return ByQueryResponse.builder() //
|
||||
.withTook(bulkByScrollResponse.getTook().getMillis()) //
|
||||
.withTimedOut(bulkByScrollResponse.isTimedOut()) //
|
||||
.withTotal(bulkByScrollResponse.getTotal()) //
|
||||
.withUpdated(bulkByScrollResponse.getUpdated()) //
|
||||
.withDeleted(bulkByScrollResponse.getDeleted()) //
|
||||
.withBatches(bulkByScrollResponse.getBatches()) //
|
||||
.withVersionConflicts(bulkByScrollResponse.getVersionConflicts()) //
|
||||
.withNoops(bulkByScrollResponse.getNoops()) //
|
||||
.withBulkRetries(bulkByScrollResponse.getBulkRetries()) //
|
||||
.withSearchRetries(bulkByScrollResponse.getSearchRetries()) //
|
||||
.withReasonCancelled(bulkByScrollResponse.getReasonCancelled()) //
|
||||
.withFailures(failures) //
|
||||
.withSearchFailure(searchFailures) //
|
||||
.build(); //
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ByQueryResponse.Failure} from {@link BulkItemResponse.Failure}
|
||||
*
|
||||
* @param failure {@link BulkItemResponse.Failure} to translate
|
||||
* @return a new {@link ByQueryResponse.Failure}
|
||||
*/
|
||||
public static ByQueryResponse.Failure byQueryResponseFailureOf(BulkItemResponse.Failure failure) {
|
||||
return ByQueryResponse.Failure.builder() //
|
||||
.withIndex(failure.getIndex()) //
|
||||
.withType(failure.getType()) //
|
||||
.withId(failure.getId()) //
|
||||
.withStatus(failure.getStatus().getStatus()) //
|
||||
.withAborted(failure.isAborted()) //
|
||||
.withCause(failure.getCause()) //
|
||||
.withSeqNo(failure.getSeqNo()) //
|
||||
.withTerm(failure.getTerm()) //
|
||||
.build(); //
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ByQueryResponse.SearchFailure} from {@link ScrollableHitSource.SearchFailure}
|
||||
*
|
||||
* @param searchFailure {@link ScrollableHitSource.SearchFailure} to translate
|
||||
* @return a new {@link ByQueryResponse.SearchFailure}
|
||||
*/
|
||||
public static ByQueryResponse.SearchFailure byQueryResponseSearchFailureOf(
|
||||
ScrollableHitSource.SearchFailure searchFailure) {
|
||||
return ByQueryResponse.SearchFailure.builder() //
|
||||
.withReason(searchFailure.getReason()) //
|
||||
.withIndex(searchFailure.getIndex()) //
|
||||
.withNodeId(searchFailure.getNodeId()) //
|
||||
.withShardId(searchFailure.getShardId()) //
|
||||
.withStatus(searchFailure.getStatus().getStatus()) //
|
||||
.build(); //
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
|
||||
+4
-42
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -28,7 +27,6 @@ import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
|
||||
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest;
|
||||
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
|
||||
import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateRequest;
|
||||
import org.elasticsearch.client.GetAliasesResponse;
|
||||
import org.elasticsearch.client.RequestOptions;
|
||||
import org.elasticsearch.client.indices.CreateIndexRequest;
|
||||
import org.elasticsearch.client.indices.GetIndexRequest;
|
||||
@@ -39,7 +37,6 @@ import org.elasticsearch.client.indices.GetMappingsRequest;
|
||||
import org.elasticsearch.client.indices.IndexTemplatesExistRequest;
|
||||
import org.elasticsearch.client.indices.PutIndexTemplateRequest;
|
||||
import org.elasticsearch.client.indices.PutMappingRequest;
|
||||
import org.elasticsearch.cluster.metadata.AliasMetadata;
|
||||
import org.elasticsearch.cluster.metadata.MappingMetadata;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -53,7 +50,6 @@ import org.springframework.data.elasticsearch.core.index.PutTemplateRequest;
|
||||
import org.springframework.data.elasticsearch.core.index.Settings;
|
||||
import org.springframework.data.elasticsearch.core.index.TemplateData;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.query.AliasQuery;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -65,18 +61,18 @@ import org.springframework.util.Assert;
|
||||
* @author George Popides
|
||||
* @since 4.0
|
||||
*/
|
||||
class DefaultIndexOperations extends AbstractDefaultIndexOperations implements IndexOperations {
|
||||
class RestIndexTemplate extends AbstractIndexTemplate implements IndexOperations {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultIndexOperations.class);
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(RestIndexTemplate.class);
|
||||
|
||||
private final ElasticsearchRestTemplate restTemplate;
|
||||
|
||||
public DefaultIndexOperations(ElasticsearchRestTemplate restTemplate, Class<?> boundClass) {
|
||||
public RestIndexTemplate(ElasticsearchRestTemplate restTemplate, Class<?> boundClass) {
|
||||
super(restTemplate.getElasticsearchConverter(), boundClass);
|
||||
this.restTemplate = restTemplate;
|
||||
}
|
||||
|
||||
public DefaultIndexOperations(ElasticsearchRestTemplate restTemplate, IndexCoordinates boundIndex) {
|
||||
public RestIndexTemplate(ElasticsearchRestTemplate restTemplate, IndexCoordinates boundIndex) {
|
||||
super(restTemplate.getElasticsearchConverter(), boundIndex);
|
||||
this.restTemplate = restTemplate;
|
||||
}
|
||||
@@ -141,40 +137,6 @@ class DefaultIndexOperations extends AbstractDefaultIndexOperations implements I
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
protected boolean doAddAlias(AliasQuery query, IndexCoordinates index) {
|
||||
|
||||
IndicesAliasesRequest request = requestFactory.indicesAddAliasesRequest(query, index);
|
||||
return restTemplate
|
||||
.execute(client -> client.indices().updateAliases(request, RequestOptions.DEFAULT).isAcknowledged());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
protected boolean doRemoveAlias(AliasQuery query, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(index, "No index defined for Alias");
|
||||
Assert.notNull(query.getAliasName(), "No alias defined");
|
||||
|
||||
IndicesAliasesRequest indicesAliasesRequest = requestFactory.indicesRemoveAliasesRequest(query, index);
|
||||
return restTemplate.execute(
|
||||
client -> client.indices().updateAliases(indicesAliasesRequest, RequestOptions.DEFAULT).isAcknowledged());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<AliasMetadata> doQueryForAlias(IndexCoordinates index) {
|
||||
|
||||
GetAliasesRequest getAliasesRequest = requestFactory.getAliasesRequest(index);
|
||||
|
||||
return restTemplate.execute(client -> {
|
||||
GetAliasesResponse alias = client.indices().getAlias(getAliasesRequest, RequestOptions.DEFAULT);
|
||||
// we only return data for the first index name that was requested (always have done so)
|
||||
String index1 = getAliasesRequest.indices()[0];
|
||||
return new ArrayList<>(alias.getAliases().get(index1));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Set<AliasData>> doGetAliases(@Nullable String[] aliasNames, @Nullable String[] indexNames) {
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Defines a runtime field to be added to a Query
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.3
|
||||
*/
|
||||
public class RuntimeField {
|
||||
|
||||
private final String name;
|
||||
private final String type;
|
||||
private final String script;
|
||||
|
||||
public RuntimeField(String name, String type, String script) {
|
||||
|
||||
Assert.notNull(name, "name must not be null");
|
||||
Assert.notNull(type, "type must not be null");
|
||||
Assert.notNull(script, "script must not be null");
|
||||
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.script = script;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the mapping as a Map like it is needed for the Elasticsearch client
|
||||
*/
|
||||
public Map<String, Object> getMapping() {
|
||||
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("type", type);
|
||||
map.put("script", script);
|
||||
return map;
|
||||
}
|
||||
}
|
||||
@@ -22,9 +22,9 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.elasticsearch.search.aggregations.Aggregations;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.document.Document;
|
||||
import org.springframework.data.elasticsearch.core.document.NestedMetaData;
|
||||
@@ -32,6 +32,8 @@ import org.springframework.data.elasticsearch.core.document.SearchDocument;
|
||||
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
|
||||
import org.springframework.data.elasticsearch.core.suggest.response.CompletionSuggestion;
|
||||
import org.springframework.data.elasticsearch.core.suggest.response.Suggest;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -44,6 +46,7 @@ import org.springframework.util.Assert;
|
||||
* @author Mark Paluch
|
||||
* @author Roman Puchkovskiy
|
||||
* @author Matt Gilene
|
||||
* @author Sascha Woo
|
||||
* @since 4.0
|
||||
*/
|
||||
class SearchHitMapping<T> {
|
||||
@@ -95,10 +98,30 @@ class SearchHitMapping<T> {
|
||||
SearchHit<T> hit = mapHit(document, content);
|
||||
searchHits.add(hit);
|
||||
}
|
||||
Aggregations aggregations = searchDocumentResponse.getAggregations();
|
||||
AggregationsContainer<?> aggregations = searchDocumentResponse.getAggregations();
|
||||
TotalHitsRelation totalHitsRelation = TotalHitsRelation.valueOf(searchDocumentResponse.getTotalHitsRelation());
|
||||
|
||||
return new SearchHitsImpl<>(totalHits, totalHitsRelation, maxScore, scrollId, searchHits, aggregations);
|
||||
Suggest suggest = searchDocumentResponse.getSuggest();
|
||||
mapHitsInCompletionSuggestion(suggest);
|
||||
|
||||
return new SearchHitsImpl<>(totalHits, totalHitsRelation, maxScore, scrollId, searchHits, aggregations, suggest);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void mapHitsInCompletionSuggestion(@Nullable Suggest suggest) {
|
||||
if (suggest != null) {
|
||||
for (Suggest.Suggestion<? extends Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option>> suggestion : suggest
|
||||
.getSuggestions()) {
|
||||
if (suggestion instanceof CompletionSuggestion) {
|
||||
CompletionSuggestion<T> completionSuggestion = (CompletionSuggestion<T>) suggestion;
|
||||
for (CompletionSuggestion.Entry<T> entry : completionSuggestion.getEntries()) {
|
||||
for (CompletionSuggestion.Entry.Option<T> option : entry.getOptions()) {
|
||||
option.updateSearchHit(this::mapHit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SearchHit<T> mapHit(SearchDocument searchDocument, T content) {
|
||||
@@ -173,7 +196,7 @@ class SearchHitMapping<T> {
|
||||
*/
|
||||
private SearchHits<?> mapInnerDocuments(SearchHits<SearchDocument> searchHits, Class<T> type) {
|
||||
|
||||
if (searchHits.getTotalHits() == 0) {
|
||||
if (searchHits.isEmpty()) {
|
||||
return searchHits;
|
||||
}
|
||||
|
||||
@@ -214,10 +237,11 @@ class SearchHitMapping<T> {
|
||||
searchHits.getMaxScore(), //
|
||||
scrollId, //
|
||||
convertedSearchHits, //
|
||||
searchHits.getAggregations());
|
||||
searchHits.getAggregations(), //
|
||||
searchHits.getSuggest());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("Could not map inner_hits", e);
|
||||
throw new UncategorizedElasticsearchException("Unable to convert inner hits.", e);
|
||||
}
|
||||
|
||||
return searchHits;
|
||||
@@ -225,7 +249,7 @@ class SearchHitMapping<T> {
|
||||
|
||||
/**
|
||||
* find a {@link ElasticsearchPersistentEntity} following the property chain defined by the nested metadata
|
||||
*
|
||||
*
|
||||
* @param persistentEntity base entity
|
||||
* @param nestedMetaData nested metadata
|
||||
* @return A {@link ElasticsearchPersistentEntityWithNestedMetaData} containing the found entity or null together with
|
||||
|
||||
@@ -18,13 +18,13 @@ package org.springframework.data.elasticsearch.core;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.elasticsearch.search.aggregations.Aggregations;
|
||||
import org.springframework.data.elasticsearch.core.suggest.response.Suggest;
|
||||
import org.springframework.data.util.Streamable;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Encapsulates a list of {@link SearchHit}s with additional information from the search.
|
||||
*
|
||||
*
|
||||
* @param <T> the result data class.
|
||||
* @author Sascha Woo
|
||||
* @since 4.0
|
||||
@@ -35,7 +35,7 @@ public interface SearchHits<T> extends Streamable<SearchHit<T>> {
|
||||
* @return the aggregations.
|
||||
*/
|
||||
@Nullable
|
||||
Aggregations getAggregations();
|
||||
AggregationsContainer<?> getAggregations();
|
||||
|
||||
/**
|
||||
* @return the maximum score
|
||||
@@ -78,6 +78,21 @@ public interface SearchHits<T> extends Streamable<SearchHit<T>> {
|
||||
return !getSearchHits().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the suggest response
|
||||
* @since 4.3
|
||||
*/
|
||||
@Nullable
|
||||
Suggest getSuggest();
|
||||
|
||||
/**
|
||||
* @return wether the {@link SearchHits} has a suggest response.
|
||||
* @since 4.3
|
||||
*/
|
||||
default boolean hasSuggest() {
|
||||
return getSuggest() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an iterator for {@link SearchHit}
|
||||
*/
|
||||
|
||||
@@ -18,7 +18,7 @@ package org.springframework.data.elasticsearch.core;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.elasticsearch.search.aggregations.Aggregations;
|
||||
import org.springframework.data.elasticsearch.core.suggest.response.Suggest;
|
||||
import org.springframework.data.util.Lazy;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -39,7 +39,8 @@ public class SearchHitsImpl<T> implements SearchScrollHits<T> {
|
||||
@Nullable private final String scrollId;
|
||||
private final List<? extends SearchHit<T>> searchHits;
|
||||
private final Lazy<List<SearchHit<T>>> unmodifiableSearchHits;
|
||||
@Nullable private final Aggregations aggregations;
|
||||
@Nullable private final AggregationsContainer<?> aggregations;
|
||||
@Nullable private final Suggest suggest;
|
||||
|
||||
/**
|
||||
* @param totalHits the number of total hits for the search
|
||||
@@ -50,7 +51,8 @@ public class SearchHitsImpl<T> implements SearchScrollHits<T> {
|
||||
* @param aggregations the aggregations if available
|
||||
*/
|
||||
public SearchHitsImpl(long totalHits, TotalHitsRelation totalHitsRelation, float maxScore, @Nullable String scrollId,
|
||||
List<? extends SearchHit<T>> searchHits, @Nullable Aggregations aggregations) {
|
||||
List<? extends SearchHit<T>> searchHits, @Nullable AggregationsContainer<?> aggregations,
|
||||
@Nullable Suggest suggest) {
|
||||
|
||||
Assert.notNull(searchHits, "searchHits must not be null");
|
||||
|
||||
@@ -60,6 +62,7 @@ public class SearchHitsImpl<T> implements SearchScrollHits<T> {
|
||||
this.scrollId = scrollId;
|
||||
this.searchHits = searchHits;
|
||||
this.aggregations = aggregations;
|
||||
this.suggest = suggest;
|
||||
this.unmodifiableSearchHits = Lazy.of(() -> Collections.unmodifiableList(searchHits));
|
||||
}
|
||||
|
||||
@@ -89,14 +92,23 @@ public class SearchHitsImpl<T> implements SearchScrollHits<T> {
|
||||
public List<SearchHit<T>> getSearchHits() {
|
||||
return unmodifiableSearchHits.get();
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region SearchHit access
|
||||
@Override
|
||||
public SearchHit<T> getSearchHit(int index) {
|
||||
return searchHits.get(index);
|
||||
}
|
||||
// endregion
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public AggregationsContainer<?> getAggregations() {
|
||||
return aggregations;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Suggest getSuggest() {
|
||||
return suggest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
@@ -109,12 +121,4 @@ public class SearchHitsImpl<T> implements SearchScrollHits<T> {
|
||||
", aggregations=" + aggregations + //
|
||||
'}';
|
||||
}
|
||||
|
||||
// region aggregations
|
||||
@Override
|
||||
@Nullable
|
||||
public Aggregations getAggregations() {
|
||||
return aggregations;
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import org.elasticsearch.search.aggregations.Aggregations;
|
||||
import org.springframework.data.util.CloseableIterator;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@@ -33,7 +32,7 @@ public interface SearchHitsIterator<T> extends CloseableIterator<SearchHit<T>> {
|
||||
* @return the aggregations.
|
||||
*/
|
||||
@Nullable
|
||||
Aggregations getAggregations();
|
||||
AggregationsContainer<?> getAggregations();
|
||||
|
||||
/**
|
||||
* @return the maximum score
|
||||
|
||||
@@ -71,7 +71,11 @@ public interface SearchOperations {
|
||||
* @param clazz the entity class
|
||||
* @return the suggest response
|
||||
* @since 4.1
|
||||
* @deprecated since 4.3 use a {@link org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder} with
|
||||
* {@link org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder#withSuggestBuilder(SuggestBuilder)},
|
||||
* call {@link #search(Query, Class)} and get the suggest from {@link SearchHits#getSuggest()}
|
||||
*/
|
||||
@Deprecated
|
||||
SearchResponse suggest(SuggestBuilder suggestion, Class<?> clazz);
|
||||
|
||||
/**
|
||||
@@ -80,7 +84,11 @@ public interface SearchOperations {
|
||||
* @param suggestion the query
|
||||
* @param index the index to run the query against
|
||||
* @return the suggest response
|
||||
* @deprecated since 4.3 use a {@link org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder} with
|
||||
* {@link org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder#withSuggestBuilder(SuggestBuilder)},
|
||||
* call {@link #search(Query, Class)} and get the suggest from {@link SearchHits#getSuggest()}
|
||||
*/
|
||||
@Deprecated
|
||||
SearchResponse suggest(SuggestBuilder suggestion, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
@@ -220,4 +228,23 @@ public interface SearchOperations {
|
||||
* are completed.
|
||||
*/
|
||||
<T> SearchHitsIterator<T> searchForStream(Query query, Class<T> clazz, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* Creates a {@link Query} to get all documents. Must be implemented by the concrete implementations to provide an
|
||||
* appropriate query using the respective client.
|
||||
*
|
||||
* @return a query to find all documents
|
||||
* @since 4.3
|
||||
*/
|
||||
Query matchAllQuery();
|
||||
|
||||
/**
|
||||
* Creates a {@link Query} to find get all documents with given ids. Must be implemented by the concrete
|
||||
* implementations to provide an appropriate query using the respective client.
|
||||
*
|
||||
* @param ids the list of ids must not be {@literal null}
|
||||
* @return query returning the documents with the given ids
|
||||
* @since 4.3
|
||||
*/
|
||||
Query idsQuery(List<String> ids);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.elasticsearch.search.aggregations.Aggregations;
|
||||
import org.springframework.data.elasticsearch.client.util.ScrollState;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -55,7 +54,7 @@ abstract class StreamQueries {
|
||||
Assert.notNull(continueScrollFunction, "continueScrollFunction must not be null.");
|
||||
Assert.notNull(clearScrollConsumer, "clearScrollConsumer must not be null.");
|
||||
|
||||
Aggregations aggregations = searchHits.getAggregations();
|
||||
AggregationsContainer<?> aggregations = searchHits.getAggregations();
|
||||
float maxScore = searchHits.getMaxScore();
|
||||
long totalHits = searchHits.getTotalHits();
|
||||
TotalHitsRelation totalHitsRelation = searchHits.getTotalHitsRelation();
|
||||
@@ -78,7 +77,7 @@ abstract class StreamQueries {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Aggregations getAggregations() {
|
||||
public AggregationsContainer<?> getAggregations() {
|
||||
return aggregations;
|
||||
}
|
||||
|
||||
|
||||
+4
-32
@@ -23,7 +23,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequestBuilder;
|
||||
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
|
||||
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
|
||||
@@ -59,7 +58,6 @@ import org.springframework.data.elasticsearch.core.index.PutTemplateRequest;
|
||||
import org.springframework.data.elasticsearch.core.index.Settings;
|
||||
import org.springframework.data.elasticsearch.core.index.TemplateData;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.query.AliasQuery;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -71,19 +69,18 @@ import org.springframework.util.Assert;
|
||||
* @author George Popides
|
||||
* @since 4.0
|
||||
*/
|
||||
class DefaultTransportIndexOperations extends AbstractDefaultIndexOperations implements IndexOperations {
|
||||
class TransportIndexTemplate extends AbstractIndexTemplate implements IndexOperations {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultTransportIndexOperations.class);
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(TransportIndexTemplate.class);
|
||||
|
||||
private final Client client;
|
||||
|
||||
public DefaultTransportIndexOperations(Client client, ElasticsearchConverter elasticsearchConverter,
|
||||
Class<?> boundClass) {
|
||||
public TransportIndexTemplate(Client client, ElasticsearchConverter elasticsearchConverter, Class<?> boundClass) {
|
||||
super(elasticsearchConverter, boundClass);
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public DefaultTransportIndexOperations(Client client, ElasticsearchConverter elasticsearchConverter,
|
||||
public TransportIndexTemplate(Client client, ElasticsearchConverter elasticsearchConverter,
|
||||
IndexCoordinates boundIndex) {
|
||||
super(elasticsearchConverter, boundIndex);
|
||||
this.client = client;
|
||||
@@ -146,31 +143,6 @@ class DefaultTransportIndexOperations extends AbstractDefaultIndexOperations imp
|
||||
return mappings.iterator().next().value.get(IndexCoordinates.TYPE).getSourceAsMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doAddAlias(AliasQuery query, IndexCoordinates index) {
|
||||
IndicesAliasesRequest.AliasActions aliasAction = requestFactory.aliasAction(query, index);
|
||||
return client.admin().indices().prepareAliases().addAliasAction(aliasAction).execute().actionGet().isAcknowledged();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
protected boolean doRemoveAlias(AliasQuery query, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(index, "No index defined for Alias");
|
||||
Assert.notNull(query.getAliasName(), "No alias defined");
|
||||
|
||||
IndicesAliasesRequestBuilder indicesAliasesRequestBuilder = requestFactory
|
||||
.indicesRemoveAliasesRequestBuilder(client, query, index);
|
||||
return indicesAliasesRequestBuilder.execute().actionGet().isAcknowledged();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<AliasMetadata> doQueryForAlias(IndexCoordinates index) {
|
||||
|
||||
GetAliasesRequest getAliasesRequest = requestFactory.getAliasesRequest(index);
|
||||
return client.admin().indices().getAliases(getAliasesRequest).actionGet().getAliases().get(index.getIndexName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Set<AliasData>> doGetAliases(@Nullable String[] aliasNames, @Nullable String[] indexNames) {
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.clients.elasticsearch7;
|
||||
|
||||
import org.elasticsearch.search.aggregations.Aggregation;
|
||||
import org.springframework.data.elasticsearch.core.AggregationContainer;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
/**
|
||||
* AggregationContainer implementation for an Elasticsearch7 aggregation.
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.3
|
||||
*/
|
||||
public class ElasticsearchAggregation implements AggregationContainer<Aggregation> {
|
||||
|
||||
private final Aggregation aggregation;
|
||||
|
||||
public ElasticsearchAggregation(Aggregation aggregation) {
|
||||
this.aggregation = aggregation;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Aggregation aggregation() {
|
||||
return aggregation;
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.clients.elasticsearch7;
|
||||
|
||||
import org.elasticsearch.search.aggregations.Aggregations;
|
||||
import org.springframework.data.elasticsearch.core.AggregationsContainer;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
/**
|
||||
* AggregationsContainer implementation for the Elasticsearch7 aggregations.
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.3
|
||||
*/
|
||||
public class ElasticsearchAggregations implements AggregationsContainer<Aggregations> {
|
||||
|
||||
private final Aggregations aggregations;
|
||||
|
||||
public ElasticsearchAggregations(Aggregations aggregations) {
|
||||
this.aggregations = aggregations;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Aggregations aggregations() {
|
||||
return aggregations;
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Classes and interfaces used by the code that uses Elasticsearch 7 client libraries
|
||||
*/
|
||||
@org.springframework.lang.NonNullApi
|
||||
@org.springframework.lang.NonNullFields
|
||||
package org.springframework.data.elasticsearch.core.clients.elasticsearch7;
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.convert;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.mapping.PropertyValueConverter;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Sascha Woo
|
||||
* @since 4.3
|
||||
*/
|
||||
public abstract class AbstractPropertyValueConverter implements PropertyValueConverter {
|
||||
|
||||
private final PersistentProperty<?> property;
|
||||
|
||||
public AbstractPropertyValueConverter(PersistentProperty<?> property) {
|
||||
|
||||
Assert.notNull(property, "property must not be null.");
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
protected PersistentProperty<?> getProperty() {
|
||||
return property;
|
||||
}
|
||||
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.convert;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.Range;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Sascha Woo
|
||||
* @since 4.3
|
||||
*/
|
||||
public abstract class AbstractRangePropertyValueConverter<T> extends AbstractPropertyValueConverter {
|
||||
|
||||
protected static final String LT_FIELD = "lt";
|
||||
protected static final String LTE_FIELD = "lte";
|
||||
protected static final String GT_FIELD = "gt";
|
||||
protected static final String GTE_FIELD = "gte";
|
||||
|
||||
public AbstractRangePropertyValueConverter(PersistentProperty<?> property) {
|
||||
super(property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object read(Object value) {
|
||||
|
||||
Assert.notNull(value, "value must not be null.");
|
||||
Assert.isInstanceOf(Map.class, value, "value must be instance of Map.");
|
||||
|
||||
try {
|
||||
Map<String, Object> source = (Map<String, Object>) value;
|
||||
Range.Bound<T> lowerBound;
|
||||
Range.Bound<T> upperBound;
|
||||
|
||||
if (source.containsKey(GTE_FIELD)) {
|
||||
lowerBound = Range.Bound.inclusive(parse((String) source.get(GTE_FIELD)));
|
||||
} else if (source.containsKey(GT_FIELD)) {
|
||||
lowerBound = Range.Bound.exclusive(parse((String) source.get(GT_FIELD)));
|
||||
} else {
|
||||
lowerBound = Range.Bound.unbounded();
|
||||
}
|
||||
|
||||
if (source.containsKey(LTE_FIELD)) {
|
||||
upperBound = Range.Bound.inclusive(parse((String) source.get(LTE_FIELD)));
|
||||
} else if (source.containsKey(LT_FIELD)) {
|
||||
upperBound = Range.Bound.exclusive(parse((String) source.get(LT_FIELD)));
|
||||
} else {
|
||||
upperBound = Range.Bound.unbounded();
|
||||
}
|
||||
|
||||
return Range.of(lowerBound, upperBound);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new ConversionException(
|
||||
String.format("Unable to convert value '%s' of property '%s'", value, getProperty().getName()), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object write(Object value) {
|
||||
|
||||
Assert.notNull(value, "value must not be null.");
|
||||
|
||||
if (!Range.class.isAssignableFrom(value.getClass())) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
try {
|
||||
Range<T> range = (Range<T>) value;
|
||||
Range.Bound<T> lowerBound = range.getLowerBound();
|
||||
Range.Bound<T> upperBound = range.getUpperBound();
|
||||
Map<String, Object> target = new LinkedHashMap<>();
|
||||
|
||||
if (lowerBound.isBounded()) {
|
||||
String lowerBoundValue = format(lowerBound.getValue().get());
|
||||
if (lowerBound.isInclusive()) {
|
||||
target.put(GTE_FIELD, lowerBoundValue);
|
||||
} else {
|
||||
target.put(GT_FIELD, lowerBoundValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (upperBound.isBounded()) {
|
||||
String upperBoundValue = format(upperBound.getValue().get());
|
||||
if (upperBound.isInclusive()) {
|
||||
target.put(LTE_FIELD, upperBoundValue);
|
||||
} else {
|
||||
target.put(LT_FIELD, upperBoundValue);
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new ConversionException(
|
||||
String.format("Unable to convert value '%s' of property '%s'", value, getProperty().getName()), e);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract String format(T value);
|
||||
|
||||
protected Class<?> getGenericType() {
|
||||
return getProperty().getTypeInformation().getTypeArguments().get(0).getType();
|
||||
}
|
||||
|
||||
protected abstract T parse(String value);
|
||||
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.convert;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
|
||||
/**
|
||||
* @author Sascha Woo
|
||||
* @since 4.3
|
||||
*/
|
||||
public class DatePropertyValueConverter extends AbstractPropertyValueConverter {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DatePropertyValueConverter.class);
|
||||
|
||||
private final List<ElasticsearchDateConverter> dateConverters;
|
||||
|
||||
public DatePropertyValueConverter(PersistentProperty<?> property, List<ElasticsearchDateConverter> dateConverters) {
|
||||
|
||||
super(property);
|
||||
this.dateConverters = dateConverters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object read(Object value) {
|
||||
|
||||
String s = value.toString();
|
||||
|
||||
for (ElasticsearchDateConverter dateConverter : dateConverters) {
|
||||
try {
|
||||
return dateConverter.parse(s);
|
||||
} catch (Exception e) {
|
||||
LOGGER.trace(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
throw new ConversionException(String.format("Unable to convert value '%s' to %s for property '%s'", s,
|
||||
getProperty().getActualType().getTypeName(), getProperty().getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object write(Object value) {
|
||||
|
||||
if (!Date.class.isAssignableFrom(value.getClass())) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
try {
|
||||
return dateConverters.get(0).format((Date) value);
|
||||
} catch (Exception e) {
|
||||
throw new ConversionException(
|
||||
String.format("Unable to convert value '%s' of property '%s'", value, getProperty().getName()), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.convert;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
|
||||
/**
|
||||
* @author Sascha Woo
|
||||
* @since 4.3
|
||||
*/
|
||||
public class DateRangePropertyValueConverter extends AbstractRangePropertyValueConverter<Date> {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DateRangePropertyValueConverter.class);
|
||||
|
||||
private final List<ElasticsearchDateConverter> dateConverters;
|
||||
|
||||
public DateRangePropertyValueConverter(PersistentProperty<?> property,
|
||||
List<ElasticsearchDateConverter> dateConverters) {
|
||||
|
||||
super(property);
|
||||
this.dateConverters = dateConverters;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String format(Date value) {
|
||||
return dateConverters.get(0).format(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Date parse(String value) {
|
||||
|
||||
for (ElasticsearchDateConverter converters : dateConverters) {
|
||||
try {
|
||||
return converters.parse(value);
|
||||
} catch (Exception e) {
|
||||
LOGGER.trace(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
throw new ConversionException(String.format("Unable to convert value '%s' to %s for property '%s'", value,
|
||||
getGenericType().getTypeName(), getProperty().getName()));
|
||||
}
|
||||
|
||||
}
|
||||
-88
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.convert;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.joda.time.LocalDateTime;
|
||||
import org.joda.time.ReadableInstant;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
import org.joda.time.format.ISODateTimeFormat;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
|
||||
/**
|
||||
* DateTimeConverters
|
||||
*
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
*/
|
||||
public final class DateTimeConverters {
|
||||
|
||||
private static DateTimeFormatter formatter = ISODateTimeFormat.dateTime().withZone(DateTimeZone.UTC);
|
||||
|
||||
/**
|
||||
* @deprecated since 4.1
|
||||
*/
|
||||
@Deprecated
|
||||
public enum JodaDateTimeConverter implements Converter<ReadableInstant, String> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public String convert(ReadableInstant source) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
return formatter.print(source);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 4.1
|
||||
*/
|
||||
@Deprecated
|
||||
public enum JodaLocalDateTimeConverter implements Converter<LocalDateTime, String> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public String convert(LocalDateTime source) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
return formatter.print(source.toDateTime(DateTimeZone.UTC));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 4.1
|
||||
*/
|
||||
@Deprecated
|
||||
public enum JavaDateConverter implements Converter<Date, String> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public String convert(Date source) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return formatter.print(source.getTime());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -19,6 +19,7 @@ import org.springframework.data.convert.EntityConverter;
|
||||
import org.springframework.data.elasticsearch.core.document.Document;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
|
||||
import org.springframework.data.elasticsearch.core.mapping.PropertyValueConverter;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
@@ -93,8 +94,7 @@ public interface ElasticsearchConverter
|
||||
/**
|
||||
* Updates a {@link Query} by renaming the property names in the query to the correct mapped field names and the
|
||||
* values to the converted values if the {@link ElasticsearchPersistentProperty} for a property has a
|
||||
* {@link org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentPropertyConverter}. If
|
||||
* domainClass is null it's a noop.
|
||||
* {@link PropertyValueConverter}. If domainClass is null it's a noop.
|
||||
*
|
||||
* @param query the query that is internally updated, must not be {@literal null}
|
||||
* @param domainClass the class of the object that is searched with the query
|
||||
|
||||
+70
-5
@@ -47,7 +47,7 @@ final public class ElasticsearchDateConverter {
|
||||
|
||||
/**
|
||||
* Creates an ElasticsearchDateConverter for the given {@link DateFormat}.
|
||||
*
|
||||
*
|
||||
* @param dateFormat must not be @{literal null}
|
||||
* @return converter
|
||||
*/
|
||||
@@ -66,7 +66,6 @@ final public class ElasticsearchDateConverter {
|
||||
*/
|
||||
public static ElasticsearchDateConverter of(String pattern) {
|
||||
|
||||
Assert.notNull(pattern, "pattern must not be null");
|
||||
Assert.hasText(pattern, "pattern must not be empty");
|
||||
|
||||
String[] subPatterns = pattern.split("\\|\\|");
|
||||
@@ -86,7 +85,7 @@ final public class ElasticsearchDateConverter {
|
||||
*/
|
||||
public String format(TemporalAccessor accessor) {
|
||||
|
||||
Assert.notNull("accessor", "accessor must not be null");
|
||||
Assert.notNull(accessor, "accessor must not be null");
|
||||
|
||||
if (accessor instanceof Instant) {
|
||||
Instant instant = (Instant) accessor;
|
||||
@@ -135,7 +134,7 @@ final public class ElasticsearchDateConverter {
|
||||
/**
|
||||
* Creates a {@link DateFormatter} for a given pattern. The pattern can be the name of a {@link DateFormat} enum value
|
||||
* or a literal pattern.
|
||||
*
|
||||
*
|
||||
* @param pattern the pattern to use
|
||||
* @return DateFormatter
|
||||
*/
|
||||
@@ -172,8 +171,73 @@ final public class ElasticsearchDateConverter {
|
||||
return new PatternDateFormatter(dateTimeFormatter);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends TemporalAccessor> TemporalQuery<T> getTemporalQuery(Class<T> type) {
|
||||
return temporal -> {
|
||||
// no reflection for java.time classes (GraalVM native)
|
||||
if (type == java.time.chrono.HijrahDate.class) {
|
||||
return (T) java.time.chrono.HijrahDate.from(temporal);
|
||||
}
|
||||
if (type == java.time.chrono.JapaneseDate.class) {
|
||||
return (T) java.time.chrono.JapaneseDate.from(temporal);
|
||||
}
|
||||
if (type == java.time.ZonedDateTime.class) {
|
||||
return (T) java.time.ZonedDateTime.from(temporal);
|
||||
}
|
||||
if (type == java.time.LocalDateTime.class) {
|
||||
return (T) java.time.LocalDateTime.from(temporal);
|
||||
}
|
||||
if (type == java.time.chrono.ThaiBuddhistDate.class) {
|
||||
return (T) java.time.chrono.ThaiBuddhistDate.from(temporal);
|
||||
}
|
||||
if (type == java.time.LocalTime.class) {
|
||||
return (T) java.time.LocalTime.from(temporal);
|
||||
}
|
||||
if (type == java.time.ZoneOffset.class) {
|
||||
return (T) java.time.ZoneOffset.from(temporal);
|
||||
}
|
||||
if (type == java.time.OffsetTime.class) {
|
||||
return (T) java.time.OffsetTime.from(temporal);
|
||||
}
|
||||
if (type == java.time.chrono.ChronoLocalDate.class) {
|
||||
return (T) java.time.chrono.ChronoLocalDate.from(temporal);
|
||||
}
|
||||
if (type == java.time.Month.class) {
|
||||
return (T) java.time.Month.from(temporal);
|
||||
}
|
||||
if (type == java.time.chrono.ChronoLocalDateTime.class) {
|
||||
return (T) java.time.chrono.ChronoLocalDateTime.from(temporal);
|
||||
}
|
||||
if (type == java.time.MonthDay.class) {
|
||||
return (T) java.time.MonthDay.from(temporal);
|
||||
}
|
||||
if (type == java.time.Instant.class) {
|
||||
return (T) java.time.Instant.from(temporal);
|
||||
}
|
||||
if (type == java.time.OffsetDateTime.class) {
|
||||
return (T) java.time.OffsetDateTime.from(temporal);
|
||||
}
|
||||
if (type == java.time.chrono.ChronoZonedDateTime.class) {
|
||||
return (T) java.time.chrono.ChronoZonedDateTime.from(temporal);
|
||||
}
|
||||
if (type == java.time.chrono.MinguoDate.class) {
|
||||
return (T) java.time.chrono.MinguoDate.from(temporal);
|
||||
}
|
||||
if (type == java.time.Year.class) {
|
||||
return (T) java.time.Year.from(temporal);
|
||||
}
|
||||
if (type == java.time.DayOfWeek.class) {
|
||||
return (T) java.time.DayOfWeek.from(temporal);
|
||||
}
|
||||
if (type == java.time.LocalDate.class) {
|
||||
return (T) java.time.LocalDate.from(temporal);
|
||||
}
|
||||
if (type == java.time.YearMonth.class) {
|
||||
return (T) java.time.YearMonth.from(temporal);
|
||||
}
|
||||
|
||||
// for implementations not covered until here use reflection to check for the existence of a static
|
||||
// from(TemporalAccessor) method
|
||||
try {
|
||||
Method method = type.getMethod("from", TemporalAccessor.class);
|
||||
Object o = method.invoke(null, temporal);
|
||||
@@ -184,7 +248,8 @@ final public class ElasticsearchDateConverter {
|
||||
throw new ConversionException("could not create object of class " + type.getName(), e);
|
||||
}
|
||||
};
|
||||
} // endregion
|
||||
}
|
||||
// endregion
|
||||
|
||||
/**
|
||||
* a DateFormatter to convert epoch milliseconds
|
||||
|
||||
+944
-860
File diff suppressed because it is too large
Load Diff
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.convert;
|
||||
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
|
||||
/**
|
||||
* @author Sascha Woo
|
||||
* @since 4.3
|
||||
*/
|
||||
public class NumberRangePropertyValueConverter extends AbstractRangePropertyValueConverter<Number> {
|
||||
|
||||
public NumberRangePropertyValueConverter(PersistentProperty<?> property) {
|
||||
super(property);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String format(Number number) {
|
||||
return String.valueOf(number);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Number parse(String value) {
|
||||
|
||||
Class<?> type = getGenericType();
|
||||
if (Integer.class.isAssignableFrom(type)) {
|
||||
return Integer.valueOf(value);
|
||||
} else if (Float.class.isAssignableFrom(type)) {
|
||||
return Float.valueOf(value);
|
||||
} else if (Long.class.isAssignableFrom(type)) {
|
||||
return Long.valueOf(value);
|
||||
} else if (Double.class.isAssignableFrom(type)) {
|
||||
return Double.valueOf(value);
|
||||
}
|
||||
|
||||
throw new ConversionException(String.format("Unable to convert value '%s' to %s for property '%s'", value,
|
||||
type.getTypeName(), getProperty().getName()));
|
||||
}
|
||||
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.convert;
|
||||
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
|
||||
/**
|
||||
* @author Sascha Woo
|
||||
* @since 4.3
|
||||
*/
|
||||
public class TemporalPropertyValueConverter extends AbstractPropertyValueConverter {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(TemporalPropertyValueConverter.class);
|
||||
|
||||
private final List<ElasticsearchDateConverter> dateConverters;
|
||||
|
||||
public TemporalPropertyValueConverter(PersistentProperty<?> property,
|
||||
List<ElasticsearchDateConverter> dateConverters) {
|
||||
|
||||
super(property);
|
||||
this.dateConverters = dateConverters;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Object read(Object value) {
|
||||
|
||||
String s = value.toString();
|
||||
Class<?> actualType = getProperty().getActualType();
|
||||
|
||||
for (ElasticsearchDateConverter dateConverter : dateConverters) {
|
||||
try {
|
||||
return dateConverter.parse(s, (Class<? extends TemporalAccessor>) actualType);
|
||||
} catch (Exception e) {
|
||||
LOGGER.trace(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
throw new ConversionException(String.format("Unable to convert value '%s' to %s for property '%s'", s,
|
||||
getProperty().getActualType().getTypeName(), getProperty().getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object write(Object value) {
|
||||
|
||||
if (!TemporalAccessor.class.isAssignableFrom(value.getClass())) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
try {
|
||||
return dateConverters.get(0).format((TemporalAccessor) value);
|
||||
} catch (Exception e) {
|
||||
throw new ConversionException(
|
||||
String.format("Unable to convert value '%s' of property '%s'", value, getProperty().getName()), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.convert;
|
||||
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Sascha Woo
|
||||
* @since 4.3
|
||||
*/
|
||||
public class TemporalRangePropertyValueConverter extends AbstractRangePropertyValueConverter<TemporalAccessor> {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(TemporalRangePropertyValueConverter.class);
|
||||
|
||||
private final List<ElasticsearchDateConverter> dateConverters;
|
||||
|
||||
public TemporalRangePropertyValueConverter(PersistentProperty<?> property,
|
||||
List<ElasticsearchDateConverter> dateConverters) {
|
||||
|
||||
super(property);
|
||||
|
||||
Assert.notEmpty(dateConverters, "dateConverters must not be empty.");
|
||||
this.dateConverters = dateConverters;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String format(TemporalAccessor temporal) {
|
||||
return dateConverters.get(0).format(temporal);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TemporalAccessor parse(String value) {
|
||||
|
||||
Class<?> type = getGenericType();
|
||||
for (ElasticsearchDateConverter converters : dateConverters) {
|
||||
try {
|
||||
return converters.parse(value, (Class<? extends TemporalAccessor>) type);
|
||||
} catch (Exception e) {
|
||||
LOGGER.trace(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
throw new ConversionException(String.format("Unable to convert value '%s' to %s for property '%s'", value,
|
||||
type.getTypeName(), getProperty().getName()));
|
||||
}
|
||||
|
||||
}
|
||||
+54
-45
@@ -23,7 +23,6 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -63,36 +62,38 @@ import com.fasterxml.jackson.core.JsonGenerator;
|
||||
* @author Matt Gilene
|
||||
* @since 4.0
|
||||
*/
|
||||
public class DocumentAdapters {
|
||||
public final class DocumentAdapters {
|
||||
|
||||
private DocumentAdapters() {}
|
||||
|
||||
/**
|
||||
* Create a {@link Document} from {@link GetResponse}.
|
||||
* <p>
|
||||
* Returns a {@link Document} using the source if available.
|
||||
* Returns a {@link Document} using the getResponse if available.
|
||||
*
|
||||
* @param source the source {@link GetResponse}.
|
||||
* @return the adapted {@link Document}, null if source.isExists() returns false.
|
||||
* @param getResponse the getResponse {@link GetResponse}.
|
||||
* @return the adapted {@link Document}, null if getResponse.isExists() returns false.
|
||||
*/
|
||||
@Nullable
|
||||
public static Document from(GetResponse source) {
|
||||
public static Document from(GetResponse getResponse) {
|
||||
|
||||
Assert.notNull(source, "GetResponse must not be null");
|
||||
Assert.notNull(getResponse, "GetResponse must not be null");
|
||||
|
||||
if (!source.isExists()) {
|
||||
if (!getResponse.isExists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (source.isSourceEmpty()) {
|
||||
return fromDocumentFields(source, source.getIndex(), source.getId(), source.getVersion(), source.getSeqNo(),
|
||||
source.getPrimaryTerm());
|
||||
if (getResponse.isSourceEmpty()) {
|
||||
return fromDocumentFields(getResponse, getResponse.getIndex(), getResponse.getId(), getResponse.getVersion(),
|
||||
getResponse.getSeqNo(), getResponse.getPrimaryTerm());
|
||||
}
|
||||
|
||||
Document document = Document.from(source.getSourceAsMap());
|
||||
document.setIndex(source.getIndex());
|
||||
document.setId(source.getId());
|
||||
document.setVersion(source.getVersion());
|
||||
document.setSeqNo(source.getSeqNo());
|
||||
document.setPrimaryTerm(source.getPrimaryTerm());
|
||||
Document document = Document.from(getResponse.getSourceAsMap());
|
||||
document.setIndex(getResponse.getIndex());
|
||||
document.setId(getResponse.getId());
|
||||
document.setVersion(getResponse.getVersion());
|
||||
document.setSeqNo(getResponse.getSeqNo());
|
||||
document.setPrimaryTerm(getResponse.getPrimaryTerm());
|
||||
|
||||
return document;
|
||||
}
|
||||
@@ -176,8 +177,8 @@ public class DocumentAdapters {
|
||||
Map<String, SearchHits> sourceInnerHits = source.getInnerHits();
|
||||
|
||||
if (sourceInnerHits != null) {
|
||||
sourceInnerHits
|
||||
.forEach((name, searchHits) -> innerHits.put(name, SearchDocumentResponse.from(searchHits, null, null)));
|
||||
sourceInnerHits.forEach((name, searchHits) -> innerHits.put(name,
|
||||
SearchDocumentResponse.from(searchHits, null, null, null, searchDocument -> null)));
|
||||
}
|
||||
|
||||
NestedMetaData nestedMetaData = from(source.getNestedIdentity());
|
||||
@@ -185,12 +186,14 @@ public class DocumentAdapters {
|
||||
List<String> matchedQueries = from(source.getMatchedQueries());
|
||||
|
||||
BytesReference sourceRef = source.getSourceRef();
|
||||
Map<String, DocumentField> sourceFields = source.getFields();
|
||||
|
||||
if (sourceRef == null || sourceRef.length() == 0) {
|
||||
return new SearchDocumentAdapter(
|
||||
source.getScore(), source.getSortValues(), source.getFields(), highlightFields, fromDocumentFields(source,
|
||||
source.getIndex(), source.getId(), source.getVersion(), source.getSeqNo(), source.getPrimaryTerm()),
|
||||
innerHits, nestedMetaData, explanation, matchedQueries);
|
||||
fromDocumentFields(source, source.getIndex(), source.getId(), source.getVersion(), source.getSeqNo(),
|
||||
source.getPrimaryTerm()),
|
||||
source.getScore(), source.getSortValues(), sourceFields, highlightFields, innerHits, nestedMetaData,
|
||||
explanation, matchedQueries);
|
||||
}
|
||||
|
||||
Document document = Document.from(source.getSourceAsMap());
|
||||
@@ -203,8 +206,8 @@ public class DocumentAdapters {
|
||||
document.setSeqNo(source.getSeqNo());
|
||||
document.setPrimaryTerm(source.getPrimaryTerm());
|
||||
|
||||
return new SearchDocumentAdapter(source.getScore(), source.getSortValues(), source.getFields(), highlightFields,
|
||||
document, innerHits, nestedMetaData, explanation, matchedQueries);
|
||||
return new SearchDocumentAdapter(document, source.getScore(), source.getSortValues(), sourceFields, highlightFields,
|
||||
innerHits, nestedMetaData, explanation, matchedQueries);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -243,6 +246,10 @@ public class DocumentAdapters {
|
||||
*
|
||||
* @param documentFields the {@link DocumentField}s backing the {@link Document}.
|
||||
* @param index the index where the Document was found
|
||||
* @param id the document id
|
||||
* @param version the document version
|
||||
* @param seqNo the seqNo if the document
|
||||
* @param primaryTerm the primaryTerm of the document
|
||||
* @return the adapted {@link Document}.
|
||||
*/
|
||||
public static Document fromDocumentFields(Iterable<DocumentField> documentFields, String index, String id,
|
||||
@@ -261,10 +268,13 @@ public class DocumentAdapters {
|
||||
return new DocumentFieldAdapter(fields, index, id, version, seqNo, primaryTerm);
|
||||
}
|
||||
|
||||
// TODO: Performance regarding keys/values/entry-set
|
||||
/**
|
||||
* Adapter for a collection of {@link DocumentField}s.
|
||||
*/
|
||||
static class DocumentFieldAdapter implements Document {
|
||||
|
||||
private final Collection<DocumentField> documentFields;
|
||||
private final Map<String, DocumentField> documentFieldMap;
|
||||
private final String index;
|
||||
private final String id;
|
||||
private final long version;
|
||||
@@ -274,6 +284,8 @@ public class DocumentAdapters {
|
||||
DocumentFieldAdapter(Collection<DocumentField> documentFields, String index, String id, long version, long seqNo,
|
||||
long primaryTerm) {
|
||||
this.documentFields = documentFields;
|
||||
this.documentFieldMap = new LinkedHashMap<>(documentFields.size());
|
||||
documentFields.forEach(documentField -> documentFieldMap.put(documentField.getName(), documentField));
|
||||
this.index = index;
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
@@ -353,14 +365,7 @@ public class DocumentAdapters {
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
|
||||
for (DocumentField documentField : documentFields) {
|
||||
if (documentField.getName().equals(key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return documentFieldMap.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -380,11 +385,9 @@ public class DocumentAdapters {
|
||||
@Override
|
||||
@Nullable
|
||||
public Object get(Object key) {
|
||||
return documentFields.stream() //
|
||||
.filter(documentField -> documentField.getName().equals(key)) //
|
||||
.map(DocumentField::getValue).findFirst() //
|
||||
.orElse(null); //
|
||||
|
||||
DocumentField documentField = documentFieldMap.get(key);
|
||||
return documentField != null ? documentField.getValue() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -409,17 +412,18 @@ public class DocumentAdapters {
|
||||
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
return documentFields.stream().map(DocumentField::getName).collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
return documentFieldMap.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Object> values() {
|
||||
return documentFields.stream().map(DocumentFieldAdapter::getValue).collect(Collectors.toList());
|
||||
return documentFieldMap.values().stream().map(DocumentFieldAdapter::getValue).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<String, Object>> entrySet() {
|
||||
return documentFields.stream().collect(Collectors.toMap(DocumentField::getName, DocumentFieldAdapter::getValue))
|
||||
return documentFieldMap.entrySet().stream()
|
||||
.collect(Collectors.toMap(Entry::getKey, entry -> DocumentFieldAdapter.getValue(entry.getValue())))
|
||||
.entrySet();
|
||||
}
|
||||
|
||||
@@ -458,7 +462,6 @@ public class DocumentAdapters {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + '@' + this.id + '#' + this.version + ' ' + toJson();
|
||||
@@ -494,14 +497,14 @@ public class DocumentAdapters {
|
||||
@Nullable private final Explanation explanation;
|
||||
@Nullable private final List<String> matchedQueries;
|
||||
|
||||
SearchDocumentAdapter(float score, Object[] sortValues, Map<String, DocumentField> fields,
|
||||
Map<String, List<String>> highlightFields, Document delegate, Map<String, SearchDocumentResponse> innerHits,
|
||||
SearchDocumentAdapter(Document delegate, float score, Object[] sortValues, Map<String, DocumentField> fields,
|
||||
Map<String, List<String>> highlightFields, Map<String, SearchDocumentResponse> innerHits,
|
||||
@Nullable NestedMetaData nestedMetaData, @Nullable Explanation explanation,
|
||||
@Nullable List<String> matchedQueries) {
|
||||
|
||||
this.delegate = delegate;
|
||||
this.score = score;
|
||||
this.sortValues = sortValues;
|
||||
this.delegate = delegate;
|
||||
fields.forEach((name, documentField) -> this.fields.put(name, documentField.getValues()));
|
||||
this.highlightFields.putAll(highlightFields);
|
||||
this.innerHits.putAll(innerHits);
|
||||
@@ -646,7 +649,13 @@ public class DocumentAdapters {
|
||||
|
||||
@Override
|
||||
public Object get(Object key) {
|
||||
return delegate.get(key);
|
||||
|
||||
if (delegate.containsKey(key)) {
|
||||
return delegate.get(key);
|
||||
}
|
||||
|
||||
// fallback to fields
|
||||
return fields.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+168
-19
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-2021 the original author or authors.
|
||||
* Copyright 2019-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,39 +17,56 @@ package org.springframework.data.elasticsearch.core.document;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.lucene.search.TotalHits;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.common.text.Text;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
import org.elasticsearch.search.SearchHits;
|
||||
import org.elasticsearch.search.aggregations.Aggregations;
|
||||
import org.springframework.data.elasticsearch.core.AggregationsContainer;
|
||||
import org.springframework.data.elasticsearch.core.clients.elasticsearch7.ElasticsearchAggregations;
|
||||
import org.springframework.data.elasticsearch.core.suggest.response.CompletionSuggestion;
|
||||
import org.springframework.data.elasticsearch.core.suggest.response.PhraseSuggestion;
|
||||
import org.springframework.data.elasticsearch.core.suggest.response.SortBy;
|
||||
import org.springframework.data.elasticsearch.core.suggest.response.Suggest;
|
||||
import org.springframework.data.elasticsearch.core.suggest.response.TermSuggestion;
|
||||
import org.springframework.data.elasticsearch.support.ScoreDoc;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* This represents the complete search response from Elasticsearch, including the returned documents. Instances must be
|
||||
* created with the {@link #from(SearchResponse)} method.
|
||||
*
|
||||
* created with the {@link #from(SearchResponse, EntityCreator)} method.
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.0
|
||||
*/
|
||||
public class SearchDocumentResponse {
|
||||
|
||||
private static final Log LOGGER = LogFactory.getLog(SearchDocumentResponse.class);
|
||||
|
||||
private final long totalHits;
|
||||
private final String totalHitsRelation;
|
||||
private final float maxScore;
|
||||
private final String scrollId;
|
||||
private final List<SearchDocument> searchDocuments;
|
||||
private final Aggregations aggregations;
|
||||
@Nullable private final AggregationsContainer<?> aggregations;
|
||||
@Nullable private final Suggest suggest;
|
||||
|
||||
private SearchDocumentResponse(long totalHits, String totalHitsRelation, float maxScore, String scrollId,
|
||||
List<SearchDocument> searchDocuments, Aggregations aggregations) {
|
||||
List<SearchDocument> searchDocuments, @Nullable Aggregations aggregations, @Nullable Suggest suggest) {
|
||||
this.totalHits = totalHits;
|
||||
this.totalHitsRelation = totalHitsRelation;
|
||||
this.maxScore = maxScore;
|
||||
this.scrollId = scrollId;
|
||||
this.searchDocuments = searchDocuments;
|
||||
this.aggregations = aggregations;
|
||||
this.aggregations = aggregations != null ? new ElasticsearchAggregations(aggregations) : null;
|
||||
this.suggest = suggest;
|
||||
}
|
||||
|
||||
public long getTotalHits() {
|
||||
@@ -72,40 +89,52 @@ public class SearchDocumentResponse {
|
||||
return searchDocuments;
|
||||
}
|
||||
|
||||
public Aggregations getAggregations() {
|
||||
@Nullable
|
||||
public AggregationsContainer<?> getAggregations() {
|
||||
return aggregations;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Suggest getSuggest() {
|
||||
return suggest;
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a SearchDocumentResponse from the {@link SearchResponse}
|
||||
*
|
||||
* @param searchResponse must not be {@literal null}
|
||||
* @param entityCreator function to create an entity from a {@link SearchDocument}
|
||||
* @param <T> entity type
|
||||
* @return the SearchDocumentResponse
|
||||
*/
|
||||
public static SearchDocumentResponse from(SearchResponse searchResponse) {
|
||||
public static <T> SearchDocumentResponse from(SearchResponse searchResponse, EntityCreator<T> entityCreator) {
|
||||
|
||||
Assert.notNull(searchResponse, "searchResponse must not be null");
|
||||
|
||||
Aggregations aggregations = searchResponse.getAggregations();
|
||||
String scrollId = searchResponse.getScrollId();
|
||||
|
||||
SearchHits searchHits = searchResponse.getHits();
|
||||
String scrollId = searchResponse.getScrollId();
|
||||
Aggregations aggregations = searchResponse.getAggregations();
|
||||
org.elasticsearch.search.suggest.Suggest suggest = searchResponse.getSuggest();
|
||||
|
||||
SearchDocumentResponse searchDocumentResponse = from(searchHits, scrollId, aggregations);
|
||||
return searchDocumentResponse;
|
||||
return from(searchHits, scrollId, aggregations, suggest, entityCreator);
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a {@link SearchDocumentResponse} from {@link SearchHits} with the given scrollId and aggregations
|
||||
*
|
||||
* creates a {@link SearchDocumentResponse} from {@link SearchHits} with the given scrollId aggregations and suggest
|
||||
*
|
||||
* @param searchHits the {@link SearchHits} to process
|
||||
* @param scrollId scrollId
|
||||
* @param aggregations aggregations
|
||||
* @param suggestES the suggestion response from Elasticsearch
|
||||
* @param entityCreator function to create an entity from a {@link SearchDocument}
|
||||
* @param <T> entity type
|
||||
* @return the {@link SearchDocumentResponse}
|
||||
* @since 4.1
|
||||
* @since 4.3
|
||||
*/
|
||||
public static SearchDocumentResponse from(SearchHits searchHits, @Nullable String scrollId,
|
||||
@Nullable Aggregations aggregations) {
|
||||
public static <T> SearchDocumentResponse from(SearchHits searchHits, @Nullable String scrollId,
|
||||
@Nullable Aggregations aggregations, @Nullable org.elasticsearch.search.suggest.Suggest suggestES,
|
||||
EntityCreator<T> entityCreator) {
|
||||
|
||||
TotalHits responseTotalHits = searchHits.getTotalHits();
|
||||
|
||||
long totalHits;
|
||||
@@ -128,7 +157,127 @@ public class SearchDocumentResponse {
|
||||
}
|
||||
}
|
||||
|
||||
return new SearchDocumentResponse(totalHits, totalHitsRelation, maxScore, scrollId, searchDocuments, aggregations);
|
||||
Suggest suggest = suggestFrom(suggestES, entityCreator);
|
||||
return new SearchDocumentResponse(totalHits, totalHitsRelation, maxScore, scrollId, searchDocuments, aggregations,
|
||||
suggest);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static <T> Suggest suggestFrom(@Nullable org.elasticsearch.search.suggest.Suggest suggestES,
|
||||
EntityCreator<T> entityCreator) {
|
||||
|
||||
if (suggestES == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Suggest.Suggestion<? extends Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option>>> suggestions = new ArrayList<>();
|
||||
|
||||
for (org.elasticsearch.search.suggest.Suggest.Suggestion<? extends org.elasticsearch.search.suggest.Suggest.Suggestion.Entry<? extends org.elasticsearch.search.suggest.Suggest.Suggestion.Entry.Option>> suggestionES : suggestES) {
|
||||
|
||||
if (suggestionES instanceof org.elasticsearch.search.suggest.term.TermSuggestion) {
|
||||
org.elasticsearch.search.suggest.term.TermSuggestion termSuggestionES = (org.elasticsearch.search.suggest.term.TermSuggestion) suggestionES;
|
||||
|
||||
List<TermSuggestion.Entry> entries = new ArrayList<>();
|
||||
for (org.elasticsearch.search.suggest.term.TermSuggestion.Entry entryES : termSuggestionES) {
|
||||
|
||||
List<TermSuggestion.Entry.Option> options = new ArrayList<>();
|
||||
for (org.elasticsearch.search.suggest.term.TermSuggestion.Entry.Option optionES : entryES) {
|
||||
options.add(new TermSuggestion.Entry.Option(textToString(optionES.getText()),
|
||||
textToString(optionES.getHighlighted()), optionES.getScore(), optionES.collateMatch(),
|
||||
optionES.getFreq()));
|
||||
}
|
||||
|
||||
entries.add(new TermSuggestion.Entry(textToString(entryES.getText()), entryES.getOffset(),
|
||||
entryES.getLength(), options));
|
||||
}
|
||||
|
||||
suggestions.add(new TermSuggestion(termSuggestionES.getName(), termSuggestionES.getSize(), entries,
|
||||
suggestFrom(termSuggestionES.getSort())));
|
||||
}
|
||||
|
||||
if (suggestionES instanceof org.elasticsearch.search.suggest.phrase.PhraseSuggestion) {
|
||||
org.elasticsearch.search.suggest.phrase.PhraseSuggestion phraseSuggestionES = (org.elasticsearch.search.suggest.phrase.PhraseSuggestion) suggestionES;
|
||||
|
||||
List<PhraseSuggestion.Entry> entries = new ArrayList<>();
|
||||
for (org.elasticsearch.search.suggest.phrase.PhraseSuggestion.Entry entryES : phraseSuggestionES) {
|
||||
|
||||
List<PhraseSuggestion.Entry.Option> options = new ArrayList<>();
|
||||
for (org.elasticsearch.search.suggest.phrase.PhraseSuggestion.Entry.Option optionES : entryES) {
|
||||
options.add(new PhraseSuggestion.Entry.Option(textToString(optionES.getText()),
|
||||
textToString(optionES.getHighlighted()), optionES.getScore(), optionES.collateMatch()));
|
||||
}
|
||||
|
||||
entries.add(new PhraseSuggestion.Entry(textToString(entryES.getText()), entryES.getOffset(),
|
||||
entryES.getLength(), options, entryES.getCutoffScore()));
|
||||
}
|
||||
|
||||
suggestions.add(new PhraseSuggestion(phraseSuggestionES.getName(), phraseSuggestionES.getSize(), entries));
|
||||
}
|
||||
|
||||
if (suggestionES instanceof org.elasticsearch.search.suggest.completion.CompletionSuggestion) {
|
||||
org.elasticsearch.search.suggest.completion.CompletionSuggestion completionSuggestionES = (org.elasticsearch.search.suggest.completion.CompletionSuggestion) suggestionES;
|
||||
|
||||
List<CompletionSuggestion.Entry<T>> entries = new ArrayList<>();
|
||||
for (org.elasticsearch.search.suggest.completion.CompletionSuggestion.Entry entryES : completionSuggestionES) {
|
||||
|
||||
List<CompletionSuggestion.Entry.Option<T>> options = new ArrayList<>();
|
||||
for (org.elasticsearch.search.suggest.completion.CompletionSuggestion.Entry.Option optionES : entryES) {
|
||||
SearchDocument searchDocument = optionES.getHit() != null ? DocumentAdapters.from(optionES.getHit()) : null;
|
||||
|
||||
T hitEntity = null;
|
||||
|
||||
if (searchDocument != null) {
|
||||
try {
|
||||
hitEntity = entityCreator.apply(searchDocument).get();
|
||||
} catch (Exception e) {
|
||||
if (LOGGER.isWarnEnabled()) {
|
||||
LOGGER.warn("Error creating entity from SearchDocument");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
options.add(new CompletionSuggestion.Entry.Option<T>(textToString(optionES.getText()),
|
||||
textToString(optionES.getHighlighted()), optionES.getScore(), optionES.collateMatch(),
|
||||
optionES.getContexts(), scoreDocFrom(optionES.getDoc()), searchDocument, hitEntity));
|
||||
}
|
||||
|
||||
entries.add(new CompletionSuggestion.Entry<T>(textToString(entryES.getText()), entryES.getOffset(),
|
||||
entryES.getLength(), options));
|
||||
}
|
||||
|
||||
suggestions.add(
|
||||
new CompletionSuggestion<T>(completionSuggestionES.getName(), completionSuggestionES.getSize(), entries));
|
||||
}
|
||||
}
|
||||
|
||||
return new Suggest(suggestions, suggestES.hasScoreDocs());
|
||||
}
|
||||
|
||||
private static SortBy suggestFrom(org.elasticsearch.search.suggest.SortBy sort) {
|
||||
return SortBy.valueOf(sort.name().toUpperCase());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ScoreDoc scoreDocFrom(@Nullable org.apache.lucene.search.ScoreDoc scoreDoc) {
|
||||
|
||||
if (scoreDoc == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ScoreDoc(scoreDoc.score, scoreDoc.doc, scoreDoc.shardIndex);
|
||||
}
|
||||
|
||||
private static String textToString(@Nullable Text text) {
|
||||
return text != null ? text.string() : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* A function to convert a {@link SearchDocument} async into an entity. Asynchronous so that it can be used from the
|
||||
* imperative and the reactive code.
|
||||
*
|
||||
* @param <T> the entity type
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface EntityCreator<T> extends Function<SearchDocument, CompletableFuture<T>> {}
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user