Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ecf2efa6e5 | |||
| 418d259fbf | |||
| 737a023b5a | |||
| cc3f578312 | |||
| 81b21f4e6f | |||
| 552cbff6a4 | |||
| 000eb152dc | |||
| ad55402973 | |||
| ef68623d5b | |||
| c075f170c3 | |||
| 02b7dde196 | |||
| 51b7fa2a26 | |||
| 4e5051c57b | |||
| 31d499e6e0 | |||
| 311a06aa67 |
+18
-22
@@ -1,22 +1,18 @@
|
||||
.DS_Store
|
||||
*.graphml
|
||||
.springBeans
|
||||
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
## Ignore svn files
|
||||
.svn
|
||||
|
||||
## ignore any target dir
|
||||
target
|
||||
|
||||
## Ignore project files created by Eclipse
|
||||
.settings
|
||||
.project
|
||||
.classpath
|
||||
|
||||
## Ignore project files created by IntelliJ IDEA
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
## Ignore svn files
|
||||
.svn
|
||||
|
||||
## ignore any target dir
|
||||
target
|
||||
|
||||
## Ignore project files created by Eclipse
|
||||
.settings
|
||||
.project
|
||||
.classpath
|
||||
|
||||
## Ignore project files created by IntelliJ IDEA
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea
|
||||
|
||||
Vendored
BIN
Binary file not shown.
-1
@@ -1 +0,0 @@
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip
|
||||
@@ -1,7 +0,0 @@
|
||||
dist: xenial
|
||||
|
||||
language: java
|
||||
|
||||
sudo: true
|
||||
|
||||
script: "./mvnw -Pjava11 clean dependency:list test -Dsort -U -B"
|
||||
@@ -1,43 +0,0 @@
|
||||
= Continuous Integration
|
||||
|
||||
image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-elasticsearch%2Fmaster&subject=Moore%20(master)[link=https://jenkins.spring.io/view/SpringData/job/spring-data-elasticsearch/]
|
||||
image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-elasticsearch%2F3.1.x&subject=Lovelace%20(3.1.x)[link=https://jenkins.spring.io/view/SpringData/job/spring-data-elasticsearch/]
|
||||
image:https://jenkins.spring.io/buildStatus/icon?job=spring-data-elasticsearch%2F2.1.x&subject=Ingalls%20(2.1.x)[link=https://jenkins.spring.io/view/SpringData/job/spring-data-elasticsearch/]
|
||||
|
||||
== Running CI tasks locally
|
||||
|
||||
Since this pipeline is purely Docker-based, it's easy to:
|
||||
|
||||
* Debug what went wrong on your local machine.
|
||||
* Test out a a tweak to your `test.sh` script before sending it out.
|
||||
* Experiment against a new image before submitting your pull request.
|
||||
|
||||
All of these use cases are great reasons to essentially run what the CI server does on your local machine.
|
||||
|
||||
IMPORTANT: To do this you must have Docker installed on your machine.
|
||||
|
||||
1. `docker run -it --mount type=bind,source="$(pwd)",target=/spring-data-elasticsearch-github adoptopenjdk/openjdk8:latest /bin/bash`
|
||||
+
|
||||
This will launch the Docker image and mount your source code at `spring-data-elasticsearch-github`.
|
||||
+
|
||||
2. `cd spring-data-elasticsearch-github`
|
||||
+
|
||||
Next, run your tests from inside the container:
|
||||
+
|
||||
3. `./mvnw clean dependency:list test -Dsort` (or whatever profile you need to test out)
|
||||
|
||||
Since the container is binding to your source, you can make edits from your IDE and continue to run build jobs.
|
||||
|
||||
If you need to package things up, do this:
|
||||
|
||||
1. `docker run -it --mount type=bind,source="$(pwd)",target=/spring-data-elasticsearch-github adoptopenjdk/openjdk8:latest /bin/bash`
|
||||
+
|
||||
This will launch the Docker image and mount your source code at `spring-data-elasticsearch-github`.
|
||||
+
|
||||
2. `cd spring-data-elasticsearch-github`
|
||||
+
|
||||
Next, try to package everything up from inside the container:
|
||||
+
|
||||
3. `./mvnw -Pci,snapshot -Dmaven.test.skip=true clean package`
|
||||
|
||||
NOTE: Docker containers can eat up disk space fast! From time to time, run `docker system prune` to clean out old images.
|
||||
@@ -24,4 +24,4 @@ Instances of abusive, harassing, or otherwise unacceptable behavior may be repor
|
||||
All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances.
|
||||
Maintainers are obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
|
||||
This Code of Conduct is adapted from the https://contributor-covenant.org[Contributor Covenant], version 1.3.0, available at https://contributor-covenant.org/version/1/3/0/[contributor-covenant.org/version/1/3/0/].
|
||||
This Code of Conduct is adapted from the http://contributor-covenant.org[Contributor Covenant], version 1.3.0, available at http://contributor-covenant.org/version/1/3/0/[contributor-covenant.org/version/1/3/0/].
|
||||
Vendored
-151
@@ -1,151 +0,0 @@
|
||||
pipeline {
|
||||
agent none
|
||||
|
||||
triggers {
|
||||
pollSCM 'H/10 * * * *'
|
||||
upstream(upstreamProjects: "spring-data-commons/master", threshold: hudson.model.Result.SUCCESS)
|
||||
}
|
||||
|
||||
options {
|
||||
disableConcurrentBuilds()
|
||||
buildDiscarder(logRotator(numToKeepStr: '14'))
|
||||
}
|
||||
|
||||
stages {
|
||||
stage("test: baseline (jdk8)") {
|
||||
when {
|
||||
anyOf {
|
||||
branch 'master'
|
||||
not { triggeredBy 'UpstreamCause' }
|
||||
}
|
||||
}
|
||||
agent {
|
||||
docker {
|
||||
image 'adoptopenjdk/openjdk8:latest'
|
||||
label 'data'
|
||||
args '-v $HOME:/tmp/jenkins-home'
|
||||
}
|
||||
}
|
||||
options { timeout(time: 30, unit: 'MINUTES') }
|
||||
steps {
|
||||
sh 'rm -rf ?'
|
||||
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw clean dependency:list test -Dsort -U -B'
|
||||
}
|
||||
}
|
||||
|
||||
stage("Test other configurations") {
|
||||
when {
|
||||
anyOf {
|
||||
branch 'master'
|
||||
not { triggeredBy 'UpstreamCause' }
|
||||
}
|
||||
}
|
||||
parallel {
|
||||
stage("test: baseline (jdk11)") {
|
||||
agent {
|
||||
docker {
|
||||
image 'adoptopenjdk/openjdk11:latest'
|
||||
label 'data'
|
||||
args '-v $HOME:/tmp/jenkins-home'
|
||||
}
|
||||
}
|
||||
options { timeout(time: 30, unit: 'MINUTES') }
|
||||
steps {
|
||||
sh 'rm -rf ?'
|
||||
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pjava11 clean dependency:list test -Dsort -U -B'
|
||||
}
|
||||
}
|
||||
|
||||
stage("test: baseline (jdk12)") {
|
||||
agent {
|
||||
docker {
|
||||
image 'adoptopenjdk/openjdk12:latest'
|
||||
label 'data'
|
||||
args '-v $HOME:/tmp/jenkins-home'
|
||||
}
|
||||
}
|
||||
options { timeout(time: 30, unit: 'MINUTES') }
|
||||
steps {
|
||||
sh 'rm -rf ?'
|
||||
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pjava11 clean dependency:list test -Dsort -U -B'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('Release to artifactory') {
|
||||
when {
|
||||
anyOf {
|
||||
branch 'master'
|
||||
not { triggeredBy 'UpstreamCause' }
|
||||
}
|
||||
}
|
||||
agent {
|
||||
docker {
|
||||
image 'adoptopenjdk/openjdk8:latest'
|
||||
label 'data'
|
||||
args '-v $HOME:/tmp/jenkins-home'
|
||||
}
|
||||
}
|
||||
options { timeout(time: 20, unit: 'MINUTES') }
|
||||
|
||||
environment {
|
||||
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
|
||||
}
|
||||
|
||||
steps {
|
||||
sh 'rm -rf ?'
|
||||
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pci,artifactory ' +
|
||||
'-Dartifactory.server=https://repo.spring.io ' +
|
||||
"-Dartifactory.username=${ARTIFACTORY_USR} " +
|
||||
"-Dartifactory.password=${ARTIFACTORY_PSW} " +
|
||||
"-Dartifactory.staging-repository=libs-snapshot-local " +
|
||||
"-Dartifactory.build-name=spring-data-elasticsearch " +
|
||||
"-Dartifactory.build-number=${BUILD_NUMBER} " +
|
||||
'-Dmaven.test.skip=true clean deploy -U -B'
|
||||
}
|
||||
}
|
||||
stage('Publish documentation') {
|
||||
when {
|
||||
branch 'master'
|
||||
}
|
||||
agent {
|
||||
docker {
|
||||
image 'adoptopenjdk/openjdk8:latest'
|
||||
label 'data'
|
||||
args '-v $HOME:/tmp/jenkins-home'
|
||||
}
|
||||
}
|
||||
options { timeout(time: 20, unit: 'MINUTES') }
|
||||
|
||||
environment {
|
||||
ARTIFACTORY = credentials('02bd1690-b54f-4c9f-819d-a77cb7a9822c')
|
||||
}
|
||||
|
||||
steps {
|
||||
sh 'MAVEN_OPTS="-Duser.name=jenkins -Duser.home=/tmp/jenkins-home" ./mvnw -Pci,distribute ' +
|
||||
'-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 {
|
||||
changed {
|
||||
script {
|
||||
slackSend(
|
||||
color: (currentBuild.currentResult == 'SUCCESS') ? 'good' : 'danger',
|
||||
channel: '#spring-data-dev',
|
||||
message: "${currentBuild.fullDisplayName} - `${currentBuild.currentResult}`\n${env.BUILD_URL}")
|
||||
emailext(
|
||||
subject: "[${currentBuild.fullDisplayName}] ${currentBuild.currentResult}",
|
||||
mimeType: 'text/html',
|
||||
recipientProviders: [[$class: 'CulpritsRecipientProvider'], [$class: 'RequesterRecipientProvider']],
|
||||
body: "<a href=\"${env.BUILD_URL}\">${currentBuild.fullDisplayName} is reported as ${currentBuild.currentResult}</a>")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-217
@@ -1,217 +0,0 @@
|
||||
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]]
|
||||
|
||||
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.
|
||||
|
||||
The Spring Data Elasticsearch project provides integration with the https://www.elastic.co/[Elasticsearch] search engine. Key functional areas of Spring Data Elasticsearch are a POJO centric model for interacting with a Elasticsearch Documents and easily writing a Repository style data access layer.
|
||||
|
||||
This project is lead and maintained by the community.
|
||||
|
||||
== Features
|
||||
|
||||
* Spring configuration support using Java based `@Configuration` classes or an XML namespace for a ES clients instances.
|
||||
* `ElasticsearchRestTemplate` helper class that increases productivity performing common ES operations. Includes integrated object mapping between documents and POJOs.
|
||||
* Feature Rich Object Mapping integrated with Spring’s Conversion Service
|
||||
* Annotation based mapping metadata
|
||||
* Automatic implementation of `Repository` interfaces including support for custom search methods.
|
||||
* CDI support for repositories
|
||||
|
||||
== Code of Conduct
|
||||
|
||||
This project is governed by the link:CODE_OF_CONDUCT.adoc[Spring Code of Conduct]. By participating, you are expected to uphold this code of conduct. Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.
|
||||
|
||||
== Getting Started
|
||||
|
||||
Here is a quick teaser of an application using Spring Data Repositories in Java:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
public interface PersonRepository extends CrudRepository<Person, Long> {
|
||||
|
||||
List<Person> findByLastname(String lastname);
|
||||
|
||||
List<Person> findByFirstnameLike(String firstname);
|
||||
}
|
||||
|
||||
@Service
|
||||
public class MyService {
|
||||
|
||||
private final PersonRepository repository;
|
||||
|
||||
public MyService(PersonRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public void doWork() {
|
||||
|
||||
repository.deleteAll();
|
||||
|
||||
Person person = new Person();
|
||||
person.setFirstname("Oliver");
|
||||
person.setLastname("Gierke");
|
||||
repository.save(person);
|
||||
|
||||
List<Person> lastNameResults = repository.findByLastname("Gierke");
|
||||
List<Person> firstNameResults = repository.findByFirstnameLike("Oli");
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
=== Using Transport Client
|
||||
|
||||
NOTE: Usage of the TransportClient is deprecated as of version 4.0, use RestClient instead.
|
||||
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
public class TransportClientConfig extends ElasticsearchConfigurationSupport {
|
||||
|
||||
@Bean
|
||||
public Client elasticsearchClient() throws UnknownHostException {
|
||||
Settings settings = Settings.builder().put("cluster.name", "elasticsearch").build();
|
||||
TransportClient client = new PreBuiltTransportClient(settings);
|
||||
client.addTransportAddress(new TransportAddress(InetAddress.getByName("127.0.0.1"), 9300));
|
||||
return client;
|
||||
}
|
||||
|
||||
@Bean(name = { "elasticsearchOperations", "elasticsearchTemplate" })
|
||||
public ElasticsearchTemplate elasticsearchTemplate() throws UnknownHostException {
|
||||
return new ElasticsearchTemplate(elasticsearchClient());
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
=== Using the RestClient
|
||||
|
||||
Provide a configuration like this:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
public class RestClientConfig extends AbstractElasticsearchConfiguration {
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public RestHighLevelClient elasticsearchClient() {
|
||||
|
||||
final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
|
||||
.connectedTo("localhost:9200")
|
||||
.build();
|
||||
|
||||
return RestClients.create(clientConfiguration).rest();
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
=== Maven configuration
|
||||
|
||||
Add the Maven dependency:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-elasticsearch</artifactId>
|
||||
<version>${version}.RELEASE</version>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
// NOTE: since Github does not support include directives, the content of
|
||||
// the src/main/asciidoc/reference/preface.adoc file is duplicated here
|
||||
// Always change both files!
|
||||
**Compatibility Matrix**
|
||||
|
||||
The compatibility between Spring Data Elasticsearch, Elasticsearch client drivers and Spring Boot versions can be found in the https://docs.spring.io/spring-data/elasticsearch/docs/3.2.0.RC3/reference/html/#preface.versions[reference documentation].
|
||||
|
||||
To use the Release candidate versions of the upcoming major version, use our Maven milestone repository and declare the appropriate dependency version:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-elasticsearch</artifactId>
|
||||
<version>${version}.RCx</version> <!-- x being 1, 2, ... -->
|
||||
</dependency>
|
||||
|
||||
<repository>
|
||||
<id>spring-libs-snapshot</id>
|
||||
<name>Spring Snapshot Repository</name>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
----
|
||||
|
||||
If you'd rather like the latest snapshots of the upcoming major version, use our Maven snapshot repository and declare the appropriate dependency version:
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-elasticsearch</artifactId>
|
||||
<version>${version}.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<repository>
|
||||
<id>spring-libs-snapshot</id>
|
||||
<name>Spring Snapshot Repository</name>
|
||||
<url>https://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
----
|
||||
|
||||
== Getting Help
|
||||
|
||||
Having trouble with Spring Data? We’d love to help!
|
||||
|
||||
* Check the
|
||||
https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/[reference documentation], and https://docs.spring.io/spring-data/elasticsearch/docs/current/api/[Javadocs].
|
||||
* Learn the Spring basics – Spring Data builds on Spring Framework, check the https://spring.io[spring.io] web-site for a wealth of reference documentation.
|
||||
If you are just starting out with Spring, try one of the https://spring.io/guides[guides].
|
||||
* If you are upgrading, check out the https://docs.spring.io/spring-data/elasticsearch/docs/current/changelog.txt[changelog] for "`new and noteworthy`" features.
|
||||
* Ask a question - we monitor https://stackoverflow.com[stackoverflow.com] for questions tagged with https://stackoverflow.com/tags/spring-data[`spring-data-elasticsearch`].
|
||||
You can also chat with the community on https://gitter.im/spring-projects/spring-data[Gitter].
|
||||
* Report bugs with Spring Data for Elasticsearch at https://jira.spring.io/browse/DATAES[jira.spring.io/browse/DATAES].
|
||||
|
||||
== Reporting Issues
|
||||
|
||||
Spring Data uses JIRA as issue tracking system to record bugs and feature requests. If you want to raise an issue, please follow the recommendations below:
|
||||
|
||||
* Before you log a bug, please search the
|
||||
https://jira.spring.io/browse/DATAES[issue tracker] to see if someone has already reported the problem.
|
||||
* If the issue doesn’t already exist, https://jira.spring.io/browse/DATAES[create a new issue].
|
||||
* Please provide as much information as possible with the issue report, we like to know the version of Spring Data that you are using and JVM version.
|
||||
* If you need to paste code, or include a stack trace use JIRA `{code}…{code}` escapes before and after your text.
|
||||
* If possible try to create a test-case or project that replicates the issue. Attach a link to your code or a compressed file containing your code.
|
||||
|
||||
== Building from Source
|
||||
|
||||
You don’t need to build from source to use Spring Data (binaries in https://repo.spring.io[repo.spring.io]), but if you want to try out the latest and greatest, Spring Data can be easily built with the https://github.com/takari/maven-wrapper[maven wrapper].
|
||||
You also need JDK 1.8.
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
$ ./mvnw clean install
|
||||
----
|
||||
|
||||
If you want to build with the regular `mvn` command, you will need https://maven.apache.org/run-maven/index.html[Maven v3.5.0 or above].
|
||||
|
||||
_Also see link:CONTRIBUTING.adoc[CONTRIBUTING.adoc] if you wish to submit pull requests, and in particular please sign the https://cla.pivotal.io/sign/spring[Contributor’s Agreement] before submitting your first pull request._
|
||||
|
||||
=== Building reference documentation
|
||||
|
||||
Building the documentation builds also the project without running tests.
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
$ ./mvnw clean install -Pdistribute
|
||||
----
|
||||
|
||||
The generated documentation is available from `target/site/reference/html/index.html`.
|
||||
|
||||
== 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.
|
||||
|
||||
== License
|
||||
|
||||
Spring Data for Elasticsearch Open Source software released under the https://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 license].
|
||||
@@ -0,0 +1,255 @@
|
||||
Spring Data Elasticsearch
|
||||
=========================
|
||||
|
||||
Spring Data implementation for ElasticSearch
|
||||
|
||||
Spring Data makes 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 as well as provide improved support for relational database technologies.
|
||||
|
||||
The Spring Data Elasticsearch project provides integration with the [elasticsearch](http://www.elasticsearch.org/) search engine.
|
||||
|
||||
Guide
|
||||
------------
|
||||
|
||||
* [Reference Documentation](http://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/)
|
||||
* [API Documentation](http://docs.spring.io/spring-data/elasticsearch/docs/current/api/)
|
||||
* [Spring Data Project](http://projects.spring.io/spring-data)
|
||||
* [Sample Test Application](https://github.com/BioMedCentralLtd/spring-data-elasticsearch-sample-application)
|
||||
* [Issues](https://jira.springsource.org/browse/DATAES)
|
||||
* [Spring Data Elasticsearch Google Group](https://groups.google.com/d/forum/spring-data-elasticsearch-devs)
|
||||
* [Questions](http://stackoverflow.com/questions/tagged/spring-data-elasticsearch)
|
||||
|
||||
|
||||
Quick Start
|
||||
-----------
|
||||
Wiki page for [Getting Started](https://github.com/spring-projects/spring-data-elasticsearch/wiki/How-to-start-with-spring-data-elasticsearch)
|
||||
|
||||
### Maven configuration
|
||||
|
||||
Add the Maven dependency:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-elasticsearch</artifactId>
|
||||
<version>x.y.z.RELEASE</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
If you'd rather like the latest snapshots of the upcoming major version, use our Maven snapshot repository and declare
|
||||
the appropriate dependency version.
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-elasticsearch</artifactId>
|
||||
<version>x.y.z.BUILD-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<repository>
|
||||
<id>spring-libs-snapshot</id>
|
||||
<name>Spring Snapshot Repository</name>
|
||||
<url>http://repo.spring.io/libs-snapshot</url>
|
||||
</repository>
|
||||
```
|
||||
|
||||
| spring data elasticsearch | elasticsearch |
|
||||
|:-----------------------------------:|:-------------:|
|
||||
| 3.0.0.RC2 | 5.5.0 |
|
||||
| 3.0.0.M4 | 5.4.0 |
|
||||
| 2.0.4.RELEASE | 2.4.0 |
|
||||
| 2.0.0.RELEASE | 2.2.0 |
|
||||
| 1.4.0.M1 | 1.7.3 |
|
||||
| 1.3.0.RELEASE | 1.5.2 |
|
||||
| 1.2.0.RELEASE | 1.4.4 |
|
||||
| 1.1.0.RELEASE | 1.3.2 |
|
||||
| 1.0.0.RELEASE | 1.1.1 |
|
||||
|
||||
|
||||
### ElasticsearchRepository
|
||||
A default implementation of ElasticsearchRepository, aligning to the generic Repository Interfaces, is provided. Spring can do the Repository implementation for you depending on method names in the interface definition.
|
||||
|
||||
The ElasticsearchCrudRepository extends PagingAndSortingRepository
|
||||
|
||||
```java
|
||||
public interface ElasticsearchCrudRepository<T, ID extends Serializable> extends ElasticsearchRepository<T, ID>, PagingAndSortingRepository<T, ID> {
|
||||
}
|
||||
```
|
||||
|
||||
Extending ElasticsearchRepository for custom methods
|
||||
|
||||
```java
|
||||
public interface BookRepository extends Repository<Book, String> {
|
||||
|
||||
List<Book> findByNameAndPrice(String name, Integer price);
|
||||
|
||||
List<Book> findByNameOrPrice(String name, Integer price);
|
||||
|
||||
Page<Book> findByName(String name,Pageable page);
|
||||
|
||||
Page<Book> findByNameNot(String name,Pageable page);
|
||||
|
||||
Page<Book> findByPriceBetween(int price,Pageable page);
|
||||
|
||||
Page<Book> findByNameLike(String name,Pageable page);
|
||||
|
||||
@Query("{\"bool\" : {\"must\" : {\"term\" : {\"message\" : \"?0\"}}}}")
|
||||
Page<Book> findByMessage(String message, Pageable pageable);
|
||||
}
|
||||
```
|
||||
Indexing a single document with Repository
|
||||
|
||||
```java
|
||||
@Autowired
|
||||
private SampleElasticsearchRepository repository;
|
||||
|
||||
String documentId = "123456";
|
||||
SampleEntity sampleEntity = new SampleEntity();
|
||||
sampleEntity.setId(documentId);
|
||||
sampleEntity.setMessage("some message");
|
||||
|
||||
repository.save(sampleEntity);
|
||||
```
|
||||
|
||||
Indexing multiple Document(bulk index) using Repository
|
||||
|
||||
```java
|
||||
@Autowired
|
||||
private SampleElasticsearchRepository repository;
|
||||
|
||||
String documentId = "123456";
|
||||
SampleEntity sampleEntity1 = new SampleEntity();
|
||||
sampleEntity1.setId(documentId);
|
||||
sampleEntity1.setMessage("some message");
|
||||
|
||||
String documentId2 = "123457"
|
||||
SampleEntity sampleEntity2 = new SampleEntity();
|
||||
sampleEntity2.setId(documentId2);
|
||||
sampleEntity2.setMessage("test message");
|
||||
|
||||
List<SampleEntity> sampleEntities = Arrays.asList(sampleEntity1, sampleEntity2);
|
||||
|
||||
//bulk index
|
||||
repository.save(sampleEntities);
|
||||
```
|
||||
|
||||
|
||||
### ElasticsearchTemplate
|
||||
|
||||
ElasticsearchTemplate is the central support class for elasticsearch operations.
|
||||
|
||||
Indexing a single document using Elasticsearch Template
|
||||
|
||||
```java
|
||||
String documentId = "123456";
|
||||
SampleEntity sampleEntity = new SampleEntity();
|
||||
sampleEntity.setId(documentId);
|
||||
sampleEntity.setMessage("some message");
|
||||
IndexQuery indexQuery = new IndexQueryBuilder().withId(sampleEntity.getId()).withObject(sampleEntity).build();
|
||||
elasticsearchTemplate.index(indexQuery);
|
||||
```
|
||||
|
||||
Indexing multiple Document(bulk index) using Elasticsearch Template
|
||||
|
||||
```java
|
||||
@Autowired
|
||||
private ElasticsearchTemplate elasticsearchTemplate;
|
||||
|
||||
List<IndexQuery> indexQueries = new ArrayList<IndexQuery>();
|
||||
//first document
|
||||
String documentId = "123456";
|
||||
SampleEntity sampleEntity1 = new SampleEntity();
|
||||
sampleEntity1.setId(documentId);
|
||||
sampleEntity1.setMessage("some message");
|
||||
|
||||
IndexQuery indexQuery1 = new IndexQueryBuilder().withId(sampleEntity1.getId()).withObject(sampleEntity1).build();
|
||||
indexQueries.add(indexQuery1);
|
||||
|
||||
//second document
|
||||
String documentId2 = "123457";
|
||||
SampleEntity sampleEntity2 = new SampleEntity();
|
||||
sampleEntity2.setId(documentId2);
|
||||
sampleEntity2.setMessage("some message");
|
||||
|
||||
IndexQuery indexQuery2 = new IndexQueryBuilder().withId(sampleEntity2.getId()).withObject(sampleEntity2).build()
|
||||
indexQueries.add(indexQuery2);
|
||||
|
||||
//bulk index
|
||||
elasticsearchTemplate.bulkIndex(indexQueries);
|
||||
```
|
||||
|
||||
Searching entities using Elasticsearch Template
|
||||
|
||||
```java
|
||||
@Autowired
|
||||
private ElasticsearchTemplate elasticsearchTemplate;
|
||||
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder()
|
||||
.withQuery(queryString(documentId).field("id"))
|
||||
.build();
|
||||
Page<SampleEntity> sampleEntities = elasticsearchTemplate.queryForPage(searchQuery,SampleEntity.class);
|
||||
```
|
||||
|
||||
### XML Namespace
|
||||
|
||||
You can set up repository scanning via xml configuration, which will happily create your repositories.
|
||||
|
||||
Using Node Client
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:elasticsearch="http://www.springframework.org/schema/data/elasticsearch"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/data/elasticsearch http://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<elasticsearch:node-client id="client" local="true"/>
|
||||
|
||||
<bean name="elasticsearchTemplate" class="org.springframework.data.elasticsearch.core.ElasticsearchTemplate">
|
||||
<constructor-arg name="client" ref="client"/>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
```
|
||||
|
||||
Using Transport Client
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:elasticsearch="http://www.springframework.org/schema/data/elasticsearch"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/data/elasticsearch http://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<elasticsearch:repositories base-package="com.xyz.acme"/>
|
||||
|
||||
<elasticsearch:transport-client id="client" cluster-nodes="ip:9300,ip:9300" cluster-name="elasticsearch" />
|
||||
|
||||
<bean name="elasticsearchTemplate" class="org.springframework.data.elasticsearch.core.ElasticsearchTemplate">
|
||||
<constructor-arg name="client" ref="client"/>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
```
|
||||
|
||||
## Help Pages
|
||||
|
||||
* [Geo distance and location search](https://github.com/spring-projects/spring-data-elasticsearch/wiki/Geo-indexing-and-request)
|
||||
* [Custom object mapper](https://github.com/spring-projects/spring-data-elasticsearch/wiki/Custom-ObjectMapper)
|
||||
|
||||
## Contributing to Spring Data
|
||||
|
||||
Here are some ways for you to get involved in the community:
|
||||
|
||||
* Get involved with the Spring community on Stack OverFlow. Please help out on the [forum](https://stackoverflow.com/questions/tagged/spring-data-elasticsearch) by responding to questions and joining the debate.
|
||||
* Create [JIRA](https://jira.spring.io/browse/DATAES/) tickets for bugs and new features and comment and vote on the ones that you are interested in.
|
||||
* Github is for social coding: if you want to write code, we encourage contributions through pull requests from [forks of this repository](http://help.github.com/forking/). If you want to contribute code this way, please reference a JIRA ticket as well covering the specific issue you are addressing.
|
||||
* Watch for upcoming articles on Spring by [subscribing](http://www.springsource.org/node/feed) to springframework.org
|
||||
|
||||
Before we accept a non-trivial patch or pull request we will need you to [sign the Contributor License Agreement](https://cla.pivotal.io/sign/spring). Signing the contributor’s agreement does not grant anyone commit rights to the main repository, but it does mean that we can accept your contributions, and you will get an author credit if we do. If you forget to do so, you'll be reminded when you submit a pull request. Active contributors might be asked to join the core team, and given the ability to merge pull requests.
|
||||
|
||||
|
||||
Code formatting for [Eclipse and Intellij](https://github.com/spring-projects/spring-data-build/tree/master/etc/ide)
|
||||
|
||||
[More information about contributing to Spring Data](https://github.com/spring-projects/spring-data-build/blob/master/CONTRIBUTING.adoc)
|
||||
@@ -1,9 +0,0 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Please see the https://spring.io/projects/spring-data-elasticsearch[Spring Data Elasticsearch] project page for supported versions.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please don't raise security vulnerabilities here. Head over to https://pivotal.io/security to learn how to disclose them responsibly.
|
||||
@@ -1,286 +0,0 @@
|
||||
#!/bin/sh
|
||||
# ----------------------------------------------------------------------------
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you 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.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Maven2 Start Up Batch script
|
||||
#
|
||||
# Required ENV vars:
|
||||
# ------------------
|
||||
# JAVA_HOME - location of a JDK home dir
|
||||
#
|
||||
# Optional ENV vars
|
||||
# -----------------
|
||||
# M2_HOME - location of maven2's installed home dir
|
||||
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
# e.g. to debug Maven itself, use
|
||||
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
if [ -z "$MAVEN_SKIP_RC" ] ; then
|
||||
|
||||
if [ -f /etc/mavenrc ] ; then
|
||||
. /etc/mavenrc
|
||||
fi
|
||||
|
||||
if [ -f "$HOME/.mavenrc" ] ; then
|
||||
. "$HOME/.mavenrc"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# OS specific support. $var _must_ be set to either true or false.
|
||||
cygwin=false;
|
||||
darwin=false;
|
||||
mingw=false
|
||||
case "`uname`" in
|
||||
CYGWIN*) cygwin=true ;;
|
||||
MINGW*) mingw=true;;
|
||||
Darwin*) darwin=true
|
||||
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
|
||||
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
if [ -x "/usr/libexec/java_home" ]; then
|
||||
export JAVA_HOME="`/usr/libexec/java_home`"
|
||||
else
|
||||
export JAVA_HOME="/Library/Java/Home"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
if [ -r /etc/gentoo-release ] ; then
|
||||
JAVA_HOME=`java-config --jre-home`
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$M2_HOME" ] ; then
|
||||
## resolve links - $0 may be a link to maven's home
|
||||
PRG="$0"
|
||||
|
||||
# need this for relative symlinks
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG="`dirname "$PRG"`/$link"
|
||||
fi
|
||||
done
|
||||
|
||||
saveddir=`pwd`
|
||||
|
||||
M2_HOME=`dirname "$PRG"`/..
|
||||
|
||||
# make it fully qualified
|
||||
M2_HOME=`cd "$M2_HOME" && pwd`
|
||||
|
||||
cd "$saveddir"
|
||||
# echo Using m2 at $M2_HOME
|
||||
fi
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||
if $cygwin ; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME=`cygpath --unix "$M2_HOME"`
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
|
||||
fi
|
||||
|
||||
# For Mingw, ensure paths are in UNIX format before anything is touched
|
||||
if $mingw ; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME="`(cd "$M2_HOME"; pwd)`"
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
|
||||
# TODO classpath?
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
javaExecutable="`which javac`"
|
||||
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
|
||||
# readlink(1) is not available as standard on Solaris 10.
|
||||
readLink=`which readlink`
|
||||
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
|
||||
if $darwin ; then
|
||||
javaHome="`dirname \"$javaExecutable\"`"
|
||||
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
|
||||
else
|
||||
javaExecutable="`readlink -f \"$javaExecutable\"`"
|
||||
fi
|
||||
javaHome="`dirname \"$javaExecutable\"`"
|
||||
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
|
||||
JAVA_HOME="$javaHome"
|
||||
export JAVA_HOME
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$JAVACMD" ] ; then
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
else
|
||||
JAVACMD="`which java`"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
echo "Error: JAVA_HOME is not defined correctly." >&2
|
||||
echo " We cannot execute $JAVACMD" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
echo "Warning: JAVA_HOME environment variable is not set."
|
||||
fi
|
||||
|
||||
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
|
||||
|
||||
# traverses directory structure from process work directory to filesystem root
|
||||
# first directory with .mvn subdirectory is considered project base directory
|
||||
find_maven_basedir() {
|
||||
|
||||
if [ -z "$1" ]
|
||||
then
|
||||
echo "Path not specified to find_maven_basedir"
|
||||
return 1
|
||||
fi
|
||||
|
||||
basedir="$1"
|
||||
wdir="$1"
|
||||
while [ "$wdir" != '/' ] ; do
|
||||
if [ -d "$wdir"/.mvn ] ; then
|
||||
basedir=$wdir
|
||||
break
|
||||
fi
|
||||
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
|
||||
if [ -d "${wdir}" ]; then
|
||||
wdir=`cd "$wdir/.."; pwd`
|
||||
fi
|
||||
# end of workaround
|
||||
done
|
||||
echo "${basedir}"
|
||||
}
|
||||
|
||||
# concatenates all lines of a file
|
||||
concat_lines() {
|
||||
if [ -f "$1" ]; then
|
||||
echo "$(tr -s '\n' ' ' < "$1")"
|
||||
fi
|
||||
}
|
||||
|
||||
BASE_DIR=`find_maven_basedir "$(pwd)"`
|
||||
if [ -z "$BASE_DIR" ]; then
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
##########################################################################################
|
||||
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
|
||||
# This allows using the maven wrapper in projects that prohibit checking in binary data.
|
||||
##########################################################################################
|
||||
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Found .mvn/wrapper/maven-wrapper.jar"
|
||||
fi
|
||||
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"
|
||||
while IFS="=" read key value; do
|
||||
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
|
||||
esac
|
||||
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Downloading from: $jarUrl"
|
||||
fi
|
||||
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
|
||||
|
||||
if command -v wget > /dev/null; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Found wget ... using wget"
|
||||
fi
|
||||
wget "$jarUrl" -O "$wrapperJarPath"
|
||||
elif command -v curl > /dev/null; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Found curl ... using curl"
|
||||
fi
|
||||
curl -o "$wrapperJarPath" "$jarUrl"
|
||||
else
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo "Falling back to using Java to download"
|
||||
fi
|
||||
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
|
||||
if [ -e "$javaClass" ]; then
|
||||
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo " - Compiling MavenWrapperDownloader.java ..."
|
||||
fi
|
||||
# Compiling the Java class
|
||||
("$JAVA_HOME/bin/javac" "$javaClass")
|
||||
fi
|
||||
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
|
||||
# Running the downloader
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo " - Running MavenWrapperDownloader.java ..."
|
||||
fi
|
||||
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
##########################################################################################
|
||||
# End of extension
|
||||
##########################################################################################
|
||||
|
||||
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
|
||||
if [ "$MVNW_VERBOSE" = true ]; then
|
||||
echo $MAVEN_PROJECTBASEDIR
|
||||
fi
|
||||
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME=`cygpath --path --windows "$M2_HOME"`
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
|
||||
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
|
||||
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
|
||||
fi
|
||||
|
||||
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
exec "$JAVACMD" \
|
||||
$MAVEN_OPTS \
|
||||
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
||||
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
||||
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
|
||||
@@ -1,161 +0,0 @@
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||
@REM or more contributor license agreements. See the NOTICE file
|
||||
@REM distributed with this work for additional information
|
||||
@REM regarding copyright ownership. The ASF licenses this file
|
||||
@REM to you under the Apache License, Version 2.0 (the
|
||||
@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
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@REM KIND, either express or implied. See the License for the
|
||||
@REM specific language governing permissions and limitations
|
||||
@REM under the License.
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Maven2 Start Up Batch script
|
||||
@REM
|
||||
@REM Required ENV vars:
|
||||
@REM JAVA_HOME - location of a JDK home dir
|
||||
@REM
|
||||
@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_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
|
||||
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
||||
@echo off
|
||||
@REM set title of command window
|
||||
title %0
|
||||
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
|
||||
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
||||
|
||||
@REM set %HOME% to equivalent of $HOME
|
||||
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
|
||||
|
||||
@REM Execute a user defined script before this one
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
|
||||
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
|
||||
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
|
||||
:skipRcPre
|
||||
|
||||
@setlocal
|
||||
|
||||
set ERROR_CODE=0
|
||||
|
||||
@REM To isolate internal variables from possible post scripts, we use another setlocal
|
||||
@setlocal
|
||||
|
||||
@REM ==== START VALIDATION ====
|
||||
if not "%JAVA_HOME%" == "" goto OkJHome
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME not found in your environment. >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
:OkJHome
|
||||
if exist "%JAVA_HOME%\bin\java.exe" goto init
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME is set to an invalid directory. >&2
|
||||
echo JAVA_HOME = "%JAVA_HOME%" >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
@REM ==== END VALIDATION ====
|
||||
|
||||
:init
|
||||
|
||||
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
|
||||
@REM Fallback to current working directory if not found.
|
||||
|
||||
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
|
||||
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
|
||||
|
||||
set EXEC_DIR=%CD%
|
||||
set WDIR=%EXEC_DIR%
|
||||
:findBaseDir
|
||||
IF EXIST "%WDIR%"\.mvn goto baseDirFound
|
||||
cd ..
|
||||
IF "%WDIR%"=="%CD%" goto baseDirNotFound
|
||||
set WDIR=%CD%
|
||||
goto findBaseDir
|
||||
|
||||
:baseDirFound
|
||||
set MAVEN_PROJECTBASEDIR=%WDIR%
|
||||
cd "%EXEC_DIR%"
|
||||
goto endDetectBaseDir
|
||||
|
||||
:baseDirNotFound
|
||||
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
|
||||
cd "%EXEC_DIR%"
|
||||
|
||||
:endDetectBaseDir
|
||||
|
||||
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
|
||||
|
||||
@setlocal EnableExtensions EnableDelayedExpansion
|
||||
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
|
||||
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
|
||||
|
||||
:endReadAdditionalConfig
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
@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%
|
||||
) 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%
|
||||
)
|
||||
@REM End of extension
|
||||
|
||||
%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
|
||||
|
||||
:error
|
||||
set ERROR_CODE=1
|
||||
|
||||
:end
|
||||
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
||||
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
|
||||
@REM check for post script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
|
||||
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
|
||||
:skipRcPost
|
||||
|
||||
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
|
||||
if "%MAVEN_BATCH_PAUSE%" == "on" pause
|
||||
|
||||
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
|
||||
|
||||
exit /B %ERROR_CODE%
|
||||
@@ -1,370 +1,255 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-elasticsearch</artifactId>
|
||||
<version>3.0.2.RELEASE</version>
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-elasticsearch</artifactId>
|
||||
<version>4.0.0.M4</version>
|
||||
<parent>
|
||||
<groupId>org.springframework.data.build</groupId>
|
||||
<artifactId>spring-data-parent</artifactId>
|
||||
<version>2.0.2.RELEASE</version>
|
||||
</parent>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.build</groupId>
|
||||
<artifactId>spring-data-parent</artifactId>
|
||||
<version>2.3.0.M4</version>
|
||||
</parent>
|
||||
<name>Spring Data Elasticsearch</name>
|
||||
<description>Spring Data Implementation for Elasticsearch</description>
|
||||
<url>https://github.com/spring-projects/spring-data-elasticsearch</url>
|
||||
|
||||
<name>Spring Data Elasticsearch</name>
|
||||
<description>Spring Data Implementation for Elasticsearch</description>
|
||||
<url>https://github.com/spring-projects/spring-data-elasticsearch</url>
|
||||
<properties>
|
||||
<commonscollections>3.2.1</commonscollections>
|
||||
<commonslang>2.6</commonslang>
|
||||
<elasticsearch>5.5.0</elasticsearch>
|
||||
<log4j>2.8.2</log4j>
|
||||
<springdata.commons>2.0.2.RELEASE</springdata.commons>
|
||||
<java-module-name>spring.data.elasticsearch</java-module-name>
|
||||
</properties>
|
||||
|
||||
<properties>
|
||||
<commonslang>2.6</commonslang>
|
||||
<elasticsearch>7.6.1</elasticsearch>
|
||||
<log4j>2.9.1</log4j>
|
||||
<springdata.commons>2.3.0.M4</springdata.commons>
|
||||
<netty>4.1.39.Final</netty>
|
||||
<java-module-name>spring.data.elasticsearch</java-module-name>
|
||||
</properties>
|
||||
<dependencies>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<id>biomedcentral</id>
|
||||
<name>BioMed Central Development Team</name>
|
||||
<timezone>+0</timezone>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>cstrobl</id>
|
||||
<name>Christoph Strobl</name>
|
||||
<email>cstrobl at pivotal.io</email>
|
||||
<organization>Pivotal</organization>
|
||||
<organizationUrl>https://www.pivotal.io</organizationUrl>
|
||||
<roles>
|
||||
<role>Developer</role>
|
||||
</roles>
|
||||
<timezone>+1</timezone>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>mpaluch</id>
|
||||
<name>Mark Paluch</name>
|
||||
<email>mpaluch at pivotal.io</email>
|
||||
<organization>Pivotal</organization>
|
||||
<organizationUrl>https://www.pivotal.io</organizationUrl>
|
||||
<roles>
|
||||
<role>Developer</role>
|
||||
</roles>
|
||||
<timezone>+1</timezone>
|
||||
</developer>
|
||||
</developers>
|
||||
<!-- Spring -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<scm>
|
||||
<url>https://github.com/spring-projects/spring-data-elasticsearch</url>
|
||||
<connection>scm:git:git://github.com/spring-projects/spring-data-elasticsearch.git</connection>
|
||||
<developerConnection>scm:git:ssh://git@github.com/spring-projects/spring-data-elasticsearch.git
|
||||
</developerConnection>
|
||||
</scm>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tx</artifactId>
|
||||
</dependency>
|
||||
|
||||
<ciManagement>
|
||||
<system>Bamboo</system>
|
||||
<url>https://build.spring.io/browse/SPRINGDATAES</url>
|
||||
</ciManagement>
|
||||
<!-- SPRING DATA -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-commons</artifactId>
|
||||
<version>${springdata.commons}</version>
|
||||
</dependency>
|
||||
|
||||
<issueManagement>
|
||||
<system>JIRA</system>
|
||||
<url>https://jira.spring.io/browse/DATAES</url>
|
||||
</issueManagement>
|
||||
<!-- APACHE -->
|
||||
<dependency>
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
<version>${commonslang}</version>
|
||||
</dependency>
|
||||
|
||||
<dependencyManagement>
|
||||
<!-- JODA Time -->
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
<artifactId>joda-time</artifactId>
|
||||
<version>${jodatime}</version>
|
||||
</dependency>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-bom</artifactId>
|
||||
<version>${netty}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<!-- Elasticsearch -->
|
||||
<dependency>
|
||||
<groupId>org.elasticsearch</groupId>
|
||||
<artifactId>elasticsearch</artifactId>
|
||||
<version>${elasticsearch}</version>
|
||||
</dependency>
|
||||
|
||||
<dependencies>
|
||||
<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>
|
||||
|
||||
<!-- Spring -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>log4j-over-slf4j</artifactId>
|
||||
<version>${slf4j}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-tx</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>${log4j}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- SPRING DATA -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-commons</artifactId>
|
||||
<version>${springdata.commons}</version>
|
||||
</dependency>
|
||||
<!-- Jackson JSON Mapper -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>${jackson}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>${jackson}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Reactive Infrastructure -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webflux</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- CDI -->
|
||||
<dependency>
|
||||
<groupId>javax.enterprise</groupId>
|
||||
<artifactId>cdi-api</artifactId>
|
||||
<version>${cdi}</version>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.projectreactor.netty</groupId>
|
||||
<artifactId>reactor-netty</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
<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>
|
||||
|
||||
<!-- APACHE -->
|
||||
<dependency>
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
<version>${commonslang}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- Upgrade xbean to 4.5 to prevent incompatibilities due to ASM versions -->
|
||||
<dependency>
|
||||
<groupId>org.apache.xbean</groupId>
|
||||
<artifactId>xbean-asm5-shaded</artifactId>
|
||||
<version>4.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- JODA Time -->
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
<artifactId>joda-time</artifactId>
|
||||
<version>${jodatime}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
<version>3.0-alpha-1</version>
|
||||
<scope>test</scope>
|
||||
</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>
|
||||
<!-- required by elasticsearch -->
|
||||
<groupId>org.elasticsearch.plugin</groupId>
|
||||
<artifactId>transport-netty4-client</artifactId>
|
||||
<version>${elasticsearch}</version>
|
||||
<!--<scope>test</scope>-->
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<!-- required by elasticsearch -->
|
||||
<groupId>org.elasticsearch.plugin</groupId>
|
||||
<artifactId>transport-netty4-client</artifactId>
|
||||
<version>${elasticsearch}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.elasticsearch.client</groupId>
|
||||
<artifactId>elasticsearch-rest-high-level-client</artifactId>
|
||||
<version>${elasticsearch}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>log4j-over-slf4j</artifactId>
|
||||
<version>${slf4j}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>wagon-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.asciidoctor</groupId>
|
||||
<artifactId>asciidoctor-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>${log4j}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jfrog.buildinfo</groupId>
|
||||
<artifactId>artifactory-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
<!-- Jackson JSON Mapper -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<developers>
|
||||
<developer>
|
||||
<id>biomedcentral</id>
|
||||
<name>BioMed Central Development Team</name>
|
||||
<timezone>+0</timezone>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<!-- CDI -->
|
||||
<dependency>
|
||||
<groupId>javax.enterprise</groupId>
|
||||
<artifactId>cdi-api</artifactId>
|
||||
<version>${cdi}</version>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-libs-release</id>
|
||||
<url>https://repo.spring.io/libs-release</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>spring-plugins-release</id>
|
||||
<url>https://repo.spring.io/plugins-release</url>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
|
||||
<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>
|
||||
<scm>
|
||||
<url>https://github.com/spring-projects/spring-data-elasticsearch</url>
|
||||
<connection>scm:git:git://github.com/spring-projects/spring-data-elasticsearch.git</connection>
|
||||
<developerConnection>scm:git:ssh://git@github.com/spring-projects/spring-data-elasticsearch.git
|
||||
</developerConnection>
|
||||
</scm>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.skyscreamer</groupId>
|
||||
<artifactId>jsonassert</artifactId>
|
||||
<version>1.5.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<ciManagement>
|
||||
<system>Bamboo</system>
|
||||
<url>https://build.spring.io/browse/SPRINGDATAES</url>
|
||||
</ciManagement>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.tomakehurst</groupId>
|
||||
<artifactId>wiremock-jre8</artifactId>
|
||||
<version>2.25.1</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<!-- these exclusions are needed because of Elasticsearch JarHell-->
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Upgrade xbean to 4.5 to prevent incompatibilities due to ASM versions -->
|
||||
<dependency>
|
||||
<groupId>org.apache.xbean</groupId>
|
||||
<artifactId>xbean-asm5-shaded</artifactId>
|
||||
<version>4.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-junit-jupiter</artifactId>
|
||||
<version>${mockito}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<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>
|
||||
<configuration>
|
||||
<useSystemClassLoader>true</useSystemClassLoader>
|
||||
<useFile>false</useFile>
|
||||
<includes>
|
||||
<include>**/*Tests.java</include>
|
||||
</includes>
|
||||
<systemPropertyVariables>
|
||||
<es.set.netty.runtime.available.processors>false</es.set.netty.runtime.available.processors>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.asciidoctor</groupId>
|
||||
<artifactId>asciidoctor-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
|
||||
<id>ci</id>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<configuration>
|
||||
<checkstyleRules>
|
||||
<module name="Checker">
|
||||
|
||||
<!-- Configure checker to use UTF-8 encoding -->
|
||||
<property name="charset" value="UTF-8"/>
|
||||
|
||||
<module name="io.spring.nohttp.checkstyle.check.NoHttpCheck"/>
|
||||
</module>
|
||||
</checkstyleRules>
|
||||
<includes>**/*</includes>
|
||||
<excludes>.git/**/*,target/**/*,**/target/**/*,.idea/**/*,**/spring.schemas,**/*.svg,mvnw,mvnw.cmd,**/*.policy</excludes>
|
||||
<sourceDirectories>./</sourceDirectories>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</profile>
|
||||
|
||||
</profiles>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-libs-milestone</id>
|
||||
<url>https://repo.spring.io/libs-milestone</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>spring-plugins-release</id>
|
||||
<url>https://repo.spring.io/plugins-release</url>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
<issueManagement>
|
||||
<system>JIRA</system>
|
||||
<url>https://jira.spring.io/browse/DATAES</url>
|
||||
</issueManagement>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
= Spring Data Elasticsearch - Reference Documentation
|
||||
BioMed Central Development Team; Oliver Drotbohm; Greg Turnquist; Christoph Strobl; Peter-Josef Meisch
|
||||
= Spring Data Elasticsearch
|
||||
BioMed Central Development Team
|
||||
:revnumber: {version}
|
||||
:revdate: {localdate}
|
||||
ifdef::backend-epub3[:front-cover-image: image:epub-cover.png[Front Cover,1050,1600]]
|
||||
:toc:
|
||||
:toc-placement!:
|
||||
:spring-data-commons-docs: ../../../../spring-data-commons/src/main/asciidoc
|
||||
|
||||
(C) 2013-2019 The original author(s).
|
||||
(C) 2013-2015 The original author(s).
|
||||
|
||||
NOTE: Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.
|
||||
|
||||
@@ -20,10 +21,7 @@ include::{spring-data-commons-docs}/repositories.adoc[]
|
||||
= Reference Documentation
|
||||
|
||||
:leveloffset: +1
|
||||
include::reference/elasticsearch-clients.adoc[]
|
||||
include::reference/elasticsearch-object-mapping.adoc[]
|
||||
include::reference/elasticsearch-operations.adoc[]
|
||||
include::reference/elasticsearch-repositories.adoc[]
|
||||
include::reference/data-elasticsearch.adoc[]
|
||||
include::reference/elasticsearch-misc.adoc[]
|
||||
:leveloffset: -1
|
||||
|
||||
|
||||
@@ -1,45 +1,20 @@
|
||||
[[preface]]
|
||||
= Preface
|
||||
|
||||
The Spring Data Elasticsearch project applies core Spring concepts to the development of solutions using the Elasticsearch Search Engine. It provides:
|
||||
The Spring Data Elasticsearch project applies core Spring concepts to the development of solutions using the Elasticsearch Search Engine. We have povided a "template" as a high-level abstraction for storing,querying,sorting and faceting documents. You will notice similarities to the Spring data solr and mongodb support in the Spring Framework.
|
||||
|
||||
* _Templates_ as a high-level abstraction for storing, searching, sorting documents and building aggregations.
|
||||
* _Repositories_ which for example enable the user to express queries by defining interfaces having customized method names (for basic information about repositories see <<repositories>>).
|
||||
|
||||
You will notice similarities to the Spring data solr and mongodb support in the Spring Framework.
|
||||
|
||||
include::reference/elasticsearch-new.adoc[leveloffset=+1]
|
||||
|
||||
[[preface.metadata]]
|
||||
[[project]]
|
||||
[preface]
|
||||
== Project Metadata
|
||||
|
||||
* Version Control - https://github.com/spring-projects/spring-data-elasticsearch
|
||||
* API Documentation - https://docs.spring.io/spring-data/elasticsearch/docs/current/api/
|
||||
* Bugtracker - https://jira.spring.io/browse/DATAES
|
||||
* Release repository - https://repo.spring.io/libs-release
|
||||
* Milestone repository - https://repo.spring.io/libs-milestone
|
||||
* Snapshot repository - https://repo.spring.io/libs-snapshot
|
||||
|
||||
[[preface.requirements]]
|
||||
[[requirements]]
|
||||
[preface]
|
||||
== Requirements
|
||||
|
||||
Requires an installation of https://www.elastic.co/products/elasticsearch[Elasticsearch].
|
||||
Requires http://www.elasticsearch.org/download/[Elasticsearch] 0.20.2 and above or optional dependency or not even that if you are using Embedded Node Client
|
||||
|
||||
[[preface.versions]]
|
||||
=== Versions
|
||||
// NOTE: since Github does not support include directives, the content of
|
||||
// this file is duplicated in the toplevel README
|
||||
// Always change both files!
|
||||
|
||||
The following table shows the Elasticsearch versions that are used by Spring Data release trains and version of Spring Data Elasticsearch included in that, as well as the Spring Boot versions refering to that particular Spring Data release train:
|
||||
[cols="^,^,^,^",options="header"]
|
||||
|===
|
||||
| Spring Data Release Train |Spring Data Elasticsearch |Elasticsearch | Spring Boot
|
||||
| Neumannfootnote:cdv[Currently in development] |4.0.xfootnote:cdv[]|7.6.1 |2.3.xfootnote:cdv[]
|
||||
| Moore | 3.2.x |6.8.6 | 2.2.x
|
||||
| Lovelace | 3.1.x | 6.2.2 |2.1.x
|
||||
| Kayfootnote:oom[Out of maintenance] | 3.0.xfootnote:oom[] | 5.5.0 | 2.0.xfootnote:oom[]
|
||||
| Ingallsfootnote:oom[] | 2.1.xfootnote:oom[] | 2.4.0 | 1.5.xfootnote:oom[]
|
||||
|===
|
||||
|
||||
Support for upcoming versions of Elasticsearch is being tracked and general compatibility should be given assuming the usage of the <<elasticsearch.clients.rest,high-level REST client>>.
|
||||
|
||||
@@ -0,0 +1,288 @@
|
||||
[[elasticsearch.repositories]]
|
||||
= Elasticsearch Repositories
|
||||
|
||||
This chapter includes details of the Elasticsearch repository implementation.
|
||||
|
||||
[[elasticsearch.introduction]]
|
||||
== Introduction
|
||||
|
||||
[[elasticsearch.namespace]]
|
||||
=== Spring Namespace
|
||||
|
||||
The Spring Data Elasticsearch module contains a custom namespace allowing definition of repository beans as well as elements for instantiating a `ElasticsearchServer` .
|
||||
|
||||
Using the `repositories` element looks up Spring Data repositories as described in <<repositories.create-instances>> .
|
||||
|
||||
.Setting up Elasticsearch repositories using Namespace
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:elasticsearch="http://www.springframework.org/schema/data/elasticsearch"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
|
||||
http://www.springframework.org/schema/data/elasticsearch
|
||||
http://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-1.0.xsd">
|
||||
|
||||
<elasticsearch:repositories base-package="com.acme.repositories" />
|
||||
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
Using the `Transport Client` or `Node Client` element registers an instance of `Elasticsearch Server` in the context.
|
||||
|
||||
.Transport Client using Namespace
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:elasticsearch="http://www.springframework.org/schema/data/elasticsearch"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
|
||||
http://www.springframework.org/schema/data/elasticsearch
|
||||
http://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-1.0.xsd">
|
||||
|
||||
<elasticsearch:transport-client id="client" cluster-nodes="localhost:9300,someip:9300" />
|
||||
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
.Node Client using Namespace
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:elasticsearch="http://www.springframework.org/schema/data/elasticsearch"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
|
||||
http://www.springframework.org/schema/data/elasticsearch
|
||||
http://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-1.0.xsd">
|
||||
|
||||
<elasticsearch:node-client id="client" local="true"" />
|
||||
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
[[elasticsearch.annotation]]
|
||||
=== Annotation based configuration
|
||||
|
||||
The Spring Data Elasticsearch repositories support cannot only be activated through an XML namespace but also using an annotation through JavaConfig.
|
||||
|
||||
.Spring Data Elasticsearch repositories using JavaConfig
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
@EnableElasticsearchRepositories(basePackages = "org/springframework/data/elasticsearch/repositories")
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public ElasticsearchOperations elasticsearchTemplate() {
|
||||
return new ElasticsearchTemplate(nodeBuilder().local(true).node().client());
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The configuration above sets up an `Embedded Elasticsearch Server` which is used by the `ElasticsearchTemplate` . Spring Data Elasticsearch Repositories are activated using the `@EnableElasticsearchRepositories` annotation, which essentially carries the same attributes as the XML namespace does. If no base package is configured, it will use the one the configuration class resides in.
|
||||
|
||||
[[elasticsearch.cdi]]
|
||||
=== Elasticsearch Repositores using CDI
|
||||
|
||||
The Spring Data Elasticsearch repositories can also be set up using CDI functionality.
|
||||
|
||||
.Spring Data Elasticsearch repositories using JavaConfig
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
class ElasticsearchTemplateProducer {
|
||||
|
||||
@Produces
|
||||
@ApplicationScoped
|
||||
public ElasticsearchOperations createElasticsearchTemplate() {
|
||||
return new ElasticsearchTemplate(nodeBuilder().local(true).node().client());
|
||||
}
|
||||
}
|
||||
|
||||
class ProductService {
|
||||
|
||||
private ProductRepository repository;
|
||||
|
||||
public Page<Product> findAvailableBookByName(String name, Pageable pageable) {
|
||||
return repository.findByAvailableTrueAndNameStartingWith(name, pageable);
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setRepository(ProductRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
[[elasticsearch.query-methods]]
|
||||
== Query methods
|
||||
|
||||
[[elasticsearch.query-methods.finders]]
|
||||
=== Query lookup strategies
|
||||
|
||||
The Elasticsearch module supports all basic query building feature as String,Abstract,Criteria or have it being derived from the method name.
|
||||
|
||||
==== Declared queries
|
||||
|
||||
Deriving the query from the method name is not always sufficient and/or may result in unreadable method names. In this case one might make either use of `@Query` annotation (see <<elasticsearch.query-methods.at-query>> ).
|
||||
|
||||
[[elasticsearch.query-methods.criterions]]
|
||||
=== Query creation
|
||||
|
||||
Generally the query creation mechanism for Elasticsearch works as described in <<repositories.query-methods>> . Here's a short example of what a Elasticsearch query method translates into:
|
||||
|
||||
.Query creation from method names
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public interface BookRepository extends Repository<Book, String>
|
||||
{
|
||||
List<Book> findByNameAndPrice(String name, Integer price);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The method name above will be translated into the following Elasticsearch json query
|
||||
|
||||
[source]
|
||||
----
|
||||
{ "bool" :
|
||||
{ "must" :
|
||||
[
|
||||
{ "field" : {"name" : "?"} },
|
||||
{ "field" : {"price" : "?"} }
|
||||
]
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
A list of supported keywords for Elasticsearch is shown below.
|
||||
|
||||
[cols="1,2,3", options="header"]
|
||||
.Supported keywords inside method names
|
||||
|===
|
||||
| Keyword
|
||||
| Sample
|
||||
| Elasticsearch Query String| `And`
|
||||
| `findByNameAndPrice`
|
||||
| `{"bool" : {"must" : [ {"field" : {"name" : "?"}},
|
||||
{"field" : {"price" : "?"}} ]}}`
|
||||
|
||||
| `Or`
|
||||
| `findByNameOrPrice`
|
||||
| `{"bool" : {"should" : [ {"field" : {"name" : "?"}},
|
||||
{"field" : {"price" : "?"}} ]}}`
|
||||
|
||||
| `Is`
|
||||
| `findByName`
|
||||
| `{"bool" : {"must" : {"field" : {"name" : "?"}}}}`
|
||||
|
||||
| `Not`
|
||||
| `findByNameNot`
|
||||
| `{"bool" : {"must_not" : {"field" : {"name" : "?"}}}}`
|
||||
|
||||
| `Between`
|
||||
| `findByPriceBetween`
|
||||
| `{"bool" : {"must" : {"range" : {"price" : {"from" :
|
||||
?,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}`
|
||||
|
||||
| `LessThanEqual`
|
||||
| `findByPriceLessThan`
|
||||
| `{"bool" : {"must" : {"range" : {"price" : {"from" :
|
||||
null,"to" : ?,"include_lower" : true,"include_upper" :
|
||||
true}}}}}`
|
||||
|
||||
| `GreaterThanEqual`
|
||||
| `findByPriceGreaterThan`
|
||||
| `{"bool" : {"must" : {"range" : {"price" : {"from" :
|
||||
?,"to" : null,"include_lower" : true,"include_upper" :
|
||||
true}}}}}`
|
||||
|
||||
| `Before`
|
||||
| `findByPriceBefore`
|
||||
| `{"bool" : {"must" : {"range" : {"price" : {"from" :
|
||||
null,"to" : ?,"include_lower" : true,"include_upper" :
|
||||
true}}}}}`
|
||||
|
||||
| `After`
|
||||
| `findByPriceAfter`
|
||||
| `{"bool" : {"must" : {"range" : {"price" : {"from" :
|
||||
?,"to" : null,"include_lower" : true,"include_upper" :
|
||||
true}}}}}`
|
||||
|
||||
| `Like`
|
||||
| `findByNameLike`
|
||||
| `{"bool" : {"must" : {"field" : {"name" : {"query" :
|
||||
"?*","analyze_wildcard" : true}}}}}`
|
||||
|
||||
| `StartingWith`
|
||||
| `findByNameStartingWith`
|
||||
| `{"bool" : {"must" : {"field" : {"name" : {"query" :
|
||||
"?*","analyze_wildcard" : true}}}}}`
|
||||
|
||||
| `EndingWith`
|
||||
| `findByNameEndingWith`
|
||||
| `{"bool" : {"must" : {"field" : {"name" : {"query" :
|
||||
"*?","analyze_wildcard" : true}}}}}`
|
||||
|
||||
| `Contains/Containing`
|
||||
| `findByNameContaining`
|
||||
| `{"bool" : {"must" : {"field" : {"name" : {"query" :
|
||||
"*?*","analyze_wildcard" : true}}}}}`
|
||||
|
||||
| `In`
|
||||
| `findByNameIn(Collection<String>names)`
|
||||
| `{"bool" : {"must" : {"bool" : {"should" : [ {"field" :
|
||||
{"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}}`
|
||||
|
||||
| `NotIn`
|
||||
| `findByNameNotIn(Collection<String>names)`
|
||||
| `{"bool" : {"must_not" : {"bool" : {"should" : {"field" :
|
||||
{"name" : "?"}}}}}}`
|
||||
|
||||
| `Near`
|
||||
| `findByStoreNear`
|
||||
| `Not Supported Yet !`
|
||||
|
||||
| `True`
|
||||
| `findByAvailableTrue`
|
||||
| `{"bool" : {"must" : {"field" : {"available" : true}}}}`
|
||||
|
||||
| `False`
|
||||
| `findByAvailableFalse`
|
||||
| `{"bool" : {"must" : {"field" : {"available" : false}}}}`
|
||||
|
||||
| `OrderBy`
|
||||
| `findByAvailableTrueOrderByNameDesc`
|
||||
| `{"sort" : [{ "name" : {"order" : "desc"} }],"bool" :
|
||||
{"must" : {"field" : {"available" : true}}}}`
|
||||
|===
|
||||
|
||||
[[elasticsearch.query-methods.at-query]]
|
||||
=== Using @Query Annotation
|
||||
|
||||
.Declare query at the method using the `@Query` annotation.
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public interface BookRepository extends ElasticsearchRepository<Book, String> {
|
||||
@Query("{"bool" : {"must" : {"field" : {"name" : "?0"}}}}")
|
||||
Page<Book> findByName(String name,Pageable pageable);
|
||||
}
|
||||
----
|
||||
====
|
||||
@@ -1,192 +0,0 @@
|
||||
[[elasticsearch.clients]]
|
||||
= Elasticsearch Clients
|
||||
|
||||
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>>.
|
||||
|
||||
[[elasticsearch.clients.transport]]
|
||||
== Transport Client
|
||||
|
||||
WARNING: The well known `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`.
|
||||
|
||||
.Transport Client
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
public class TransportClientConfig extends ElasticsearchConfigurationSupport {
|
||||
|
||||
@Bean
|
||||
public Client elasticsearchClient() throws UnknownHostException {
|
||||
Settings settings = Settings.builder().put("cluster.name", "elasticsearch").build(); <1>
|
||||
TransportClient client = new PreBuiltTransportClient(settings);
|
||||
client.addTransportAddress(new TransportAddress(InetAddress.getByName("127.0.0.1"), 9300)); <2>
|
||||
return client;
|
||||
}
|
||||
|
||||
@Bean(name = { "elasticsearchOperations", "elasticsearchTemplate" })
|
||||
public ElasticsearchTemplate elasticsearchTemplate() throws UnknownHostException {
|
||||
return new ElasticsearchTemplate(elasticsearchClient());
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
IndexRequest request = new IndexRequest("spring-data", "elasticsearch", randomID())
|
||||
.source(someObject)
|
||||
.setRefreshPolicy(IMMEDIATE);
|
||||
|
||||
IndexResponse response = client.index(request);
|
||||
----
|
||||
<1> The `TransportClient` must be configured with the cluster name.
|
||||
<2> The host and port to connect the client to.
|
||||
====
|
||||
|
||||
[[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.
|
||||
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
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
public class RestClientConfig extends AbstractElasticsearchConfiguration {
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public RestHighLevelClient elasticsearchClient() {
|
||||
|
||||
final ClientConfiguration clientConfiguration = ClientConfiguration.builder() <1>
|
||||
.connectedTo("localhost:9200")
|
||||
.build();
|
||||
|
||||
return RestClients.create(clientConfiguration).rest(); <2>
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
@Autowired
|
||||
RestHighLevelClient highLevelClient;
|
||||
|
||||
RestClient lowLevelClient = highLevelClient.lowLevelClient(); <3>
|
||||
|
||||
// ...
|
||||
|
||||
IndexRequest request = new IndexRequest("spring-data", "elasticsearch", randomID())
|
||||
.source(singletonMap("feature", "high-level-rest-client"))
|
||||
.setRefreshPolicy(IMMEDIATE);
|
||||
|
||||
IndexResponse response = highLevelClient.index(request);
|
||||
----
|
||||
<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.
|
||||
====
|
||||
|
||||
[[elasticsearch.clients.reactive]]
|
||||
== Reactive Client
|
||||
|
||||
The `ReactiveElasticsearchClient` is a non official driver based on `WebClient`.
|
||||
It uses the request/response objects provided by the Elasticsearch core project.
|
||||
Calls are directly operated on the reactive stack, **not** wrapping async (thread pool bound) responses into reactive types.
|
||||
|
||||
.Reactive REST Client
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
ReactiveElasticsearchClient client() {
|
||||
|
||||
ClientConfiguration clientConfiguration = ClientConfiguration.builder() <1>
|
||||
.connectedTo("localhost:9200", "localhost:9291")
|
||||
.withWebClientConfigurer(webClient -> { <2>
|
||||
ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder()
|
||||
.codecs(configurer -> configurer.defaultCodecs()
|
||||
.maxInMemorySize(-1))
|
||||
.build();
|
||||
return webClient.mutate().exchangeStrategies(exchangeStrategies).build();
|
||||
})
|
||||
.build();
|
||||
|
||||
return ReactiveRestClients.create(clientConfiguration);
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
Mono<IndexResponse> response = client.index(request ->
|
||||
|
||||
request.index("spring-data")
|
||||
.type("elasticsearch")
|
||||
.id(randomID())
|
||||
.source(singletonMap("feature", "reactive-client"))
|
||||
.setRefreshPolicy(IMMEDIATE);
|
||||
);
|
||||
----
|
||||
<1> Use the builder to provide cluster addresses, set default `HttpHeaders` or enable SSL.
|
||||
<2> when configuring a reactive client, the `withWebClientConfigurer` hook can be used to customize the WebClient.
|
||||
====
|
||||
|
||||
NOTE: The ReactiveClient response, especially for search operations, is bound to the `from` (offset) & `size` (limit) options of the request.
|
||||
|
||||
[[elasticsearch.clients.configuration]]
|
||||
== Client Configuration
|
||||
|
||||
Client behaviour can be changed via the `ClientConfiguration` that allows to set options for SSL, connect and socket timeouts.
|
||||
|
||||
.Client Configuration
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
// optional if Basic Auhtentication is needed
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.add("es-security-runas-user", "some-user") <1>
|
||||
|
||||
ClientConfiguration clientConfiguration = ClientConfiguration.builder()
|
||||
.connectedTo("localhost:9200", "localhost:9291") <2>
|
||||
.withProxy("localhost:8888") <3>
|
||||
.withPathPrefix("ela") <4>
|
||||
.withConnectTimeout(Duration.ofSeconds(5)) <5>
|
||||
.withSocketTimeout(Duration.ofSeconds(3)) <6>
|
||||
.useSsl() <7>
|
||||
.withDefaultHeaders(defaultHeaders) <8>
|
||||
.withBasicAuth(username, password) <9>
|
||||
. // ... other options
|
||||
.build();
|
||||
|
||||
----
|
||||
<1> Define default headers, if they need to be customized
|
||||
<2> Use the builder to provide cluster addresses, set default `HttpHeaders` or enable SSL.
|
||||
<3> Optionally set a proxy footnote:notreactive[not yet implemented for the reactive client].
|
||||
<4> Optionally set a path prefix, mostly used when different clusters a behind some reverse proxy.
|
||||
<5> Set the connection timeout. Default is 10 sec.
|
||||
<6> Set the socket timeout. Default is 5 sec.
|
||||
<7> Optionally enable SSL.
|
||||
<8> Optionally set headers.
|
||||
<9> Add basic authentication.
|
||||
====
|
||||
|
||||
[[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.
|
||||
|
||||
.Enable transport layer logging
|
||||
[source,xml]
|
||||
----
|
||||
<logger name="org.springframework.data.elasticsearch.client.WIRE" level="trace"/>
|
||||
----
|
||||
|
||||
NOTE: The above applies to both the `RestHighLevelClient` and `ReactiveElasticsearchClient` when obtained via `RestClients` respectively `ReactiveRestClients`, is not available for the `TransportClient`.
|
||||
@@ -11,86 +11,62 @@ Filter Builder improves query speed.
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
private ElasticsearchOperations operations;
|
||||
|
||||
IndexCoordinates index = IndexCoordinates.of("sample-index");
|
||||
private ElasticsearchTemplate elasticsearchTemplate;
|
||||
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder()
|
||||
.withQuery(matchAllQuery())
|
||||
.withFilter(boolFilter().must(termFilter("id", documentId)))
|
||||
.build();
|
||||
|
||||
Page<SampleEntity> sampleEntities = operations.searchForPage(searchQuery, SampleEntity.class, index);
|
||||
.withQuery(matchAllQuery())
|
||||
.withFilter(boolFilter().must(termFilter("id", documentId)))
|
||||
.build();
|
||||
|
||||
Page<SampleEntity> sampleEntities =
|
||||
elasticsearchTemplate.queryForPage(searchQuery,SampleEntity.class);
|
||||
----
|
||||
====
|
||||
|
||||
[[elasticsearch.scroll]]
|
||||
== Using Scroll For Big Result Set
|
||||
[[elasticsearch.scan.and.scroll]]
|
||||
== Using Scan And Scroll For Big Result Set
|
||||
|
||||
Elasticsearch has a scroll API for getting big result set in chunks. `ElasticsearchOperations` has startScroll and continueScroll methods that can be used as below.
|
||||
Elasticsearch has scan and scroll feature for getting big result set in chunks. `ElasticsearchTemplate` has scan and scroll methods that can be used as below.
|
||||
|
||||
.Using startScroll and continueScroll
|
||||
.Using Scan and Scroll
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
IndexCoordinates index = IndexCoordinates.of("sample-index");
|
||||
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder()
|
||||
.withQuery(matchAllQuery())
|
||||
.withIndices(INDEX_NAME)
|
||||
.withTypes(TYPE_NAME)
|
||||
.withFields("message")
|
||||
.withPageable(PageRequest.of(0, 10))
|
||||
.build();
|
||||
|
||||
ScrolledPage<SampleEntity> scroll = operations.startScroll(1000, searchQuery, SampleEntity.class, index);
|
||||
|
||||
String scrollId = scroll.getScrollId();
|
||||
List<SampleEntity> sampleEntities = new ArrayList<>();
|
||||
while (scroll.hasContent()) {
|
||||
sampleEntities.addAll(scroll.getContent());
|
||||
scrollId = scroll.getScrollId();
|
||||
scroll = operations.continueScroll(scrollId, 1000, SampleEntity.class);
|
||||
}
|
||||
operations.clearScroll(scrollId);
|
||||
.withQuery(matchAllQuery())
|
||||
.withIndices("test-index")
|
||||
.withTypes("test-type")
|
||||
.withPageable(new PageRequest(0,1))
|
||||
.build();
|
||||
String scrollId = elasticsearchTemplate.scan(searchQuery,1000,false);
|
||||
List<SampleEntity> sampleEntities = new ArrayList<SampleEntity>();
|
||||
boolean hasRecords = true;
|
||||
while (hasRecords){
|
||||
Page<SampleEntity> page = elasticsearchTemplate.scroll(scrollId, 5000L , new ResultsMapper<SampleEntity>()
|
||||
{
|
||||
@Override
|
||||
public Page<SampleEntity> mapResults(SearchResponse response) {
|
||||
List<SampleEntity> chunk = new ArrayList<SampleEntity>();
|
||||
for(SearchHit searchHit : response.getHits()){
|
||||
if(response.getHits().getHits().length <= 0) {
|
||||
return null;
|
||||
}
|
||||
SampleEntity user = new SampleEntity();
|
||||
user.setId(searchHit.getId());
|
||||
user.setMessage((String)searchHit.getSource().get("message"));
|
||||
chunk.add(user);
|
||||
}
|
||||
return new PageImpl<SampleEntity>(chunk);
|
||||
}
|
||||
});
|
||||
if(page != null) {
|
||||
sampleEntities.addAll(page.getContent());
|
||||
hasRecords = page.hasNextPage();
|
||||
}
|
||||
else{
|
||||
hasRecords = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
`ElasticsearchOperations` additionally has the stream method which wraps the scan and scroll operations into a CloseableIterator.
|
||||
|
||||
.Using stream
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
IndexCoordinates index = IndexCoordinates.of("sample-index");
|
||||
|
||||
SearchQuery searchQuery = new NativeSearchQueryBuilder()
|
||||
.withQuery(matchAllQuery())
|
||||
.withIndices(INDEX_NAME)
|
||||
.withTypes(TYPE_NAME)
|
||||
.withFields("message")
|
||||
.withPageable(PageRequest.of(0, 10))
|
||||
.build();
|
||||
|
||||
CloseableIterator<SampleEntity> stream = elasticsearchTemplate.stream(searchQuery, SampleEntity.class, index);
|
||||
|
||||
List<SampleEntity> sampleEntities = new ArrayList<>();
|
||||
while (stream.hasNext()) {
|
||||
sampleEntities.add(stream.next());
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
[[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.
|
||||
|
||||
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:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
Sort.by(new GeoDistanceOrder("location", new GeoPoint(48.137154, 11.5761247)))
|
||||
----
|
||||
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
[[new-features]]
|
||||
= What's new
|
||||
|
||||
[[new-features.4-0-0]]
|
||||
== New in Spring Data Elasticsearch 4.0
|
||||
|
||||
* Uses Spring 5.2.
|
||||
* Upgrade to Elasticsearch 7.6.1.
|
||||
* Deprecation of `TransportClient` usage.
|
||||
* Implements most of the mapping-types available for the index mappings.
|
||||
* Removal of the Jackson `ObjectMapper`, now using the <<elasticsearch.mapping.meta-model,MappingElasticsearchConverter>>
|
||||
* Cleanup of the API in the `*Operations` interfaces, grouping and renaming methods so that they match the Elasticsearch API, deprecating the old methods, aligning with other Spring Data modules.
|
||||
* Introduction of `SearchHit<T>` class to represent a found document together with the relevant result metadata for this document (i.e. _sortValues_).
|
||||
* Introduction of the `SearchHits<T>` class to represent a whole search result together with the metadata for the complete search result (i.e. _max_score_).
|
||||
* Introduction of the `GeoDistanceOrder` class to be able to create sorting by geographical distance
|
||||
|
||||
[[new-features.3-2-0]]
|
||||
== New in Spring Data Elasticsearch 3.2
|
||||
|
||||
* Secured Elasticsearch cluster support with Basic Authentication and SSL transport.
|
||||
* Upgrade to Elasticsearch 6.8.1.
|
||||
* Reactive programming support with <<elasticsearch.reactive.operations>> and <<elasticsearch.reactive.repositories>>.
|
||||
* Introduction of the <<elasticsearch.mapping.meta-model,ElasticsearchEntityMapper>> as an alternative to the Jackson `ObjectMapper`.
|
||||
* Field name customization in `@Field`.
|
||||
* Support for Delete by Query.
|
||||
@@ -1,256 +0,0 @@
|
||||
[[elasticsearch.mapping]]
|
||||
= Elasticsearch Object Mapping
|
||||
|
||||
Spring Data Elasticsearch Object Mapping is the process that maps a Java object - the domain entity - into the JSON representation that is stored in Elasticsearch and back.
|
||||
|
||||
Earlier versions of Spring Data Elasticsearch used a Jackson based conversion, Spring Data Elasticsearch 3.2.x introduced the <<elasticsearch.mapping.meta-model>>. As of version 4.0 only the Meta Object Mapping is used, the Jackson based mapper is not available anymore and the `MappingElasticsearchConverter` is used.
|
||||
|
||||
The main reasons for the removal of the Jackson based mapper are:
|
||||
|
||||
* Custom mappings of fields needed to be done with annotations like `@JsonFormat` or `@JsonInclude`. This often caused problems when the same object was used in different JSON based datastores or sent over a JSON based API.
|
||||
* Custom field types and formats also need to be stored into the Elasticsearch index mappings. The Jackson based annotations did not fully provide all the information that is necessary to represent the types of Elasticsearch.
|
||||
* Fields must be mapped not only when converting fromand to entities, but also an query argument, returned data and on other places.
|
||||
|
||||
Using the `MappingElasticsearchConverter` now covers all these cases.
|
||||
|
||||
|
||||
[[elasticsearch.mapping.meta-model]]
|
||||
== Meta Model Object Mapping
|
||||
|
||||
The Metamodel based approach uses domain type information for reading/writing from/to Elasticsearch.
|
||||
This allows to register `Converter` instances for specific domain type mapping.
|
||||
|
||||
[[elasticsearch.mapping.meta-model.annotations]]
|
||||
=== Mapping Annotation Overview
|
||||
|
||||
The `MappingElasticsearchConverter` uses metadata to drive the mapping of objects to documents. The metadata is taken from the entity's properties which can be annotated.
|
||||
|
||||
The following annotations are available:
|
||||
|
||||
* `@Document`: Applied at the class level to indicate this class is a candidate for mapping to the database. The most important attributes are:
|
||||
** `indexName`: the name of the index to store this entity in
|
||||
** `type`: the mapping type. If not set, the lowercased simple name of the class is used.
|
||||
** `shards`: the number of shards for the index.
|
||||
** `replicas`: the number of replicas for the index.
|
||||
** `refreshIntervall`: Refresh interval for the index. Used for index creation. Default value is _"1s"_.
|
||||
** `indexStoreType`: Index storage type for the index. Used for index creation. Default value is _"fs"_.
|
||||
** `createIndex`: Configuration whether to create an index on repository bootstrapping. Default value is _true_.
|
||||
** `versionType`: Configuration of version management. Default value is _EXTERNAL_.
|
||||
* `@Id`: Applied at the field level to mark the field used for identity purpose.
|
||||
* `@Transient`: By default all private fields are mapped to the document, this annotation excludes the field where it is applied from being stored in the database
|
||||
* `@PersistenceConstructor`: Marks a given constructor - even a package protected one - to use when instantiating the object from the database. Constructor arguments are mapped by name to the key values in the retrieved Document.
|
||||
* `@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:
|
||||
** `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, Integer, Long, Date, Float, Double, Boolean, Object, Auto, Nested, Ip, Attachment, Keyword_. See https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html[Elasticsearch Mapping Types]
|
||||
** `format` and `pattern` custom definitions for the _Date_ type.
|
||||
** `store`: Flag wether the original field value should be store in Elasticsearch, default value is _false_.
|
||||
** `analyzer`, `searchAnalyzer`, `normalizer` for specifying custom custom analyzers and normalizer.
|
||||
** `copy_to`: the target field to copy multiple document fields to.
|
||||
* `@GeoPoint`: marks a field as _geo_point_ datatype. Can be omitted if the field is an instance of the `GeoPoint` class.
|
||||
|
||||
The mapping metadata infrastructure is defined in a separate spring-data-commons project that is technology agnostic.
|
||||
|
||||
[[elasticsearch.mapping.meta-model.rules]]
|
||||
=== Mapping Rules
|
||||
|
||||
==== Type Hints
|
||||
|
||||
Mapping uses _type hints_ embedded in the document sent to the server to allow generic type mapping.
|
||||
Those type hints are represented as `_class` attributes within the document and are written for each aggregate root.
|
||||
|
||||
.Type Hints
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public class Person { <1>
|
||||
|
||||
@Id String id;
|
||||
String firstname;
|
||||
String lastname;
|
||||
}
|
||||
----
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
"_class" : "com.example.Person", <1>
|
||||
"id" : "cb7bef",
|
||||
"firstname" : "Sarah",
|
||||
"lastname" : "Connor"
|
||||
}
|
||||
----
|
||||
<1> By default the domain types class name is used for the type hint.
|
||||
====
|
||||
|
||||
Type hints can be configured to hold custom information. Use the `@TypeAlias` annotation to do so.
|
||||
|
||||
NOTE: Make sure to add types with `@TypeAlias` to the initial entity set (`AbstractElasticsearchConfiguration#getInitialEntitySet`)
|
||||
to already have entity information available when first reading data from the store.
|
||||
|
||||
.Type Hints with Alias
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@TypeAlias("human") <1>
|
||||
public class Person {
|
||||
|
||||
@Id String id;
|
||||
// ...
|
||||
}
|
||||
----
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
"_class" : "human", <1>
|
||||
"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.
|
||||
|
||||
==== Geospatial Types
|
||||
|
||||
Geospatial types like `Point` & `GeoPoint` are converted into _lat/lon_ pairs.
|
||||
|
||||
.Geospatial types
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public class Address {
|
||||
|
||||
String city, street;
|
||||
Point location;
|
||||
}
|
||||
----
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
"city" : "Los Angeles",
|
||||
"street" : "2800 East Observatory Road",
|
||||
"location" : { "lat" : 34.118347, "lon" : -118.3026284 }
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
==== Collections
|
||||
|
||||
For values inside Collections apply the same mapping rules as for aggregate roots when it comes to _type hints_ and <<elasticsearch.mapping.meta-model.conversions>>.
|
||||
|
||||
.Collections
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public class Person {
|
||||
|
||||
// ...
|
||||
|
||||
List<Person> friends;
|
||||
|
||||
}
|
||||
----
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
// ...
|
||||
|
||||
"friends" : [ { "firstname" : "Kyle", "lastname" : "Reese" } ]
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
==== Maps
|
||||
|
||||
For values inside Maps apply the same mapping rules as for aggregate roots when it comes to _type hints_ and <<elasticsearch.mapping.meta-model.conversions>>.
|
||||
However the Map key needs to a String to be processed by Elasticsearch.
|
||||
|
||||
.Collections
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public class Person {
|
||||
|
||||
// ...
|
||||
|
||||
Map<String, Address> knownLocations;
|
||||
|
||||
}
|
||||
----
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
// ...
|
||||
|
||||
"knownLocations" : {
|
||||
"arrivedAt" : {
|
||||
"city" : "Los Angeles",
|
||||
"street" : "2800 East Observatory Road",
|
||||
"location" : { "lat" : 34.118347, "lon" : -118.3026284 }
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
[[elasticsearch.mapping.meta-model.conversions]]
|
||||
=== Custom Conversions
|
||||
|
||||
Looking at the `Configuration` from the <<elasticsearch.mapping.meta-model, previous section>> `ElasticsearchCustomConversions` allows registering specific rules for mapping domain and simple types.
|
||||
|
||||
.Meta Model Object Mapping Configuration
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
public class Config extends AbstractElasticsearchConfiguration {
|
||||
|
||||
@Override
|
||||
public RestHighLevelClient elasticsearchClient() {
|
||||
return RestClients.create(ClientConfiguration.create("localhost:9200")).rest();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Override
|
||||
public ElasticsearchCustomConversions elasticsearchCustomConversions() {
|
||||
return new ElasticsearchCustomConversions(
|
||||
Arrays.asList(new AddressToMap(), new MapToAddress())); <1>
|
||||
}
|
||||
|
||||
@WritingConverter <2>
|
||||
static class AddressToMap implements Converter<Address, Map<String, Object>> {
|
||||
|
||||
@Override
|
||||
public Map<String, Object> convert(Address source) {
|
||||
|
||||
LinkedHashMap<String, Object> target = new LinkedHashMap<>();
|
||||
target.put("ciudad", source.getCity());
|
||||
// ...
|
||||
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
||||
@ReadingConverter <3>
|
||||
static class MapToAddress implements Converter<Map<String, Object>, Address> {
|
||||
|
||||
@Override
|
||||
public Address convert(Map<String, Object> source) {
|
||||
|
||||
// ...
|
||||
return address;
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
"ciudad" : "Los Angeles",
|
||||
"calle" : "2800 East Observatory Road",
|
||||
"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.
|
||||
====
|
||||
@@ -1,147 +0,0 @@
|
||||
[[elasticsearch.operations]]
|
||||
= Elasticsearch Operations
|
||||
|
||||
Spring Data Elasticsearch uses several interfaces to define the operations that can be called against an Elasticsearch index (for a description of the reactive interfaces see <<elasticsearch.reactive.operations>>).
|
||||
|
||||
* `IndexOperations` defines actions on index level like creating or deleting an index.
|
||||
* `DocumentOperations` defines actions to store, update and retrieve entities based on their id.
|
||||
* `SearchOperations` define the actions to search for multiple entities using queries
|
||||
* `ElasticsearchOperations` combines the `DocumentOperations` and `SearchOperations` interfaces.
|
||||
|
||||
These interfaces correspond to the structuring of the https://www.elastic.co/guide/en/elasticsearch/reference/current/rest-apis.html[Elasticsearch API].
|
||||
|
||||
The default implementations of the interfaces offer:
|
||||
|
||||
* index management functionality.
|
||||
* Read/Write mapping support for domain types.
|
||||
* A rich query and criteria api.
|
||||
* Resource management and Exception translation.
|
||||
|
||||
[[elasticsearch.operations.template]]
|
||||
== ElasticsearchTemplate
|
||||
|
||||
NOTE: Usage of the ElasticsearchTemplate is deprecated as of version 4.0, use ElasticsearchRestTemplate instead.
|
||||
|
||||
The `ElasticsearchTemplate` is an implementation of the `ElasticsearchOperations` interface using the <<elasticsearch.clients.transport>>.
|
||||
|
||||
.ElasticsearchTemplate configuration
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
public class TransportClientConfig extends ElasticsearchConfigurationSupport {
|
||||
|
||||
@Bean
|
||||
public Client elasticsearchClient() throws UnknownHostException { <1>
|
||||
Settings settings = Settings.builder().put("cluster.name", "elasticsearch").build();
|
||||
TransportClient client = new PreBuiltTransportClient(settings);
|
||||
client.addTransportAddress(new TransportAddress(InetAddress.getByName("127.0.0.1"), 9300));
|
||||
return client;
|
||||
}
|
||||
|
||||
@Bean(name = {"elasticsearchOperations", "elasticsearchTemplate"})
|
||||
public ElasticsearchTemplate elasticsearchTemplate() throws UnknownHostException { <2>
|
||||
return new ElasticsearchTemplate(elasticsearchClient());
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> Setting up the <<elasticsearch.clients.transport>>. Deprecated as of version 4.0.
|
||||
<2> Creating the `ElasticsearchTemplate` bean, offering both names, _elasticsearchOperations_ and _elasticsearchTemplate_.
|
||||
====
|
||||
|
||||
[[elasticsearch.operations.resttemplate]]
|
||||
== ElasticsearchRestTemplate
|
||||
|
||||
The `ElasticsearchRestTemplate` is an implementation of the `ElasticsearchOperations` interface using the <<elasticsearch.clients.rest>>.
|
||||
|
||||
.ElasticsearchRestTemplate configuration
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
public class RestClientConfig extends AbstractElasticsearchConfiguration {
|
||||
@Override
|
||||
public RestHighLevelClient elasticsearchClient() { <1>
|
||||
return RestClients.create(ClientConfiguration.localhost()).rest();
|
||||
}
|
||||
|
||||
// no special bean creation needed <2>
|
||||
}
|
||||
----
|
||||
<1> Setting up the <<elasticsearch.clients.rest>>.
|
||||
<2> The base class `AbstractElasticsearchConfiguration` already provides the `elasticsearchTemplate` bean.
|
||||
====
|
||||
|
||||
[[elasticsearch.operations.usage]]
|
||||
== Usage examples
|
||||
|
||||
As both `ElasticsearchTemplate` and `ElasticsearchRestTemplate` implement the `ElasticsearchOperations` interface, the code to use them is not different. The example shows how to use an injected `ElasticsearchOperations` instance in a Spring REST controller. The decision, if this is using the `TransportClient` or the `RestClient` is made by providing the corresponding Bean with one of the configurations shown above.
|
||||
|
||||
.ElasticsearchOperations usage
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@RestController
|
||||
@RequestMapping("/")
|
||||
public class TestController {
|
||||
|
||||
private ElasticsearchOperations elasticsearchOperations;
|
||||
|
||||
public TestController(ElasticsearchOperations elasticsearchOperations) { <1>
|
||||
this.elasticsearchOperations = elasticsearchOperations;
|
||||
}
|
||||
|
||||
@PostMapping("/person")
|
||||
public String save(@RequestBody Person person) { <2>
|
||||
|
||||
IndexQuery indexQuery = new IndexQueryBuilder()
|
||||
.withId(person.getId().toString())
|
||||
.withObject(person)
|
||||
.build();
|
||||
String documentId = elasticsearchOperations.index(indexQuery);
|
||||
return documentId;
|
||||
}
|
||||
|
||||
@GetMapping("/person/{id}")
|
||||
public Person findById(@PathVariable("id") Long id) { <3>
|
||||
Person person = elasticsearchOperations
|
||||
.queryForObject(GetQuery.getById(id.toString()), Person.class);
|
||||
return person;
|
||||
}
|
||||
}
|
||||
|
||||
----
|
||||
<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.
|
||||
====
|
||||
|
||||
To see the full possibilities of `ElasticsearchOperations` please refer to the API documentation.
|
||||
|
||||
include::reactive-elasticsearch-operations.adoc[leveloffset=+1]
|
||||
|
||||
[[elasticsearch.operations.searchresulttypes]]
|
||||
== Search Result Types
|
||||
|
||||
When a document is retrieved with the methods of the `DocumentOperations` interface, just the found entity will be returned. When searching with the methods of the `SearchOperations` interface, additional information is available for each entity, for example the _score_ or the _sortValues_ of the found entity.
|
||||
|
||||
In order to return this information, each entity is wrapped in a `SearchHit` object that contains this entity-specific additional information. These `SearchHit` objects themselves are returned within a `SearchHits` object which additionally contains informations about the whole search ike the _maxScore_ or requested aggregations.
|
||||
|
||||
.SearchHit<T>
|
||||
Contains the following information:
|
||||
|
||||
* Id
|
||||
* Score
|
||||
* Sort Values
|
||||
* Highligth fields
|
||||
* The retrieved entity of type <T>
|
||||
|
||||
.SearchHits<T>
|
||||
Contains the following information:
|
||||
|
||||
* Number of total hits
|
||||
* Total hits relation
|
||||
* Maximum score
|
||||
* A list of `SearchHit<T>` objects
|
||||
* Returned aggregations
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
[[elasticsearch.repositories]]
|
||||
= Elasticsearch Repositories
|
||||
|
||||
This chapter includes details of the Elasticsearch repository implementation.
|
||||
|
||||
include::elasticsearch-repository-queries.adoc[leveloffset=+1]
|
||||
|
||||
[[elasticsearch.annotation]]
|
||||
== Annotation based configuration
|
||||
|
||||
The Spring Data Elasticsearch repositories support can be activated using an annotation through JavaConfig.
|
||||
|
||||
.Spring Data Elasticsearch repositories using JavaConfig
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
@EnableElasticsearchRepositories( <1>
|
||||
basePackages = "org.springframework.data.elasticsearch.repositories"
|
||||
)
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public ElasticsearchOperations elasticsearchTemplate() { <2>
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
class ProductService {
|
||||
|
||||
private ProductRepository repository; <3>
|
||||
|
||||
public ProductService(ProductRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public Page<Product> findAvailableBookByName(String name, Pageable pageable) {
|
||||
return repository.findByAvailableTrueAndNameStartingWith(name, pageable);
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
<1> The `EnableElasticsearchRepositories` annotation activates the Repository support. If no base package is configured, it will use the one of the configuration class it is put on.
|
||||
<2> Provide a Bean named `elasticsearchTemplate` of type `ElasticsearchOperations` by using one of the configurations shown in the <<elasticsearch.operations>> chapter.
|
||||
<3> Let Spring inject the Repository bean into your class.
|
||||
====
|
||||
|
||||
[[elasticsearch.cdi]]
|
||||
== Elasticsearch Repositories using CDI
|
||||
|
||||
The Spring Data Elasticsearch repositories can also be set up using CDI functionality.
|
||||
|
||||
.Spring Data Elasticsearch repositories using CDI
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
class ElasticsearchTemplateProducer {
|
||||
|
||||
@Produces
|
||||
@ApplicationScoped
|
||||
public ElasticsearchOperations createElasticsearchTemplate() {
|
||||
// ... <1>
|
||||
}
|
||||
}
|
||||
|
||||
class ProductService {
|
||||
|
||||
private ProductRepository repository; <2>
|
||||
public Page<Product> findAvailableBookByName(String name, Pageable pageable) {
|
||||
return repository.findByAvailableTrueAndNameStartingWith(name, pageable);
|
||||
}
|
||||
@Inject
|
||||
public void setRepository(ProductRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> Create a component by using the same calls as are used in the <<elasticsearch.operations>> chapter.
|
||||
<2> Let the CDI framework inject the Repository into your class.
|
||||
|
||||
====
|
||||
|
||||
[[elasticsearch.namespace]]
|
||||
== Spring Namespace
|
||||
|
||||
The Spring Data Elasticsearch module contains a custom namespace allowing definition of repository beans as well as elements for instantiating a `ElasticsearchServer` .
|
||||
|
||||
Using the `repositories` element looks up Spring Data repositories as described in <<repositories.create-instances>> .
|
||||
|
||||
.Setting up Elasticsearch repositories using Namespace
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:elasticsearch="http://www.springframework.org/schema/data/elasticsearch"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
https://www.springframework.org/schema/beans/spring-beans-3.1.xsd
|
||||
http://www.springframework.org/schema/data/elasticsearch
|
||||
https://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-1.0.xsd">
|
||||
|
||||
<elasticsearch:repositories base-package="com.acme.repositories" />
|
||||
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
Using the `Transport Client` or `Rest Client` element registers an instance of `Elasticsearch Server` in the context.
|
||||
|
||||
.Transport Client using Namespace
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:elasticsearch="http://www.springframework.org/schema/data/elasticsearch"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
https://www.springframework.org/schema/beans/spring-beans-3.1.xsd
|
||||
http://www.springframework.org/schema/data/elasticsearch
|
||||
https://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-1.0.xsd">
|
||||
|
||||
<elasticsearch:transport-client id="client" cluster-nodes="localhost:9300,someip:9300" />
|
||||
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
.Rest Client using Namespace
|
||||
====
|
||||
[source,xml]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:elasticsearch="http://www.springframework.org/schema/data/elasticsearch"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/data/elasticsearch
|
||||
https://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch.xsd
|
||||
http://www.springframework.org/schema/beans
|
||||
https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<elasticsearch:rest-client id="restClient" hosts="http://localhost:9200">
|
||||
|
||||
</beans>
|
||||
----
|
||||
====
|
||||
|
||||
include::reactive-elasticsearch-repositories.adoc[leveloffset=+1]
|
||||
@@ -1,305 +0,0 @@
|
||||
[[elasticsearch.query-methods]]
|
||||
= Query methods
|
||||
|
||||
[[elasticsearch.query-methods.finders]]
|
||||
== Query lookup strategies
|
||||
|
||||
The Elasticsearch module supports all basic query building feature as string queries, native search queries, criteria based queries or have it being derived from the method name.
|
||||
|
||||
=== Declared queries
|
||||
|
||||
Deriving the query from the method name is not always sufficient and/or may result in unreadable method names. In this case one might make use of the `@Query` annotation (see <<elasticsearch.query-methods.at-query>> ).
|
||||
|
||||
[[elasticsearch.query-methods.criterions]]
|
||||
== Query creation
|
||||
|
||||
Generally the query creation mechanism for Elasticsearch works as described in <<repositories.query-methods>>. Here's a short example of what a Elasticsearch query method translates into:
|
||||
|
||||
.Query creation from method names
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
interface BookRepository extends Repository<Book, String> {
|
||||
List<Book> findByNameAndPrice(String name, Integer price);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
The method name above will be translated into the following Elasticsearch json query
|
||||
|
||||
[source]
|
||||
----
|
||||
{
|
||||
"query": {
|
||||
"bool" : {
|
||||
"must" : [
|
||||
{ "query_string" : { "query" : "?", "fields" : [ "name" ] } },
|
||||
{ "query_string" : { "query" : "?", "fields" : [ "price" ] } }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
A list of supported keywords for Elasticsearch is shown below.
|
||||
|
||||
[cols="1,2,3", options="header"]
|
||||
.Supported keywords inside method names
|
||||
|===
|
||||
| Keyword
|
||||
| Sample
|
||||
| Elasticsearch Query String| `And`
|
||||
| `findByNameAndPrice`
|
||||
| `{ "query" : {
|
||||
"bool" : {
|
||||
"must" : [
|
||||
{ "query_string" : { "query" : "?", "fields" : [ "name" ] } },
|
||||
{ "query_string" : { "query" : "?", "fields" : [ "price" ] } }
|
||||
]
|
||||
}
|
||||
}}`
|
||||
|
||||
| `Or`
|
||||
| `findByNameOrPrice`
|
||||
| `{ "query" : {
|
||||
"bool" : {
|
||||
"should" : [
|
||||
{ "query_string" : { "query" : "?", "fields" : [ "name" ] } },
|
||||
{ "query_string" : { "query" : "?", "fields" : [ "price" ] } }
|
||||
]
|
||||
}
|
||||
}}`
|
||||
|
||||
| `Is`
|
||||
| `findByName`
|
||||
| `{ "query" : {
|
||||
"bool" : {
|
||||
"must" : [
|
||||
{ "query_string" : { "query" : "?", "fields" : [ "name" ] } }
|
||||
]
|
||||
}
|
||||
}}`
|
||||
|
||||
| `Not`
|
||||
| `findByNameNot`
|
||||
| `{ "query" : {
|
||||
"bool" : {
|
||||
"must_not" : [
|
||||
{ "query_string" : { "query" : "?", "fields" : [ "name" ] } }
|
||||
]
|
||||
}
|
||||
}}`
|
||||
|
||||
| `Between`
|
||||
| `findByPriceBetween`
|
||||
| `{ "query" : {
|
||||
"bool" : {
|
||||
"must" : [
|
||||
{"range" : {"price" : {"from" : ?, "to" : ?, "include_lower" : true, "include_upper" : true } } }
|
||||
]
|
||||
}
|
||||
}}`
|
||||
|
||||
| `LessThan`
|
||||
| `findByPriceLessThan`
|
||||
| `{ "query" : {
|
||||
"bool" : {
|
||||
"must" : [
|
||||
{"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : false } } }
|
||||
]
|
||||
}
|
||||
}}`
|
||||
|
||||
| `LessThanEqual`
|
||||
| `findByPriceLessThanEqual`
|
||||
| `{ "query" : {
|
||||
"bool" : {
|
||||
"must" : [
|
||||
{"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : true } } }
|
||||
]
|
||||
}
|
||||
}}`
|
||||
|
||||
| `GreaterThan`
|
||||
| `findByPriceGreaterThan`
|
||||
| `{ "query" : {
|
||||
"bool" : {
|
||||
"must" : [
|
||||
{"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : false, "include_upper" : true } } }
|
||||
]
|
||||
}
|
||||
}}`
|
||||
|
||||
|
||||
| `GreaterThanEqual`
|
||||
| `findByPriceGreaterThan`
|
||||
| `{ "query" : {
|
||||
"bool" : {
|
||||
"must" : [
|
||||
{"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : true, "include_upper" : true } } }
|
||||
]
|
||||
}
|
||||
}}`
|
||||
|
||||
| `Before`
|
||||
| `findByPriceBefore`
|
||||
| `{ "query" : {
|
||||
"bool" : {
|
||||
"must" : [
|
||||
{"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : true } } }
|
||||
]
|
||||
}
|
||||
}}`
|
||||
|
||||
| `After`
|
||||
| `findByPriceAfter`
|
||||
| `{ "query" : {
|
||||
"bool" : {
|
||||
"must" : [
|
||||
{"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : true, "include_upper" : true } } }
|
||||
]
|
||||
}
|
||||
}}`
|
||||
|
||||
| `Like`
|
||||
| `findByNameLike`
|
||||
| `{ "query" : {
|
||||
"bool" : {
|
||||
"must" : [
|
||||
{ "query_string" : { "query" : "?*", "fields" : [ "name" ] }, "analyze_wildcard": true }
|
||||
]
|
||||
}
|
||||
}}`
|
||||
|
||||
| `StartingWith`
|
||||
| `findByNameStartingWith`
|
||||
| `{ "query" : {
|
||||
"bool" : {
|
||||
"must" : [
|
||||
{ "query_string" : { "query" : "?*", "fields" : [ "name" ] }, "analyze_wildcard": true }
|
||||
]
|
||||
}
|
||||
}}`
|
||||
|
||||
| `EndingWith`
|
||||
| `findByNameEndingWith`
|
||||
| `{ "query" : {
|
||||
"bool" : {
|
||||
"must" : [
|
||||
{ "query_string" : { "query" : "*?", "fields" : [ "name" ] }, "analyze_wildcard": true }
|
||||
]
|
||||
}
|
||||
}}`
|
||||
|
||||
| `Contains/Containing`
|
||||
| `findByNameContaining`
|
||||
| `{ "query" : {
|
||||
"bool" : {
|
||||
"must" : [
|
||||
{ "query_string" : { "query" : "\*?*", "fields" : [ "name" ] }, "analyze_wildcard": true }
|
||||
]
|
||||
}
|
||||
}}`
|
||||
|
||||
| `In`
|
||||
| `findByNameIn(Collection<String>names)`
|
||||
| `{ "query" : {
|
||||
"bool" : {
|
||||
"must" : [
|
||||
{"bool" : {"must" : [
|
||||
{"terms" : {"name" : ["?","?"]}}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}}`
|
||||
|
||||
| `NotIn`
|
||||
| `findByNameNotIn(Collection<String>names)`
|
||||
| `{ "query" : {
|
||||
"bool" : {
|
||||
"must" : [
|
||||
{"bool" : {"must_not" : [
|
||||
{"terms" : {"name" : ["?","?"]}}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}}`
|
||||
|
||||
| `Near`
|
||||
| `findByStoreNear`
|
||||
| `Not Supported Yet !`
|
||||
|
||||
| `True`
|
||||
| `findByAvailableTrue`
|
||||
| `{ "query" : {
|
||||
"bool" : {
|
||||
"must" : [
|
||||
{ "query_string" : { "query" : "true", "fields" : [ "available" ] } }
|
||||
]
|
||||
}
|
||||
}}`
|
||||
|
||||
| `False`
|
||||
| `findByAvailableFalse`
|
||||
| `{ "query" : {
|
||||
"bool" : {
|
||||
"must" : [
|
||||
{ "query_string" : { "query" : "false", "fields" : [ "available" ] } }
|
||||
]
|
||||
}
|
||||
}}`
|
||||
|
||||
| `OrderBy`
|
||||
| `findByAvailableTrueOrderByNameDesc`
|
||||
| `{ "query" : {
|
||||
"bool" : {
|
||||
"must" : [
|
||||
{ "query_string" : { "query" : "true", "fields" : [ "available" ] } }
|
||||
]
|
||||
}
|
||||
}, "sort":[{"name":{"order":"desc"}}]
|
||||
}`
|
||||
|
||||
|===
|
||||
|
||||
== Method return types
|
||||
|
||||
Repository methods can be defined to have the following return types for returning multiple Elements:
|
||||
|
||||
* `List<T>`
|
||||
* `Stream<T>`
|
||||
* `SearchHits<T>`
|
||||
* `List<SearchHits<T>>`
|
||||
* `Stream<SearchHit<T>>`
|
||||
* `SearchPage<T>`
|
||||
|
||||
[[elasticsearch.query-methods.at-query]]
|
||||
== Using @Query Annotation
|
||||
|
||||
.Declare query at the method using the `@Query` annotation.
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
interface BookRepository extends ElasticsearchRepository<Book, String> {
|
||||
@Query("{\"match\": {\"name\": {\"query\": \"?0\"}}}")
|
||||
Page<Book> findByName(String name,Pageable pageable);
|
||||
}
|
||||
----
|
||||
The String that is set as the annotation argument must be a valid Elasticsearch JSON query. It will be sent to Easticsearch as value of the query element; if for example the function is called with the parameter _John_, it would produce the following query body:
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
"query": {
|
||||
"match": {
|
||||
"name": {
|
||||
"query": "John"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
@@ -1,124 +0,0 @@
|
||||
[[elasticsearch.reactive.operations]]
|
||||
= Reactive Elasticsearch Operations
|
||||
|
||||
`ReactiveElasticsearchOperations` is the gateway to executing high level commands against an Elasticsearch cluster using the `ReactiveElasticsearchClient`.
|
||||
|
||||
The `ReactiveElasticsearchTemplate` is the default implementation of `ReactiveElasticsearchOperations`.
|
||||
|
||||
[[elasticsearch.reactive.template]]
|
||||
== Reactive Elasticsearch Template
|
||||
|
||||
To get started the `ReactiveElasticsearchTemplate` needs to know about the actual client to work with.
|
||||
Please see <<elasticsearch.clients.reactive>> for details on the client.
|
||||
|
||||
[[elasticsearch.reactive.template.configuration]]
|
||||
=== Reactive Template Configuration
|
||||
|
||||
The easiest way of setting up the `ReactiveElasticsearchTemplate` is via `AbstractReactiveElasticsearchConfiguration` providing
|
||||
dedicated configuration method hooks for `base package`, the `initial entity set` etc.
|
||||
|
||||
.The AbstractReactiveElasticsearchConfiguration
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
public class Config extends AbstractReactiveElasticsearchConfiguration {
|
||||
|
||||
@Bean <1>
|
||||
@Override
|
||||
public ReactiveElasticsearchClient reactiveElasticsearchClient() {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> Configure the client to use. This can be done by `ReactiveRestClients` or directly via `DefaultReactiveElasticsearchClient`.
|
||||
====
|
||||
|
||||
NOTE: If applicable set default `HttpHeaders` via the `ClientConfiguration` of the `ReactiveElasticsearchClient`. See <<elasticsearch.clients.configuration>>.
|
||||
|
||||
TIP: If needed the `ReactiveElasticsearchTemplate` can be configured with default `RefreshPolicy` and `IndicesOptions` that get applied to the related requests by overriding the defaults of `refreshPolicy()` and `indicesOptions()`.
|
||||
|
||||
However one might want to be more in control over the actual components and use a more verbose approach.
|
||||
|
||||
.Configure the ReactiveElasticsearchTemplate
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
public class Config {
|
||||
|
||||
@Bean <1>
|
||||
public ReactiveElasticsearchClient reactiveElasticsearchClient() {
|
||||
// ...
|
||||
}
|
||||
@Bean <2>
|
||||
public ElasticsearchConverter elasticsearchConverter() {
|
||||
return new MappingElasticsearchConverter(elasticsearchMappingContext());
|
||||
}
|
||||
@Bean <3>
|
||||
public SimpleElasticsearchMappingContext elasticsearchMappingContext() {
|
||||
return new SimpleElasticsearchMappingContext();
|
||||
}
|
||||
@Bean <4>
|
||||
public ReactiveElasticsearchOperations reactiveElasticsearchOperations() {
|
||||
return new ReactiveElasticsearchTemplate(reactiveElasticsearchClient(), elasticsearchConverter());
|
||||
}
|
||||
}
|
||||
----
|
||||
<1> Configure the client to use. This can be done by `ReactiveRestClients` or directly via `DefaultReactiveElasticsearchClient`.
|
||||
<2> Set up the `ElasticsearchConverter` used for domain type mapping utilizing metadata provided by the mapping context.
|
||||
<3> The Elasticsearch specific mapping context for domain type metadata.
|
||||
<4> The actual template based on the client and conversion infrastructure.
|
||||
====
|
||||
|
||||
[[elasticsearch.reactive.template.usage]]
|
||||
=== Reactive Template Usage
|
||||
|
||||
`ReactiveElasticsearchTemplate` lets you save, find and delete your domain objects and map those objects to documents stored in Elasticsearch.
|
||||
|
||||
Consider the following:
|
||||
|
||||
.Use the ReactiveElasticsearchTemplate
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Document(indexName = "marvel")
|
||||
public class Person {
|
||||
|
||||
private @Id String id;
|
||||
private String name;
|
||||
private int age;
|
||||
// Getter/Setter omitted...
|
||||
}
|
||||
----
|
||||
|
||||
[source,java]
|
||||
----
|
||||
template.save(new Person("Bruce Banner", 42)) <1>
|
||||
.doOnNext(System.out::println)
|
||||
.flatMap(person -> template.findById(person.id, Person.class)) <2>
|
||||
.doOnNext(System.out::println)
|
||||
.flatMap(person -> template.delete(person)) <3>
|
||||
.doOnNext(System.out::println)
|
||||
.flatMap(id -> template.count(Person.class)) <4>
|
||||
.doOnNext(System.out::println)
|
||||
.subscribe(); <5>
|
||||
----
|
||||
|
||||
The above outputs the following sequence on the console.
|
||||
|
||||
[source,text]
|
||||
----
|
||||
> Person(id=QjWCWWcBXiLAnp77ksfR, name=Bruce Banner, age=42)
|
||||
> Person(id=QjWCWWcBXiLAnp77ksfR, name=Bruce Banner, age=42)
|
||||
> QjWCWWcBXiLAnp77ksfR
|
||||
> 0
|
||||
----
|
||||
<1> Insert a new `Person` document into the _marvel_ index under type _characters_. The `id` is generated on server side and set into the instance returned.
|
||||
<2> Lookup the `Person` with matching `id` in the _marvel_ index under type _characters_.
|
||||
<3> Delete the `Person` with matching `id`, extracted from the given instance, in the _marvel_ index under type _characters_.
|
||||
<4> Count the total number of documents in the _marvel_ index under type _characters_.
|
||||
<5> Don't forget to _subscribe()_.
|
||||
====
|
||||
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
[[elasticsearch.reactive.repositories]]
|
||||
= Reactive Elasticsearch Repositories
|
||||
|
||||
Reactive Elasticsearch repository support builds on the core repository support explained in <<repositories>> utilizing
|
||||
operations provided via <<elasticsearch.reactive.operations>> executed by a <<elasticsearch.clients.reactive>>.
|
||||
|
||||
Spring Data Elasticsearch reactive repository support uses https://projectreactor.io/[Project Reactor] as its reactive
|
||||
composition library of choice.
|
||||
|
||||
There are 3 main interfaces to be used:
|
||||
|
||||
* `ReactiveRepository`
|
||||
* `ReactiveCrudRepository`
|
||||
* `ReactiveSortingRepository`
|
||||
|
||||
[[elasticsearch.reactive.repositories.usage]]
|
||||
== Usage
|
||||
|
||||
To access domain objects stored in a Elasticsearch using a `Repository`, just create an interface for it.
|
||||
Before you can actually go on and do that you will need an entity.
|
||||
|
||||
.Sample `Person` entity
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public class Person {
|
||||
|
||||
@Id
|
||||
private String id;
|
||||
private String firstname;
|
||||
private String lastname;
|
||||
private Address address;
|
||||
|
||||
// … getters and setters omitted
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
NOTE: Please note that the `id` property needs to be of type `String`.
|
||||
|
||||
.Basic repository interface to persist Person entities
|
||||
====
|
||||
[source]
|
||||
----
|
||||
interface ReactivePersonRepository extends ReactiveSortingRepository<Person, String> {
|
||||
|
||||
Flux<Person> findByFirstname(String firstname); <1>
|
||||
|
||||
Flux<Person> findByFirstname(Publisher<String> firstname); <2>
|
||||
|
||||
Flux<Person> findByFirstnameOrderByLastname(String firstname); <3>
|
||||
|
||||
Flux<Person> findByFirstname(String firstname, Sort sort); <4>
|
||||
|
||||
Flux<Person> findByFirstname(String firstname, Pageable page); <5>
|
||||
|
||||
Mono<Person> findByFirstnameAndLastname(String firstname, String lastname); <6>
|
||||
|
||||
Mono<Person> findFirstByLastname(String lastname); <7>
|
||||
|
||||
@Query("{ \"bool\" : { \"must\" : { \"term\" : { \"lastname\" : \"?0\" } } } }")
|
||||
Flux<Person> findByLastname(String lastname); <8>
|
||||
|
||||
Mono<Long> countByFirstname(String firstname) <9>
|
||||
|
||||
Mono<Boolean> existsByFirstname(String firstname) <10>
|
||||
|
||||
Mono<Long> deleteByFirstname(String firstname) <11>
|
||||
}
|
||||
----
|
||||
<1> The method shows a query for all people with the given `lastname`.
|
||||
<2> Finder method awaiting input from `Publisher` to bind parameter value for `firstname`.
|
||||
<3> Finder method ordering matching documents by `lastname`.
|
||||
<4> Finder method ordering matching documents by the expression defined via the `Sort` parameter.
|
||||
<5> Use `Pageable` to pass offset and sorting parameters to the database.
|
||||
<6> Finder method concating criteria using `And` / `Or` keywords.
|
||||
<7> Find the first matching entity.
|
||||
<8> The method shows a query for all people with the given `lastname` looked up by running the annotated `@Query` with given
|
||||
parameters.
|
||||
<9> Count all entities with matching `firstname`.
|
||||
<10> Check if at least one entity with matching `firstname` exists.
|
||||
<11> Delete all entites with matching `firstname`.
|
||||
====
|
||||
|
||||
[[elasticsearch.reactive.repositories.configuration]]
|
||||
== Configuration
|
||||
|
||||
For Java configuration, use the `@EnableReactiveElasticsearchRepositories` annotation. If no base package is configured,
|
||||
the infrastructure scans the package of the annotated configuration class.
|
||||
|
||||
The following listing shows how to use Java configuration for a repository:
|
||||
|
||||
.Java configuration for repositories
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
@EnableReactiveElasticsearchRepositories
|
||||
public class Config extends AbstractReactiveElasticsearchConfiguration {
|
||||
|
||||
@Override
|
||||
public ReactiveElasticsearchClient reactiveElasticsearchClient() {
|
||||
return ReactiveRestClients.create(ClientConfiguration.localhost());
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
Because the repository from the previous example extends `ReactiveSortingRepository`, all CRUD operations are available
|
||||
as well as methods for sorted access to the entities. Working with the repository instance is a matter of dependency
|
||||
injecting it into a client, as the following example shows:
|
||||
|
||||
.Sorted access to Person entities
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
public class PersonRepositoryTests {
|
||||
|
||||
@Autowired ReactivePersonRepository repository;
|
||||
|
||||
@Test
|
||||
public void sortsElementsCorrectly() {
|
||||
|
||||
Flux<Person> persons = repository.findAll(Sort.by(new Order(ASC, "lastname")));
|
||||
|
||||
// ...
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
@@ -1,55 +1,51 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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 java.util.Map;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* ElasticsearchException
|
||||
*
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
public class ElasticsearchException extends RuntimeException {
|
||||
|
||||
@Nullable private Map<String, String> failedDocuments;
|
||||
|
||||
public ElasticsearchException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ElasticsearchException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ElasticsearchException(String message, Throwable cause, Map<String, String> failedDocuments) {
|
||||
super(message, cause);
|
||||
this.failedDocuments = failedDocuments;
|
||||
}
|
||||
|
||||
public ElasticsearchException(String message, Map<String, String> failedDocuments) {
|
||||
super(message);
|
||||
this.failedDocuments = failedDocuments;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Map<String, String> getFailedDocuments() {
|
||||
return failedDocuments;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright 2013 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
|
||||
*
|
||||
* 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 "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 java.util.Map;
|
||||
|
||||
/**
|
||||
* ElasticsearchException
|
||||
*
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
*/
|
||||
public class ElasticsearchException extends RuntimeException {
|
||||
|
||||
private Map<String, String> failedDocuments;
|
||||
|
||||
public ElasticsearchException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ElasticsearchException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ElasticsearchException(String message, Throwable cause, Map<String, String> failedDocuments) {
|
||||
super(message, cause);
|
||||
this.failedDocuments = failedDocuments;
|
||||
}
|
||||
|
||||
public ElasticsearchException(String message, Map<String, String> failedDocuments) {
|
||||
super(message);
|
||||
this.failedDocuments = failedDocuments;
|
||||
}
|
||||
|
||||
public Map<String, String> getFailedDocuments() {
|
||||
return failedDocuments;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2020 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.NonTransientDataAccessResourceException;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 3.2
|
||||
*/
|
||||
public class NoSuchIndexException extends NonTransientDataAccessResourceException {
|
||||
|
||||
private final String index;
|
||||
|
||||
public NoSuchIndexException(String index, Throwable cause) {
|
||||
super(String.format("Index %s not found.", index), cause);
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public String getIndex() {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
package org.springframework.data.elasticsearch.annotations;
|
||||
|
||||
import org.elasticsearch.search.suggest.completion.context.ContextMapping;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Based on reference doc - https://www.elastic.co/guide/en/elasticsearch/reference/current/suggester-context.html
|
||||
*
|
||||
* @author Robert Gruendler
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
@Documented
|
||||
@Inherited
|
||||
public @interface CompletionContext {
|
||||
|
||||
String name();
|
||||
|
||||
ContextMapping.Type type();
|
||||
|
||||
String precision() default "";
|
||||
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
package org.springframework.data.elasticsearch.annotations;
|
||||
|
||||
/**
|
||||
* Based on reference doc - https://www.elastic.co/guide/en/elasticsearch/reference/current/suggester-context.html
|
||||
*
|
||||
* @author Robert Gruendler
|
||||
*/
|
||||
public enum CompletionContextType {
|
||||
|
||||
CATEGORY, GEO
|
||||
|
||||
}
|
||||
+4
-12
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
* Copyright 2013 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
|
||||
* 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 "AS IS" BASIS,
|
||||
@@ -15,18 +15,12 @@
|
||||
*/
|
||||
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 java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Based on the reference doc - https://www.elastic.co/guide/en/elasticsearch/reference/current/search-suggesters-completion.html
|
||||
* Based on the reference doc - http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-suggesters-completion.html
|
||||
*
|
||||
* @author Mewes Kochheim
|
||||
* @author Robert Gruendler
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
@@ -43,6 +37,4 @@ public @interface CompletionField {
|
||||
boolean preservePositionIncrements() default true;
|
||||
|
||||
int maxInputLength() default 50;
|
||||
|
||||
CompletionContext[] contexts() default {};
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2014-2020 the original author or authors.
|
||||
* Copyright 2014 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
|
||||
* 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 "AS IS" BASIS,
|
||||
@@ -17,7 +17,7 @@ package org.springframework.data.elasticsearch.annotations;
|
||||
|
||||
/**
|
||||
* @author Jakub Vavrik
|
||||
* Values based on reference doc - https://www.elastic.co/guide/reference/mapping/date-format/
|
||||
* Values based on reference doc - http://www.elasticsearch.org/guide/reference/mapping/date-format/
|
||||
*/
|
||||
public enum DateFormat {
|
||||
none, custom, basic_date, basic_date_time, basic_date_time_no_millis, basic_ordinal_date, basic_ordinal_date_time,
|
||||
|
||||
@@ -1,103 +1,51 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Identifies a domain object to be persisted to Elasticsearch.
|
||||
*
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Mason Chan
|
||||
* @author Ivan Greene
|
||||
* @author Mark Paluch
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
@Persistent
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.TYPE })
|
||||
public @interface Document {
|
||||
|
||||
/**
|
||||
* Name of the Elasticsearch index.
|
||||
* <ul>
|
||||
* <li>Lowercase only</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
|
||||
* faster)</li>
|
||||
* </ul>
|
||||
*/
|
||||
String indexName();
|
||||
|
||||
/**
|
||||
* Mapping type name. <br/>
|
||||
* deprecated as Elasticsearch does not support this anymore
|
||||
* (@see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.3/removal-of-types.html">Elastisearch removal of types documentation</a>) and will remove it in
|
||||
* Elasticsearch 8.
|
||||
*
|
||||
* @deprecated since 4.0
|
||||
*/
|
||||
@Deprecated
|
||||
String type() default "";
|
||||
|
||||
/**
|
||||
* Use server-side settings when creating the index.
|
||||
*/
|
||||
boolean useServerConfiguration() default false;
|
||||
|
||||
/**
|
||||
* Number of shards for the index {@link #indexName()}. Used for index creation. <br/>
|
||||
* With version 4.0, the default value is changed from 5 to 1 to reflect the change in the default settings of
|
||||
* Elasticsearch which changed to 1 as well in Elasticsearch 7.0.
|
||||
*/
|
||||
short shards() default 1;
|
||||
|
||||
/**
|
||||
* Number of replicas for the index {@link #indexName()}. Used for index creation.
|
||||
*/
|
||||
short replicas() default 1;
|
||||
|
||||
/**
|
||||
* Refresh interval for the index {@link #indexName()}. Used for index creation.
|
||||
*/
|
||||
String refreshInterval() default "1s";
|
||||
|
||||
/**
|
||||
* Index storage type for the index {@link #indexName()}. Used for index creation.
|
||||
*/
|
||||
String indexStoreType() default "fs";
|
||||
|
||||
/**
|
||||
* Configuration whether to create an index on repository bootstrapping.
|
||||
*/
|
||||
boolean createIndex() default true;
|
||||
|
||||
/**
|
||||
* Configuration of version management.
|
||||
*/
|
||||
VersionType versionType() default VersionType.EXTERNAL;
|
||||
}
|
||||
/*
|
||||
* Copyright 2013 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
|
||||
*
|
||||
* 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 "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.*;
|
||||
|
||||
import org.springframework.data.annotation.Persistent;
|
||||
|
||||
/**
|
||||
* Document
|
||||
*
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Mason Chan
|
||||
*/
|
||||
|
||||
@Persistent
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE})
|
||||
public @interface Document {
|
||||
|
||||
String indexName();
|
||||
|
||||
String type() default "";
|
||||
|
||||
boolean useServerConfiguration() default false;
|
||||
|
||||
short shards() default 5;
|
||||
|
||||
short replicas() default 1;
|
||||
|
||||
String refreshInterval() default "1s";
|
||||
|
||||
String indexStoreType() default "fs";
|
||||
|
||||
boolean createIndex() default true;
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2020 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.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation to set the dynamic mapping mode
|
||||
* {@see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic.html">elasticsearch doc</a>}
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.0
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.TYPE, ElementType.FIELD })
|
||||
@Documented
|
||||
public @interface DynamicMapping {
|
||||
|
||||
DynamicMappingValue value() default DynamicMappingValue.True;
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
package org.springframework.data.elasticsearch.annotations;
|
||||
|
||||
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.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 ommited if {@link Mapping} annotation is used.
|
||||
*
|
||||
* @author Petr Kukral
|
||||
*/
|
||||
@Persistent
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE})
|
||||
public @interface DynamicTemplates {
|
||||
|
||||
String mappingPath() default "";
|
||||
|
||||
}
|
||||
@@ -1,151 +1,58 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.core.annotation.AliasFor;
|
||||
|
||||
/**
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Artur Konczak
|
||||
* @author Jonathan Yan
|
||||
* @author Jakub Vavrik
|
||||
* @author Kevin Leturc
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Xiao Yu
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
@Documented
|
||||
@Inherited
|
||||
public @interface Field {
|
||||
|
||||
/**
|
||||
* Alias for {@link #name}.
|
||||
*
|
||||
* @since 3.2
|
||||
*/
|
||||
@AliasFor("name")
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* The <em>name</em> to be used to store the field inside the document.
|
||||
* <p>
|
||||
* √5 If not set, the name of the annotated property is used.
|
||||
*
|
||||
* @since 3.2
|
||||
*/
|
||||
@AliasFor("value")
|
||||
String name() default "";
|
||||
|
||||
FieldType type() default FieldType.Auto;
|
||||
|
||||
boolean index() default true;
|
||||
|
||||
DateFormat format() default DateFormat.none;
|
||||
|
||||
String pattern() default "";
|
||||
|
||||
boolean store() default false;
|
||||
|
||||
boolean fielddata() default false;
|
||||
|
||||
String searchAnalyzer() default "";
|
||||
|
||||
String analyzer() default "";
|
||||
|
||||
String normalizer() default "";
|
||||
|
||||
String[] ignoreFields() default {};
|
||||
|
||||
boolean includeInParent() default false;
|
||||
|
||||
String[] copyTo() default {};
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
int ignoreAbove() default -1;
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
boolean coerce() default true;
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
boolean docValues() default true;
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
boolean ignoreMalformed() default false;
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
IndexOptions indexOptions() default IndexOptions.none;
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
boolean indexPhrases() default false;
|
||||
|
||||
/**
|
||||
* implemented as array to enable the empty default value
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
IndexPrefixes[] indexPrefixes() default {};
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
boolean norms() default true;
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
String nullValue() default "";
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
int positionIncrementGap() default -1;
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
Similarity similarity() default Similarity.Default;
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
TermVector termVector() default TermVector.none;
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
double scalingFactor() default 1;
|
||||
}
|
||||
/*
|
||||
* Copyright 2013 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
|
||||
*
|
||||
* 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 "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;
|
||||
|
||||
/**
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Artur Konczak
|
||||
* @author Jonathan Yan
|
||||
* @author Jakub Vavrik
|
||||
* @author Kevin Leturc
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
@Documented
|
||||
@Inherited
|
||||
public @interface Field {
|
||||
|
||||
FieldType type() default FieldType.Auto;
|
||||
|
||||
boolean index() default true;
|
||||
|
||||
DateFormat format() default DateFormat.none;
|
||||
|
||||
String pattern() default "";
|
||||
|
||||
boolean store() default false;
|
||||
|
||||
boolean fielddata() default false;
|
||||
|
||||
String searchAnalyzer() default "";
|
||||
|
||||
String analyzer() default "";
|
||||
|
||||
String[] ignoreFields() default {};
|
||||
|
||||
boolean includeInParent() default false;
|
||||
}
|
||||
|
||||
+8
-6
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2019-2020 the original author or authors.
|
||||
* Copyright 2014 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
|
||||
* 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 "AS IS" BASIS,
|
||||
@@ -16,9 +16,11 @@
|
||||
package org.springframework.data.elasticsearch.annotations;
|
||||
|
||||
/**
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.0
|
||||
* @author Artur Konczak
|
||||
* @author Mohsin Husen
|
||||
* @author Alexander Volz
|
||||
* @author Dennis Maaß
|
||||
*/
|
||||
public enum IndexOptions {
|
||||
none, docs, freqs, positions, offsets
|
||||
public enum FieldIndex {
|
||||
not_analyzed, analyzed, no
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
* Copyright 2013 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
|
||||
* 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 "AS IS" BASIS,
|
||||
@@ -19,35 +19,7 @@ package org.springframework.data.elasticsearch.annotations;
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Artur Konczak
|
||||
* @author Zeng Zetang
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
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
|
||||
text, Integer, Long, Date, Float, Double, Boolean, Object, Auto, Nested, Ip, Attachment, keyword
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
* Copyright 2013 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
|
||||
* 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 "AS IS" BASIS,
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020-2020 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.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.0
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Highlight {
|
||||
|
||||
HighlightParameters parameters() default @HighlightParameters;
|
||||
|
||||
HighlightField[] fields();
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020-2020 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.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.0
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface HighlightField {
|
||||
|
||||
/**
|
||||
* The name of the field to apply highlighting to. This must be the field name of the entity's property, not the name
|
||||
* of the field in the index mappings.
|
||||
*/
|
||||
String name() default "";
|
||||
|
||||
HighlightParameters parameters() default @HighlightParameters;
|
||||
}
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020-2020 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.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.0
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface HighlightParameters {
|
||||
String boundaryChars() default "";
|
||||
|
||||
int boundaryMaxScan() default -1;
|
||||
|
||||
String boundaryScanner() default "";
|
||||
|
||||
String boundaryScannerLocale() default "";
|
||||
|
||||
/**
|
||||
* only used for {@link Highlight}s.
|
||||
*/
|
||||
String encoder() default "";
|
||||
|
||||
boolean forceSource() default false;
|
||||
|
||||
String fragmenter() default "";
|
||||
|
||||
/**
|
||||
* only used for {@link HighlightField}s.
|
||||
*/
|
||||
int fragmentOffset() default -1;
|
||||
|
||||
int fragmentSize() default -1;
|
||||
|
||||
/**
|
||||
* only used for {@link HighlightField}s.
|
||||
*/
|
||||
String[] matchedFields() default {};
|
||||
|
||||
int noMatchSize() default -1;
|
||||
|
||||
int numberOfFragments() default -1;
|
||||
|
||||
String order() default "";
|
||||
|
||||
int phraseLimit() default -1;
|
||||
|
||||
String[] preTags() default {};
|
||||
|
||||
String[] postTags() default {};
|
||||
|
||||
boolean requireFieldMatch() default true;
|
||||
|
||||
/**
|
||||
* only used for {@link Highlight}s.
|
||||
*/
|
||||
String tagsSchema() default "";
|
||||
|
||||
String type() default "";
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2014-2020 the original author or authors.
|
||||
* Copyright 2014 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
|
||||
* 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 "AS IS" BASIS,
|
||||
@@ -21,11 +21,7 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author Artur Konczak
|
||||
* @author Mohsin Husen
|
||||
* @author Sascha Woo
|
||||
* @author Xiao Yu
|
||||
* @author Peter-Josef Meisch
|
||||
*
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
@@ -37,84 +33,11 @@ public @interface InnerField {
|
||||
|
||||
boolean index() default true;
|
||||
|
||||
DateFormat format() default DateFormat.none;
|
||||
|
||||
String pattern() default "";
|
||||
|
||||
boolean store() default false;
|
||||
|
||||
boolean fielddata() default false;
|
||||
|
||||
String searchAnalyzer() default "";
|
||||
|
||||
String analyzer() default "";
|
||||
|
||||
String normalizer() default "";
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
int ignoreAbove() default -1;
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
boolean coerce() default true;
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
boolean docValues() default true;
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
boolean ignoreMalformed() default false;
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
IndexOptions indexOptions() default IndexOptions.none;
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
boolean indexPhrases() default false;
|
||||
|
||||
/**
|
||||
* implemented as array to enable the empty default value
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
IndexPrefixes[] indexPrefixes() default {};
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
boolean norms() default true;
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
String nullValue() default "";
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
int positionIncrementGap() default -1;
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
Similarity similarity() default Similarity.Default;
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
TermVector termVector() default TermVector.none;
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
double scalingFactor() default 1;
|
||||
String indexAnalyzer() default "";
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2014-2020 the original author or authors.
|
||||
* Copyright 2014 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
|
||||
* 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 "AS IS" BASIS,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2014-2020 the original author or authors.
|
||||
* Copyright 2014 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
|
||||
* 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 "AS IS" BASIS,
|
||||
@@ -15,25 +15,20 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.annotations;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Artur Konczak
|
||||
* @author Jonathan Yan
|
||||
* @author Xiao Yu
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
@Documented
|
||||
public @interface MultiField {
|
||||
|
||||
Field mainField();
|
||||
public Field mainField();
|
||||
|
||||
InnerField[] otherFields() default {};
|
||||
public InnerField[] otherFields() default {};
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2014-2020 the original author or authors.
|
||||
* Copyright 2014 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
|
||||
* 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 "AS IS" BASIS,
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.*;
|
||||
|
||||
/**
|
||||
* Query
|
||||
*
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
*/
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@Documented
|
||||
public @interface Query {
|
||||
|
||||
/**
|
||||
* Elasticsearch query to be used when executing query. May contain placeholders eg. ?0
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* Named Query Named looked up by repository.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String name() default "";
|
||||
}
|
||||
/*
|
||||
* Copyright 2013 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
|
||||
*
|
||||
* 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 "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.*;
|
||||
|
||||
/**
|
||||
* Query
|
||||
*
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
*/
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@Documented
|
||||
public @interface Query {
|
||||
|
||||
/**
|
||||
* Elasticsearch query to be used when executing query. May contain placeholders eg. ?0
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* Named Query Named looked up by repository.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String name() default "";
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
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.annotation.ReadOnlyProperty;
|
||||
import org.springframework.data.elasticsearch.core.SearchHit;
|
||||
|
||||
/**
|
||||
* Specifies that this field is used for storing the document score.
|
||||
*
|
||||
* @author Sascha Woo
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 3.1
|
||||
* @deprecated since 4.0, use {@link SearchHit#getScore()} instead
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
@Documented
|
||||
@Inherited
|
||||
@ReadOnlyProperty
|
||||
@Deprecated
|
||||
public @interface Score {
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2014-2020 the original author or authors.
|
||||
* Copyright 2014 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
|
||||
* 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 "AS IS" BASIS,
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2020 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;
|
||||
|
||||
/**
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.0
|
||||
*/
|
||||
public enum Similarity {
|
||||
Default("default"), BM25("BM25"), classic("classic"), Boolean("boolean");
|
||||
|
||||
// need to use a custom name because 'boolean' can't be used as enum name
|
||||
private final String toStringName;
|
||||
|
||||
Similarity(String name) {
|
||||
this.toStringName = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toStringName;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
@org.springframework.lang.NonNullApi
|
||||
@org.springframework.lang.NonNullFields
|
||||
package org.springframework.data.elasticsearch.annotations;
|
||||
@@ -1,343 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* Configuration interface exposing common client configuration properties for Elasticsearch clients.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Huw Ayling-Miller
|
||||
* @author Henrique Amaral
|
||||
* @since 3.2
|
||||
*/
|
||||
public interface ClientConfiguration {
|
||||
|
||||
/**
|
||||
* Creates a new {@link ClientConfigurationBuilder} instance.
|
||||
*
|
||||
* @return a new {@link ClientConfigurationBuilder} instance.
|
||||
*/
|
||||
static ClientConfigurationBuilderWithRequiredEndpoint builder() {
|
||||
return new ClientConfigurationBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ClientConfiguration} instance configured to localhost.
|
||||
* <p/>
|
||||
*
|
||||
* <pre class="code">
|
||||
* // "localhost:9200"
|
||||
* ClientConfiguration configuration = ClientConfiguration.localhost();
|
||||
* </pre>
|
||||
*
|
||||
* @return a new {@link ClientConfiguration} instance
|
||||
* @see ClientConfigurationBuilder#connectedToLocalhost()
|
||||
*/
|
||||
static ClientConfiguration localhost() {
|
||||
return new ClientConfigurationBuilder().connectedToLocalhost().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ClientConfiguration} instance configured to a single host given {@code hostAndPort}.
|
||||
* <p/>
|
||||
* For example given the endpoint http://localhost:9200
|
||||
*
|
||||
* <pre class="code">
|
||||
* ClientConfiguration configuration = ClientConfiguration.create("localhost:9200");
|
||||
* </pre>
|
||||
*
|
||||
* @return a new {@link ClientConfigurationBuilder} instance.
|
||||
*/
|
||||
static ClientConfiguration create(String hostAndPort) {
|
||||
return new ClientConfigurationBuilder().connectedTo(hostAndPort).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ClientConfiguration} instance configured to a single host given {@link InetSocketAddress}.
|
||||
* <p/>
|
||||
* For example given the endpoint http://localhost:9200
|
||||
*
|
||||
* <pre class="code">
|
||||
* ClientConfiguration configuration = ClientConfiguration
|
||||
* .create(InetSocketAddress.createUnresolved("localhost", 9200));
|
||||
* </pre>
|
||||
*
|
||||
* @return a new {@link ClientConfigurationBuilder} instance.
|
||||
*/
|
||||
static ClientConfiguration create(InetSocketAddress socketAddress) {
|
||||
return new ClientConfigurationBuilder().connectedTo(socketAddress).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configured endpoints.
|
||||
*
|
||||
* @return the configured endpoints.
|
||||
*/
|
||||
List<InetSocketAddress> getEndpoints();
|
||||
|
||||
/**
|
||||
* Obtain the {@link HttpHeaders} to be used by default.
|
||||
*
|
||||
* @return the {@link HttpHeaders} to be used by default.
|
||||
*/
|
||||
HttpHeaders getDefaultHeaders();
|
||||
|
||||
/**
|
||||
* Returns {@literal true} when the client should use SSL.
|
||||
*
|
||||
* @return {@literal true} when the client should use SSL.
|
||||
*/
|
||||
boolean useSsl();
|
||||
|
||||
/**
|
||||
* Returns the {@link SSLContext} to use. Can be {@link Optional#empty()} if unconfigured.
|
||||
*
|
||||
* @return the {@link SSLContext} to use. Can be {@link Optional#empty()} if unconfigured.
|
||||
*/
|
||||
Optional<SSLContext> getSslContext();
|
||||
|
||||
/**
|
||||
* Returns the {@link HostnameVerifier} to use. Can be {@link Optional#empty()} if unconfigured.
|
||||
*
|
||||
* @return the {@link HostnameVerifier} to use. Can be {@link Optional#empty()} if unconfigured.
|
||||
*/
|
||||
Optional<HostnameVerifier> getHostNameVerifier();
|
||||
|
||||
/**
|
||||
* Returns the {@link java.time.Duration connect timeout}.
|
||||
*
|
||||
* @see java.net.Socket#connect(SocketAddress, int)
|
||||
* @see io.netty.channel.ChannelOption#CONNECT_TIMEOUT_MILLIS
|
||||
*/
|
||||
Duration getConnectTimeout();
|
||||
|
||||
/**
|
||||
* Returns the {@link java.time.Duration socket timeout} which is typically applied as SO-timeout/read timeout.
|
||||
*
|
||||
* @see java.net.Socket#setSoTimeout(int)
|
||||
* @see io.netty.handler.timeout.ReadTimeoutHandler
|
||||
* @see io.netty.handler.timeout.WriteTimeoutHandler
|
||||
*/
|
||||
Duration getSocketTimeout();
|
||||
|
||||
/**
|
||||
* Returns the path prefix that should be prepended to HTTP(s) requests for Elasticsearch behind a proxy.
|
||||
*
|
||||
* @return the path prefix.
|
||||
* @since 4.0
|
||||
*/
|
||||
String getPathPrefix();
|
||||
|
||||
/**
|
||||
* returns an optionally set proxy in the form host:port
|
||||
*
|
||||
* @return the optional proxy
|
||||
* @since 4.0
|
||||
*/
|
||||
Optional<String> getProxy();
|
||||
|
||||
/**
|
||||
* @return the function for configuring a WebClient.
|
||||
*/
|
||||
Function<WebClient, WebClient> getWebClientConfigurer();
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
interface ClientConfigurationBuilderWithRequiredEndpoint {
|
||||
|
||||
/**
|
||||
* @param hostAndPort the {@literal host} and {@literal port} formatted as String {@literal host:port}.
|
||||
* @return the {@link MaybeSecureClientConfigurationBuilder}.
|
||||
*/
|
||||
default MaybeSecureClientConfigurationBuilder connectedTo(String hostAndPort) {
|
||||
return connectedTo(new String[] { hostAndPort });
|
||||
}
|
||||
|
||||
/**
|
||||
* @param hostAndPorts the list of {@literal host} and {@literal port} combinations formatted as String
|
||||
* {@literal host:port}.
|
||||
* @return the {@link MaybeSecureClientConfigurationBuilder}.
|
||||
*/
|
||||
MaybeSecureClientConfigurationBuilder connectedTo(String... hostAndPorts);
|
||||
|
||||
/**
|
||||
* @param endpoint the {@literal host} and {@literal port}.
|
||||
* @return the {@link MaybeSecureClientConfigurationBuilder}.
|
||||
*/
|
||||
default MaybeSecureClientConfigurationBuilder connectedTo(InetSocketAddress endpoint) {
|
||||
return connectedTo(new InetSocketAddress[] { endpoint });
|
||||
}
|
||||
|
||||
/**
|
||||
* @param endpoints the list of {@literal host} and {@literal port} combinations.
|
||||
* @return the {@link MaybeSecureClientConfigurationBuilder}.
|
||||
*/
|
||||
MaybeSecureClientConfigurationBuilder connectedTo(InetSocketAddress... endpoints);
|
||||
|
||||
/**
|
||||
* Obviously for testing.
|
||||
*
|
||||
* @return the {@link MaybeSecureClientConfigurationBuilder}.
|
||||
*/
|
||||
default MaybeSecureClientConfigurationBuilder connectedToLocalhost() {
|
||||
return connectedTo("localhost:9200");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
interface MaybeSecureClientConfigurationBuilder extends TerminalClientConfigurationBuilder {
|
||||
|
||||
/**
|
||||
* Connect via {@literal https} <br />
|
||||
* <strong>NOTE</strong> You need to leave out the protocol in
|
||||
* {@link ClientConfigurationBuilderWithRequiredEndpoint#connectedTo(String)}.
|
||||
*
|
||||
* @return the {@link TerminalClientConfigurationBuilder}.
|
||||
*/
|
||||
TerminalClientConfigurationBuilder usingSsl();
|
||||
|
||||
/**
|
||||
* Connect via {@literal https} using the given {@link SSLContext}.<br />
|
||||
* <strong>NOTE</strong> You need to leave out the protocol in
|
||||
* {@link ClientConfigurationBuilderWithRequiredEndpoint#connectedTo(String)}.
|
||||
*
|
||||
* @return the {@link TerminalClientConfigurationBuilder}.
|
||||
*/
|
||||
TerminalClientConfigurationBuilder usingSsl(SSLContext sslContext);
|
||||
|
||||
/**
|
||||
* Connect via {@literal https} using the givens {@link SSLContext} and HostnameVerifier {@link HostnameVerifier}
|
||||
* .<br />
|
||||
* <strong>NOTE</strong> You need to leave out the protocol in
|
||||
* {@link ClientConfigurationBuilderWithRequiredEndpoint#connectedTo(String)}.
|
||||
*
|
||||
* @return the {@link TerminalClientConfigurationBuilder}.
|
||||
*/
|
||||
TerminalClientConfigurationBuilder usingSsl(SSLContext sslContext, HostnameVerifier hostnameVerifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
interface TerminalClientConfigurationBuilder {
|
||||
|
||||
/**
|
||||
* @param defaultHeaders must not be {@literal null}.
|
||||
* @return the {@link TerminalClientConfigurationBuilder}
|
||||
*/
|
||||
TerminalClientConfigurationBuilder withDefaultHeaders(HttpHeaders defaultHeaders);
|
||||
|
||||
/**
|
||||
* Configure the {@literal milliseconds} for the connect timeout.
|
||||
*
|
||||
* @param millis the timeout to use.
|
||||
* @return the {@link TerminalClientConfigurationBuilder}
|
||||
* @see #withConnectTimeout(Duration)
|
||||
*/
|
||||
default TerminalClientConfigurationBuilder withConnectTimeout(long millis) {
|
||||
return withConnectTimeout(Duration.ofMillis(millis));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a {@link java.time.Duration} connect timeout.
|
||||
*
|
||||
* @param timeout the timeout to use. Must not be {@literal null}.
|
||||
* @return the {@link TerminalClientConfigurationBuilder}
|
||||
* @see java.net.Socket#connect(SocketAddress, int)
|
||||
* @see io.netty.channel.ChannelOption#CONNECT_TIMEOUT_MILLIS
|
||||
*/
|
||||
TerminalClientConfigurationBuilder withConnectTimeout(Duration timeout);
|
||||
|
||||
/**
|
||||
* Configure the {@literal milliseconds} for the socket timeout.
|
||||
*
|
||||
* @param millis the timeout to use.
|
||||
* @return the {@link TerminalClientConfigurationBuilder}
|
||||
* @see #withSocketTimeout(Duration)
|
||||
*/
|
||||
default TerminalClientConfigurationBuilder withSocketTimeout(long millis) {
|
||||
return withSocketTimeout(Duration.ofMillis(millis));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a {@link java.time.Duration socket timeout} which is typically applied as SO-timeout/read timeout.
|
||||
*
|
||||
* @param timeout the timeout to use. Must not be {@literal null}.
|
||||
* @return the {@link TerminalClientConfigurationBuilder}
|
||||
* @see java.net.Socket#setSoTimeout(int)
|
||||
* @see io.netty.handler.timeout.ReadTimeoutHandler
|
||||
* @see io.netty.handler.timeout.WriteTimeoutHandler
|
||||
*/
|
||||
TerminalClientConfigurationBuilder withSocketTimeout(Duration timeout);
|
||||
|
||||
/**
|
||||
* Configure the username and password to be sent as a Basic Authentication header
|
||||
*
|
||||
* @param username the username. Must not be {@literal null}.
|
||||
* @param password the password. Must not be {@literal null}.
|
||||
* @return the {@link TerminalClientConfigurationBuilder}
|
||||
*/
|
||||
TerminalClientConfigurationBuilder withBasicAuth(String username, String password);
|
||||
|
||||
/**
|
||||
* Configure the path prefix that will be prepended to any HTTP(s) requests
|
||||
*
|
||||
* @param pathPrefix the pathPrefix.
|
||||
* @return the {@link TerminalClientConfigurationBuilder}
|
||||
* @since 4.0
|
||||
*/
|
||||
TerminalClientConfigurationBuilder withPathPrefix(String pathPrefix);
|
||||
|
||||
/**
|
||||
* @param proxy a proxy formatted as String {@literal host:port}.
|
||||
* @return the {@link TerminalClientConfigurationBuilder}.
|
||||
*/
|
||||
TerminalClientConfigurationBuilder withProxy(String proxy);
|
||||
|
||||
/**
|
||||
* set customization hook in case of a reactive configuration
|
||||
*
|
||||
* @param webClientConfigurer function to configure the WebClient
|
||||
* @return the {@link TerminalClientConfigurationBuilder}.
|
||||
*/
|
||||
TerminalClientConfigurationBuilder withWebClientConfigurer(Function<WebClient, WebClient> webClientConfigurer);
|
||||
|
||||
/**
|
||||
* Build the {@link ClientConfiguration} object.
|
||||
*
|
||||
* @return the {@link ClientConfiguration} object.
|
||||
*/
|
||||
ClientConfiguration build();
|
||||
}
|
||||
}
|
||||
-229
@@ -1,229 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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.net.InetSocketAddress;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
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.http.HttpHeaders;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* Default builder implementation for {@link ClientConfiguration}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Huw Ayling-Miller
|
||||
* @author Henrique Amaral
|
||||
* @since 3.2
|
||||
*/
|
||||
class ClientConfigurationBuilder
|
||||
implements ClientConfigurationBuilderWithRequiredEndpoint, MaybeSecureClientConfigurationBuilder {
|
||||
|
||||
private List<InetSocketAddress> hosts = new ArrayList<>();
|
||||
private HttpHeaders headers = HttpHeaders.EMPTY;
|
||||
private boolean useSsl;
|
||||
private @Nullable SSLContext sslContext;
|
||||
private @Nullable HostnameVerifier hostnameVerifier;
|
||||
private Duration connectTimeout = Duration.ofSeconds(10);
|
||||
private Duration soTimeout = Duration.ofSeconds(5);
|
||||
private @Nullable String username;
|
||||
private @Nullable String password;
|
||||
private @Nullable String pathPrefix;
|
||||
private @Nullable String proxy;
|
||||
private @Nullable Function<WebClient, WebClient> webClientConfigurer;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationBuilderWithRequiredEndpoint#connectedTo(java.lang.String[])
|
||||
*/
|
||||
@Override
|
||||
public MaybeSecureClientConfigurationBuilder connectedTo(String... hostAndPorts) {
|
||||
|
||||
Assert.notEmpty(hostAndPorts, "At least one host is required");
|
||||
|
||||
this.hosts.addAll(Arrays.stream(hostAndPorts).map(ClientConfigurationBuilder::parse).collect(Collectors.toList()));
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationBuilderWithRequiredEndpoint#connectedTo(java.net.InetSocketAddress[])
|
||||
*/
|
||||
@Override
|
||||
public MaybeSecureClientConfigurationBuilder connectedTo(InetSocketAddress... endpoints) {
|
||||
|
||||
Assert.notEmpty(endpoints, "At least one endpoint is required");
|
||||
|
||||
this.hosts.addAll(Arrays.asList(endpoints));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MaybeSecureClientConfigurationBuilder withProxy(String proxy) {
|
||||
Assert.hasLength(proxy, "proxy must not be null or empty");
|
||||
this.proxy = proxy;
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.ClientConfiguration.MaybeSecureClientConfigurationBuilder#usingSsl()
|
||||
*/
|
||||
@Override
|
||||
public TerminalClientConfigurationBuilder usingSsl() {
|
||||
|
||||
this.useSsl = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.ClientConfiguration.MaybeSecureClientConfigurationBuilder#usingSsl(javax.net.ssl.SSLContext)
|
||||
*/
|
||||
@Override
|
||||
public TerminalClientConfigurationBuilder usingSsl(SSLContext sslContext) {
|
||||
|
||||
Assert.notNull(sslContext, "SSL Context must not be null");
|
||||
|
||||
this.useSsl = true;
|
||||
this.sslContext = sslContext;
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.ClientConfiguration.MaybeSecureClientConfigurationBuilder#usingSsl(javax.net.ssl.SSLContext, javax.net.ssl.HostnameVerifier)
|
||||
*/
|
||||
@Override
|
||||
public TerminalClientConfigurationBuilder usingSsl(SSLContext sslContext, HostnameVerifier hostnameVerifier) {
|
||||
|
||||
Assert.notNull(sslContext, "SSL Context must not be null");
|
||||
Assert.notNull(hostnameVerifier, "Host Name Verifier must not be null");
|
||||
|
||||
this.useSsl = true;
|
||||
this.sslContext = sslContext;
|
||||
this.hostnameVerifier = hostnameVerifier;
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.ClientConfiguration.TerminalClientConfigurationBuilder#withDefaultHeaders(org.springframework.http.HttpHeaders)
|
||||
*/
|
||||
@Override
|
||||
public TerminalClientConfigurationBuilder withDefaultHeaders(HttpHeaders defaultHeaders) {
|
||||
|
||||
Assert.notNull(defaultHeaders, "Default HTTP headers must not be null");
|
||||
|
||||
this.headers = new HttpHeaders();
|
||||
this.headers.addAll(defaultHeaders);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.ClientConfiguration.TerminalClientConfigurationBuilder#withConnectTimeout(java.time.Duration)
|
||||
*/
|
||||
@Override
|
||||
public TerminalClientConfigurationBuilder withConnectTimeout(Duration timeout) {
|
||||
|
||||
Assert.notNull(timeout, "I/O timeout must not be null!");
|
||||
|
||||
this.connectTimeout = timeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.ClientConfiguration.TerminalClientConfigurationBuilder#withTimeout(java.time.Duration)
|
||||
*/
|
||||
@Override
|
||||
public TerminalClientConfigurationBuilder withSocketTimeout(Duration timeout) {
|
||||
|
||||
Assert.notNull(timeout, "Socket timeout must not be null!");
|
||||
|
||||
this.soTimeout = timeout;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminalClientConfigurationBuilder withBasicAuth(String username, String password) {
|
||||
|
||||
Assert.notNull(username, "username must not be null");
|
||||
Assert.notNull(password, "password must not be null");
|
||||
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminalClientConfigurationBuilder withPathPrefix(String pathPrefix) {
|
||||
this.pathPrefix = pathPrefix;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminalClientConfigurationBuilder withWebClientConfigurer(Function<WebClient, WebClient> webClientConfigurer) {
|
||||
|
||||
Assert.notNull(webClientConfigurer, "webClientConfigurer must not be null");
|
||||
|
||||
this.webClientConfigurer = webClientConfigurer;
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.ClientConfiguration.ClientConfigurationBuilderWithOptionalDefaultHeaders#build()
|
||||
*/
|
||||
@Override
|
||||
public ClientConfiguration build() {
|
||||
|
||||
if (username != null && password != null) {
|
||||
if (HttpHeaders.EMPTY.equals(headers)) {
|
||||
headers = new HttpHeaders();
|
||||
}
|
||||
headers.setBasicAuth(username, password);
|
||||
}
|
||||
|
||||
return new DefaultClientConfiguration(hosts, headers, useSsl, sslContext, soTimeout, connectTimeout, pathPrefix,
|
||||
hostnameVerifier, proxy, webClientConfigurer);
|
||||
}
|
||||
|
||||
private static InetSocketAddress parse(String hostAndPort) {
|
||||
return InetSocketAddressParser.parse(hostAndPort, ElasticsearchHost.DEFAULT_PORT);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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.util.function.Supplier;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Logging Utility to log client requests and responses. Logs client requests and responses to Elasticsearch to a
|
||||
* dedicated logger: {@code org.springframework.data.elasticsearch.client.WIRE} on {@link org.slf4j.event.Level#TRACE}
|
||||
* level.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 3.2
|
||||
*/
|
||||
public abstract class ClientLogger {
|
||||
|
||||
private static final String lineSeparator = System.getProperty("line.separator");
|
||||
private static final Logger WIRE_LOGGER = LoggerFactory
|
||||
.getLogger("org.springframework.data.elasticsearch.client.WIRE");
|
||||
|
||||
private ClientLogger() {}
|
||||
|
||||
/**
|
||||
* Returns {@literal true} if the logger is enabled.
|
||||
*
|
||||
* @return {@literal true} if the logger is enabled.
|
||||
*/
|
||||
public static boolean isEnabled() {
|
||||
return WIRE_LOGGER.isTraceEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an outgoing HTTP request.
|
||||
*
|
||||
* @param logId the correlation Id, see {@link #newLogId()}.
|
||||
* @param method HTTP method
|
||||
* @param endpoint URI
|
||||
* @param parameters optional parameters.
|
||||
*/
|
||||
public static void logRequest(String logId, String method, String endpoint, Object parameters) {
|
||||
|
||||
if (isEnabled()) {
|
||||
|
||||
WIRE_LOGGER.trace("[{}] Sending request {} {} with parameters: {}", logId, method.toUpperCase(), endpoint,
|
||||
parameters);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an outgoing HTTP request with a request body.
|
||||
*
|
||||
* @param logId the correlation Id, see {@link #newLogId()}.
|
||||
* @param method HTTP method
|
||||
* @param endpoint URI
|
||||
* @param parameters optional parameters.
|
||||
* @param body body content supplier.
|
||||
*/
|
||||
public static void logRequest(String logId, String method, String endpoint, Object parameters,
|
||||
Supplier<Object> body) {
|
||||
|
||||
if (isEnabled()) {
|
||||
|
||||
WIRE_LOGGER.trace("[{}] Sending request {} {} with parameters: {}{}Request body: {}", logId, method.toUpperCase(),
|
||||
endpoint, parameters, lineSeparator, body.get());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a raw HTTP response without logging the body.
|
||||
*
|
||||
* @param logId the correlation Id, see {@link #newLogId()}.
|
||||
* @param statusCode the HTTP status code.
|
||||
*/
|
||||
public static void logRawResponse(String logId, HttpStatus statusCode) {
|
||||
|
||||
if (isEnabled()) {
|
||||
WIRE_LOGGER.trace("[{}] Received raw response: {}", logId, statusCode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a raw HTTP response along with the body.
|
||||
*
|
||||
* @param logId the correlation Id, see {@link #newLogId()}.
|
||||
* @param statusCode the HTTP status code.
|
||||
* @param body body content.
|
||||
*/
|
||||
public static void logResponse(String logId, HttpStatus statusCode, String body) {
|
||||
|
||||
if (isEnabled()) {
|
||||
WIRE_LOGGER.trace("[{}] Received response: {}{}Response body: {}", logId, statusCode, lineSeparator, body);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new, unique correlation Id to improve tracing across log events.
|
||||
*
|
||||
* @return a new, unique correlation Id.
|
||||
*/
|
||||
public static String newLogId() {
|
||||
|
||||
if (!isEnabled()) {
|
||||
return "-";
|
||||
}
|
||||
|
||||
return ObjectUtils.getIdentityHexString(new Object());
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.elasticsearch.common.transport.TransportAddress;
|
||||
import org.springframework.data.util.Streamable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Value object to represent a list of cluster nodes.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @since 3.1
|
||||
*/
|
||||
class ClusterNodes implements Streamable<TransportAddress> {
|
||||
|
||||
public static ClusterNodes DEFAULT = ClusterNodes.of("127.0.0.1:9300");
|
||||
|
||||
private static final String COLON = ":";
|
||||
private static final String COMMA = ",";
|
||||
|
||||
private final List<TransportAddress> clusterNodes;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ClusterNodes} by parsing the given source.
|
||||
*
|
||||
* @param source must not be {@literal null} or empty.
|
||||
*/
|
||||
private ClusterNodes(String source) {
|
||||
|
||||
Assert.hasText(source, "Cluster nodes source must not be null or empty!");
|
||||
|
||||
String[] nodes = StringUtils.delimitedListToStringArray(source, COMMA);
|
||||
|
||||
this.clusterNodes = Arrays.stream(nodes).map(node -> {
|
||||
|
||||
String[] segments = StringUtils.delimitedListToStringArray(node, COLON);
|
||||
|
||||
Assert.isTrue(segments.length == 2,
|
||||
() -> String.format("Invalid cluster node %s in %s! Must be in the format host:port!", node, source));
|
||||
|
||||
String host = segments[0].trim();
|
||||
String port = segments[1].trim();
|
||||
|
||||
Assert.hasText(host, () -> String.format("No host name given cluster node %s!", node));
|
||||
Assert.hasText(port, () -> String.format("No port given in cluster node %s!", node));
|
||||
|
||||
return new TransportAddress(toInetAddress(host), Integer.parseInt(port));
|
||||
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
*/
|
||||
@Override
|
||||
public Iterator<TransportAddress> iterator() {
|
||||
return clusterNodes.iterator();
|
||||
}
|
||||
|
||||
private static InetAddress toInetAddress(String host) {
|
||||
|
||||
try {
|
||||
return InetAddress.getByName(host);
|
||||
} catch (UnknownHostException o_O) {
|
||||
throw new IllegalArgumentException(o_O);
|
||||
}
|
||||
}
|
||||
}
|
||||
-120
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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.net.InetSocketAddress;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* Default {@link ClientConfiguration} implementation.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @author Huw Ayling-Miller
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 3.2
|
||||
*/
|
||||
class DefaultClientConfiguration implements ClientConfiguration {
|
||||
|
||||
private final List<InetSocketAddress> hosts;
|
||||
private final HttpHeaders headers;
|
||||
private final boolean useSsl;
|
||||
private final @Nullable SSLContext sslContext;
|
||||
private final Duration soTimeout;
|
||||
private final Duration connectTimeout;
|
||||
private final String pathPrefix;
|
||||
private final @Nullable HostnameVerifier hostnameVerifier;
|
||||
private final String proxy;
|
||||
private final Function<WebClient, WebClient> webClientConfigurer;
|
||||
|
||||
DefaultClientConfiguration(List<InetSocketAddress> hosts, HttpHeaders headers, boolean useSsl,
|
||||
@Nullable SSLContext sslContext, Duration soTimeout, Duration connectTimeout, @Nullable String pathPrefix,
|
||||
@Nullable HostnameVerifier hostnameVerifier, String proxy, Function<WebClient, WebClient> webClientConfigurer) {
|
||||
|
||||
this.hosts = Collections.unmodifiableList(new ArrayList<>(hosts));
|
||||
this.headers = new HttpHeaders(headers);
|
||||
this.useSsl = useSsl;
|
||||
this.sslContext = sslContext;
|
||||
this.soTimeout = soTimeout;
|
||||
this.connectTimeout = connectTimeout;
|
||||
this.pathPrefix = pathPrefix;
|
||||
this.hostnameVerifier = hostnameVerifier;
|
||||
this.proxy = proxy;
|
||||
this.webClientConfigurer = webClientConfigurer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InetSocketAddress> getEndpoints() {
|
||||
return this.hosts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getDefaultHeaders() {
|
||||
return this.headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useSsl() {
|
||||
return this.useSsl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<SSLContext> getSslContext() {
|
||||
return Optional.ofNullable(this.sslContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<HostnameVerifier> getHostNameVerifier() {
|
||||
return Optional.ofNullable(this.hostnameVerifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getConnectTimeout() {
|
||||
return this.connectTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getSocketTimeout() {
|
||||
return this.soTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPathPrefix() {
|
||||
return this.pathPrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getProxy() {
|
||||
return Optional.ofNullable(proxy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<WebClient, WebClient> getWebClientConfigurer() {
|
||||
return webClientConfigurer != null ? webClientConfigurer : Function.identity();
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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.net.InetSocketAddress;
|
||||
import java.time.Instant;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Value Object containing information about Elasticsearch cluster nodes.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 3.2
|
||||
*/
|
||||
public class ElasticsearchHost {
|
||||
|
||||
/**
|
||||
* Default HTTP port for Elasticsearch servers.
|
||||
*/
|
||||
public static final int DEFAULT_PORT = 9200;
|
||||
|
||||
private final InetSocketAddress endpoint;
|
||||
private final State state;
|
||||
private final Instant timestamp;
|
||||
|
||||
public ElasticsearchHost(InetSocketAddress endpoint, State state) {
|
||||
|
||||
Assert.notNull(endpoint, "Host must not be null");
|
||||
Assert.notNull(state, "State must not be null");
|
||||
|
||||
this.endpoint = endpoint;
|
||||
this.state = state;
|
||||
this.timestamp = Instant.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param host must not be {@literal null}.
|
||||
* @return new instance of {@link ElasticsearchHost}.
|
||||
*/
|
||||
public static ElasticsearchHost online(InetSocketAddress host) {
|
||||
return new ElasticsearchHost(host, State.ONLINE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param host must not be {@literal null}.
|
||||
* @return new instance of {@link ElasticsearchHost}.
|
||||
*/
|
||||
public static ElasticsearchHost offline(InetSocketAddress host) {
|
||||
return new ElasticsearchHost(host, State.OFFLINE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a {@literal hostAndPort} string into a {@link InetSocketAddress}.
|
||||
*
|
||||
* @param hostAndPort the string containing host and port or IP address and port in the format {@code host:port}.
|
||||
* @return the parsed {@link InetSocketAddress}.
|
||||
*/
|
||||
public static InetSocketAddress parse(String hostAndPort) {
|
||||
return InetSocketAddressParser.parse(hostAndPort, DEFAULT_PORT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@literal true} if the last known {@link State} was {@link State#ONLINE}
|
||||
*/
|
||||
public boolean isOnline() {
|
||||
return State.ONLINE.equals(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public InetSocketAddress getEndpoint() {
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the last known {@link State}.
|
||||
*/
|
||||
public State getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link Instant} the information was captured.
|
||||
*/
|
||||
public Instant getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ElasticsearchHost(" + endpoint + ", " + state.name() + ')';
|
||||
}
|
||||
|
||||
public enum State {
|
||||
ONLINE, OFFLINE, UNKNOWN
|
||||
}
|
||||
}
|
||||
-117
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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.net.InetSocketAddress;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Utility to parse endpoints in {@code host:port} format into {@link java.net.InetSocketAddress}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 3.2
|
||||
*/
|
||||
class InetSocketAddressParser {
|
||||
|
||||
/**
|
||||
* Parse a host and port string into a {@link InetSocketAddress}.
|
||||
*
|
||||
* @param hostPortString Hostname/IP address and port formatted as {@code host:port} or {@code host}.
|
||||
* @param defaultPort default port to apply if {@code hostPostString} does not contain a port.
|
||||
* @return a {@link InetSocketAddress} that is unresolved to avoid DNS lookups.
|
||||
* @see InetSocketAddress#createUnresolved(String, int)
|
||||
*/
|
||||
static InetSocketAddress parse(String hostPortString, int defaultPort) {
|
||||
|
||||
Assert.notNull(hostPortString, "HostPortString must not be null");
|
||||
String host;
|
||||
String portString = null;
|
||||
|
||||
if (hostPortString.startsWith("[")) {
|
||||
String[] hostAndPort = getHostAndPortFromBracketedHost(hostPortString);
|
||||
host = hostAndPort[0];
|
||||
portString = hostAndPort[1];
|
||||
} else {
|
||||
int colonPos = hostPortString.indexOf(':');
|
||||
if (colonPos >= 0 && hostPortString.indexOf(':', colonPos + 1) == -1) {
|
||||
// Exactly 1 colon. Split into host:port.
|
||||
host = hostPortString.substring(0, colonPos);
|
||||
portString = hostPortString.substring(colonPos + 1);
|
||||
} else {
|
||||
// 0 or 2+ colons. Bare hostname or IPv6 literal.
|
||||
host = hostPortString;
|
||||
}
|
||||
}
|
||||
|
||||
int port = defaultPort;
|
||||
if (StringUtils.hasText(portString)) {
|
||||
// Try to parse the whole port string as a number.
|
||||
Assert.isTrue(!portString.startsWith("+"), String.format("Cannot parse port number: %s", hostPortString));
|
||||
try {
|
||||
port = Integer.parseInt(portString);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException(String.format("Cannot parse port number: %s", hostPortString));
|
||||
}
|
||||
|
||||
Assert.isTrue(isValidPort(port), String.format("Port number out of range: %s", hostPortString));
|
||||
}
|
||||
|
||||
return InetSocketAddress.createUnresolved(host, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a bracketed host-port string, throwing IllegalArgumentException if parsing fails.
|
||||
*
|
||||
* @param hostPortString the full bracketed host-port specification. Post might not be specified.
|
||||
* @return an array with 2 strings: host and port, in that order.
|
||||
* @throws IllegalArgumentException if parsing the bracketed host-port string fails.
|
||||
*/
|
||||
private static String[] getHostAndPortFromBracketedHost(String hostPortString) {
|
||||
|
||||
Assert.isTrue(hostPortString.charAt(0) == '[',
|
||||
String.format("Bracketed host-port string must start with a bracket: %s", hostPortString));
|
||||
|
||||
int colonIndex = hostPortString.indexOf(':');
|
||||
int closeBracketIndex = hostPortString.lastIndexOf(']');
|
||||
|
||||
Assert.isTrue(colonIndex > -1 && closeBracketIndex > colonIndex,
|
||||
String.format("Invalid bracketed host/port: %s", hostPortString));
|
||||
|
||||
String host = hostPortString.substring(1, closeBracketIndex);
|
||||
if (closeBracketIndex + 1 == hostPortString.length()) {
|
||||
return new String[] { host, "" };
|
||||
} else {
|
||||
|
||||
Assert.isTrue(hostPortString.charAt(closeBracketIndex + 1) == ':',
|
||||
"Only a colon may follow a close bracket: " + hostPortString);
|
||||
for (int i = closeBracketIndex + 2; i < hostPortString.length(); ++i) {
|
||||
Assert.isTrue(Character.isDigit(hostPortString.charAt(i)),
|
||||
String.format("Port must be numeric: %s", hostPortString));
|
||||
}
|
||||
return new String[] { host, hostPortString.substring(closeBracketIndex + 2) };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param port the port number
|
||||
* @return {@literal true} for valid port numbers.
|
||||
*/
|
||||
private static boolean isValidPort(int port) {
|
||||
return port >= 0 && port <= 65535;
|
||||
}
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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.util.Set;
|
||||
|
||||
/**
|
||||
* {@link RuntimeException} to be emitted / thrown when the cluster is down (aka none of the known nodes is reachable).
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 3.2
|
||||
*/
|
||||
public class NoReachableHostException extends RuntimeException {
|
||||
|
||||
public NoReachableHostException(Set<ElasticsearchHost> hosts) {
|
||||
super(createMessage(hosts));
|
||||
}
|
||||
|
||||
public NoReachableHostException(Set<ElasticsearchHost> hosts, Throwable cause) {
|
||||
super(createMessage(hosts), cause);
|
||||
}
|
||||
|
||||
private static String createMessage(Set<ElasticsearchHost> hosts) {
|
||||
|
||||
if (hosts.size() == 1) {
|
||||
return String.format("Host '%s' not reachable. Cluster state is offline.", hosts.iterator().next().getEndpoint());
|
||||
}
|
||||
|
||||
return String.format("No active host found in cluster. (%s) of (%s) nodes offline.", hosts.size(), hosts.size());
|
||||
}
|
||||
}
|
||||
+31
-58
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2015-2020 the original author or authors.
|
||||
* Copyright 2015-2017 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
|
||||
* 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 "AS IS" BASIS,
|
||||
@@ -18,11 +18,9 @@ package org.springframework.data.elasticsearch.client;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
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;
|
||||
@@ -32,63 +30,42 @@ 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;
|
||||
import static java.util.Arrays.*;
|
||||
|
||||
/**
|
||||
* NodeClientFactoryBean
|
||||
*
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Ilkang Na
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
|
||||
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;
|
||||
private String clusterName;
|
||||
private NodeClient nodeClient;
|
||||
private String pathData;
|
||||
private String pathHome;
|
||||
private 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
|
||||
}
|
||||
super(InternalSettingsPreparer.prepareEnvironment(preparedSettings, null), classpathPlugins);
|
||||
}
|
||||
}
|
||||
|
||||
NodeClientFactoryBean() {}
|
||||
NodeClientFactoryBean() {
|
||||
}
|
||||
|
||||
public NodeClientFactoryBean(boolean local) {
|
||||
this.local = local;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeClient getObject() {
|
||||
|
||||
if (nodeClient == null) {
|
||||
throw new FactoryBeanNotInitializedException();
|
||||
}
|
||||
|
||||
public NodeClient getObject() throws Exception {
|
||||
return nodeClient;
|
||||
}
|
||||
|
||||
@@ -105,25 +82,24 @@ public class NodeClientFactoryBean implements FactoryBean<Client>, InitializingB
|
||||
@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();
|
||||
nodeClient = (NodeClient) new TestNode(
|
||||
Settings.builder().put(loadConfig())
|
||||
.put("transport.type", "netty4")
|
||||
.put("transport.type", "local")
|
||||
.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)
|
||||
.put("script.inline", "true")
|
||||
.build(), asList(Netty4Plugin.class)).start().client();
|
||||
}
|
||||
|
||||
private Settings loadConfig() throws IOException {
|
||||
if (!StringUtils.isEmpty(pathConfiguration)) {
|
||||
if (StringUtils.isNotBlank(pathConfiguration)) {
|
||||
InputStream stream = getClass().getClassLoader().getResourceAsStream(pathConfiguration);
|
||||
if (stream != null) {
|
||||
return Settings.builder().loadFromStream(pathConfiguration,
|
||||
getClass().getClassLoader().getResourceAsStream(pathConfiguration), false).build();
|
||||
return Settings.builder().loadFromStream(pathConfiguration, getClass().getClassLoader().getResourceAsStream(pathConfiguration)).build();
|
||||
}
|
||||
logger.error(String.format("Unable to read node configuration from file [%s]", pathConfiguration));
|
||||
}
|
||||
@@ -155,14 +131,11 @@ public class NodeClientFactoryBean implements FactoryBean<Client>, InitializingB
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
public void destroy() throws Exception {
|
||||
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;
|
||||
logger.info("Closing elasticSearch client");
|
||||
if (nodeClient != null) {
|
||||
nodeClient.close();
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
logger.error("Error closing ElasticSearch client: ", e);
|
||||
|
||||
-102
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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 lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.apache.http.HttpHost;
|
||||
import org.elasticsearch.client.RestClient;
|
||||
import org.elasticsearch.client.RestHighLevelClient;
|
||||
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.Assert;
|
||||
|
||||
/**
|
||||
* RestClientFactoryBean
|
||||
*
|
||||
* @author Don Wellington
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
@Slf4j
|
||||
public class RestClientFactoryBean implements FactoryBean<RestHighLevelClient>, InitializingBean, DisposableBean {
|
||||
|
||||
private @Nullable RestHighLevelClient client;
|
||||
private String hosts = "http://localhost:9200";
|
||||
static final String COMMA = ",";
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
try {
|
||||
log.info("Closing elasticSearch client");
|
||||
if (client != null) {
|
||||
client.close();
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
log.error("Error closing ElasticSearch client: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
buildClient();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestHighLevelClient getObject() {
|
||||
|
||||
if (client == null) {
|
||||
throw new FactoryBeanNotInitializedException();
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return RestHighLevelClient.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void buildClient() throws Exception {
|
||||
|
||||
Assert.hasText(hosts, "[Assertion Failed] At least one host must be set.");
|
||||
|
||||
ArrayList<HttpHost> httpHosts = new ArrayList<>();
|
||||
for (String host : hosts.split(COMMA)) {
|
||||
URL hostUrl = new URL(host);
|
||||
httpHosts.add(new HttpHost(hostUrl.getHost(), hostUrl.getPort(), hostUrl.getProtocol()));
|
||||
}
|
||||
client = new RestHighLevelClient(RestClient.builder(httpHosts.toArray(new HttpHost[httpHosts.size()])));
|
||||
}
|
||||
|
||||
public void setHosts(String hosts) {
|
||||
this.hosts = hosts;
|
||||
}
|
||||
|
||||
public String getHosts() {
|
||||
return this.hosts;
|
||||
}
|
||||
}
|
||||
@@ -1,213 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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.ByteArrayOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpEntityEnclosingRequest;
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.HttpRequest;
|
||||
import org.apache.http.HttpRequestInterceptor;
|
||||
import org.apache.http.HttpResponse;
|
||||
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.message.BasicHeader;
|
||||
import org.apache.http.protocol.HttpContext;
|
||||
import org.elasticsearch.client.RestClient;
|
||||
import org.elasticsearch.client.RestClientBuilder;
|
||||
import org.elasticsearch.client.RestHighLevelClient;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Utility class for common access to Elasticsearch clients. {@link RestClients} consolidates set up routines for the
|
||||
* various drivers into a single place.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @author Huw Ayling-Miller
|
||||
* @author Henrique Amaral
|
||||
* @since 3.2
|
||||
*/
|
||||
public final class RestClients {
|
||||
|
||||
/**
|
||||
* Name of whose value can be used to correlate log messages for this request.
|
||||
*/
|
||||
private static final String LOG_ID_ATTRIBUTE = RestClients.class.getName() + ".LOG_ID";
|
||||
|
||||
private RestClients() {}
|
||||
|
||||
/**
|
||||
* Start here to create a new client tailored to your needs.
|
||||
*
|
||||
* @return new instance of {@link ElasticsearchRestClient}.
|
||||
*/
|
||||
public static ElasticsearchRestClient create(ClientConfiguration clientConfiguration) {
|
||||
|
||||
Assert.notNull(clientConfiguration, "ClientConfiguration must not be null!");
|
||||
|
||||
HttpHost[] httpHosts = formattedHosts(clientConfiguration.getEndpoints(), clientConfiguration.useSsl()).stream()
|
||||
.map(HttpHost::create).toArray(HttpHost[]::new);
|
||||
RestClientBuilder builder = RestClient.builder(httpHosts);
|
||||
|
||||
if (clientConfiguration.getPathPrefix() != null) {
|
||||
builder.setPathPrefix(clientConfiguration.getPathPrefix());
|
||||
}
|
||||
|
||||
HttpHeaders headers = clientConfiguration.getDefaultHeaders();
|
||||
|
||||
if (!headers.isEmpty()) {
|
||||
|
||||
Header[] httpHeaders = headers.toSingleValueMap().entrySet().stream()
|
||||
.map(it -> new BasicHeader(it.getKey(), it.getValue())).toArray(Header[]::new);
|
||||
builder.setDefaultHeaders(httpHeaders);
|
||||
}
|
||||
|
||||
builder.setHttpClientConfigCallback(clientBuilder -> {
|
||||
|
||||
Optional<SSLContext> sslContext = clientConfiguration.getSslContext();
|
||||
Optional<HostnameVerifier> hostNameVerifier = clientConfiguration.getHostNameVerifier();
|
||||
sslContext.ifPresent(clientBuilder::setSSLContext);
|
||||
hostNameVerifier.ifPresent(clientBuilder::setSSLHostnameVerifier);
|
||||
|
||||
if (ClientLogger.isEnabled()) {
|
||||
|
||||
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
|
||||
|
||||
clientBuilder.addInterceptorLast((HttpRequestInterceptor) interceptor);
|
||||
clientBuilder.addInterceptorLast((HttpResponseInterceptor) interceptor);
|
||||
}
|
||||
|
||||
Duration connectTimeout = clientConfiguration.getConnectTimeout();
|
||||
Duration timeout = clientConfiguration.getSocketTimeout();
|
||||
|
||||
Builder requestConfigBuilder = RequestConfig.custom();
|
||||
|
||||
if (!connectTimeout.isNegative()) {
|
||||
|
||||
requestConfigBuilder.setConnectTimeout(Math.toIntExact(connectTimeout.toMillis()));
|
||||
requestConfigBuilder.setConnectionRequestTimeout(Math.toIntExact(connectTimeout.toMillis()));
|
||||
}
|
||||
|
||||
if (!timeout.isNegative()) {
|
||||
requestConfigBuilder.setSocketTimeout(Math.toIntExact(timeout.toMillis()));
|
||||
}
|
||||
|
||||
clientBuilder.setDefaultRequestConfig(requestConfigBuilder.build());
|
||||
|
||||
clientConfiguration.getProxy().map(HttpHost::create).ifPresent(clientBuilder::setProxy);
|
||||
|
||||
return clientBuilder;
|
||||
});
|
||||
|
||||
RestHighLevelClient client = new RestHighLevelClient(builder);
|
||||
return () -> client;
|
||||
}
|
||||
|
||||
private static List<String> formattedHosts(List<InetSocketAddress> hosts, boolean useSsl) {
|
||||
return hosts.stream().map(it -> (useSsl ? "https" : "http") + "://" + it).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ElasticsearchRestClient extends Closeable {
|
||||
|
||||
/**
|
||||
* Apply the configuration to create a {@link RestHighLevelClient}.
|
||||
*
|
||||
* @return new instance of {@link RestHighLevelClient}.
|
||||
*/
|
||||
RestHighLevelClient rest();
|
||||
|
||||
/**
|
||||
* Apply the configuration to create a {@link RestClient}.
|
||||
*
|
||||
* @return new instance of {@link RestClient}.
|
||||
*/
|
||||
default RestClient lowLevelRest() {
|
||||
return rest().getLowLevelClient();
|
||||
}
|
||||
|
||||
@Override
|
||||
default void close() throws IOException {
|
||||
rest().close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logging interceptors for Elasticsearch client logging.
|
||||
*
|
||||
* @see ClientLogger
|
||||
* @since 3.2
|
||||
*/
|
||||
private static class HttpLoggingInterceptor implements HttpResponseInterceptor, HttpRequestInterceptor {
|
||||
|
||||
@Override
|
||||
public void process(HttpRequest request, HttpContext context) throws IOException {
|
||||
|
||||
String logId = (String) context.getAttribute(RestClients.LOG_ID_ATTRIBUTE);
|
||||
|
||||
if (logId == null) {
|
||||
|
||||
logId = ClientLogger.newLogId();
|
||||
context.setAttribute(RestClients.LOG_ID_ATTRIBUTE, logId);
|
||||
}
|
||||
|
||||
if (request instanceof HttpEntityEnclosingRequest && ((HttpEntityEnclosingRequest) request).getEntity() != null) {
|
||||
|
||||
HttpEntityEnclosingRequest entityRequest = (HttpEntityEnclosingRequest) request;
|
||||
HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
entity.writeTo(buffer);
|
||||
|
||||
if (!entity.isRepeatable()) {
|
||||
entityRequest.setEntity(new ByteArrayEntity(buffer.toByteArray()));
|
||||
}
|
||||
|
||||
ClientLogger.logRequest(logId, request.getRequestLine().getMethod(), request.getRequestLine().getUri(), "",
|
||||
() -> new String(buffer.toByteArray()));
|
||||
} else {
|
||||
ClientLogger.logRequest(logId, request.getRequestLine().getMethod(), request.getRequestLine().getUri(), "");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(HttpResponse response, HttpContext context) {
|
||||
|
||||
String logId = (String) context.getAttribute(RestClients.LOG_ID_ATTRIBUTE);
|
||||
|
||||
ClientLogger.logRawResponse(logId, HttpStatus.resolve(response.getStatusLine().getStatusCode()));
|
||||
}
|
||||
}
|
||||
}
|
||||
+156
-158
@@ -1,158 +1,156 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.util.Properties;
|
||||
|
||||
import org.elasticsearch.client.transport.TransportClient;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.transport.client.PreBuiltTransportClient;
|
||||
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;
|
||||
|
||||
/**
|
||||
* TransportClientFactoryBean
|
||||
*
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Jakub Vavrik
|
||||
* @author Piotr Betkier
|
||||
* @author Ilkang Na
|
||||
* @author Oliver Gierke
|
||||
* @author Peter-Josef Meisch
|
||||
* @deprecated as of 4.0
|
||||
*/
|
||||
@Deprecated
|
||||
public class TransportClientFactoryBean implements FactoryBean<TransportClient>, InitializingBean, DisposableBean {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(TransportClientFactoryBean.class);
|
||||
private ClusterNodes clusterNodes = ClusterNodes.of("127.0.0.1:9300");
|
||||
private String clusterName = "elasticsearch";
|
||||
private Boolean clientTransportSniff = true;
|
||||
private Boolean clientIgnoreClusterName = Boolean.FALSE;
|
||||
private String clientPingTimeout = "5s";
|
||||
private String clientNodesSamplerInterval = "5s";
|
||||
private @Nullable TransportClient client;
|
||||
private @Nullable Properties properties;
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
try {
|
||||
logger.info("Closing elasticSearch client");
|
||||
if (client != null) {
|
||||
client.close();
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
logger.error("Error closing ElasticSearch client: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportClient getObject() {
|
||||
|
||||
if (clientTransportSniff == null) {
|
||||
throw new FactoryBeanNotInitializedException();
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<TransportClient> getObjectType() {
|
||||
return TransportClient.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
buildClient();
|
||||
}
|
||||
|
||||
protected void buildClient() {
|
||||
|
||||
client = new PreBuiltTransportClient(settings());
|
||||
|
||||
clusterNodes.stream() //
|
||||
.peek(it -> logger.info("Adding transport node : " + it.toString())) //
|
||||
.forEach(client::addTransportAddress);
|
||||
|
||||
client.connectedNodes();
|
||||
}
|
||||
|
||||
private Settings settings() {
|
||||
if (properties != null) {
|
||||
Settings.Builder builder = Settings.builder();
|
||||
|
||||
properties.forEach((key, value) -> {
|
||||
builder.put(key.toString(), value.toString());
|
||||
});
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
return Settings.builder().put("cluster.name", clusterName).put("client.transport.sniff", clientTransportSniff)
|
||||
.put("client.transport.ignore_cluster_name", clientIgnoreClusterName)
|
||||
.put("client.transport.ping_timeout", clientPingTimeout)
|
||||
.put("client.transport.nodes_sampler_interval", clientNodesSamplerInterval).build();
|
||||
}
|
||||
|
||||
public void setClusterNodes(String clusterNodes) {
|
||||
this.clusterNodes = ClusterNodes.of(clusterNodes);
|
||||
}
|
||||
|
||||
public void setClusterName(String clusterName) {
|
||||
this.clusterName = clusterName;
|
||||
}
|
||||
|
||||
public void setClientTransportSniff(Boolean clientTransportSniff) {
|
||||
this.clientTransportSniff = clientTransportSniff;
|
||||
}
|
||||
|
||||
public String getClientNodesSamplerInterval() {
|
||||
return clientNodesSamplerInterval;
|
||||
}
|
||||
|
||||
public void setClientNodesSamplerInterval(String clientNodesSamplerInterval) {
|
||||
this.clientNodesSamplerInterval = clientNodesSamplerInterval;
|
||||
}
|
||||
|
||||
public String getClientPingTimeout() {
|
||||
return clientPingTimeout;
|
||||
}
|
||||
|
||||
public void setClientPingTimeout(String clientPingTimeout) {
|
||||
this.clientPingTimeout = clientPingTimeout;
|
||||
}
|
||||
|
||||
public Boolean getClientIgnoreClusterName() {
|
||||
return clientIgnoreClusterName;
|
||||
}
|
||||
|
||||
public void setClientIgnoreClusterName(Boolean clientIgnoreClusterName) {
|
||||
this.clientIgnoreClusterName = clientIgnoreClusterName;
|
||||
}
|
||||
|
||||
public void setProperties(Properties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright 2013 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
|
||||
*
|
||||
* 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 "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 static org.apache.commons.lang.StringUtils.*;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.elasticsearch.client.transport.TransportClient;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.transport.InetSocketTransportAddress;
|
||||
import org.elasticsearch.transport.client.PreBuiltTransportClient;
|
||||
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.InitializingBean;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* TransportClientFactoryBean
|
||||
*
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Jakub Vavrik
|
||||
* @author Piotr Betkier
|
||||
*/
|
||||
|
||||
public class TransportClientFactoryBean implements FactoryBean<TransportClient>, InitializingBean, DisposableBean {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(TransportClientFactoryBean.class);
|
||||
private String clusterNodes = "127.0.0.1:9300";
|
||||
private String clusterName = "elasticsearch";
|
||||
private Boolean clientTransportSniff = true;
|
||||
private Boolean clientIgnoreClusterName = Boolean.FALSE;
|
||||
private String clientPingTimeout = "5s";
|
||||
private String clientNodesSamplerInterval = "5s";
|
||||
private TransportClient client;
|
||||
private Properties properties;
|
||||
static final String COLON = ":";
|
||||
static final String COMMA = ",";
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
try {
|
||||
logger.info("Closing elasticSearch client");
|
||||
if (client != null) {
|
||||
client.close();
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
logger.error("Error closing ElasticSearch client: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportClient getObject() throws Exception {
|
||||
return client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<TransportClient> getObjectType() {
|
||||
return TransportClient.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
buildClient();
|
||||
}
|
||||
|
||||
protected void buildClient() throws Exception {
|
||||
|
||||
client = new PreBuiltTransportClient(settings());
|
||||
Assert.hasText(clusterNodes, "[Assertion failed] clusterNodes settings missing.");
|
||||
for (String clusterNode : split(clusterNodes, COMMA)) {
|
||||
String hostName = substringBeforeLast(clusterNode, COLON);
|
||||
String port = substringAfterLast(clusterNode, COLON);
|
||||
Assert.hasText(hostName, "[Assertion failed] missing host name in 'clusterNodes'");
|
||||
Assert.hasText(port, "[Assertion failed] missing port in 'clusterNodes'");
|
||||
logger.info("adding transport node : " + clusterNode);
|
||||
client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(hostName), Integer.valueOf(port)));
|
||||
}
|
||||
client.connectedNodes();
|
||||
}
|
||||
|
||||
private Settings settings() {
|
||||
if (properties != null) {
|
||||
return Settings.builder().put(properties).build();
|
||||
}
|
||||
return Settings.builder()
|
||||
.put("cluster.name", clusterName)
|
||||
.put("client.transport.sniff", clientTransportSniff)
|
||||
.put("client.transport.ignore_cluster_name", clientIgnoreClusterName)
|
||||
.put("client.transport.ping_timeout", clientPingTimeout)
|
||||
.put("client.transport.nodes_sampler_interval", clientNodesSamplerInterval)
|
||||
.build();
|
||||
}
|
||||
|
||||
public void setClusterNodes(String clusterNodes) {
|
||||
this.clusterNodes = clusterNodes;
|
||||
}
|
||||
|
||||
public void setClusterName(String clusterName) {
|
||||
this.clusterName = clusterName;
|
||||
}
|
||||
|
||||
public void setClientTransportSniff(Boolean clientTransportSniff) {
|
||||
this.clientTransportSniff = clientTransportSniff;
|
||||
}
|
||||
|
||||
public String getClientNodesSamplerInterval() {
|
||||
return clientNodesSamplerInterval;
|
||||
}
|
||||
|
||||
public void setClientNodesSamplerInterval(String clientNodesSamplerInterval) {
|
||||
this.clientNodesSamplerInterval = clientNodesSamplerInterval;
|
||||
}
|
||||
|
||||
public String getClientPingTimeout() {
|
||||
return clientPingTimeout;
|
||||
}
|
||||
|
||||
public void setClientPingTimeout(String clientPingTimeout) {
|
||||
this.clientPingTimeout = clientPingTimeout;
|
||||
}
|
||||
|
||||
public Boolean getClientIgnoreClusterName() {
|
||||
return clientIgnoreClusterName;
|
||||
}
|
||||
|
||||
public void setClientIgnoreClusterName(Boolean clientIgnoreClusterName) {
|
||||
this.clientIgnoreClusterName = clientIgnoreClusterName;
|
||||
}
|
||||
|
||||
public void setProperties(Properties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
@org.springframework.lang.NonNullApi
|
||||
@org.springframework.lang.NonNullFields
|
||||
package org.springframework.data.elasticsearch.client;
|
||||
-894
@@ -1,894 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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.reactive;
|
||||
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.handler.ssl.ClientAuth;
|
||||
import io.netty.handler.ssl.JdkSslContext;
|
||||
import io.netty.handler.timeout.ReadTimeoutHandler;
|
||||
import io.netty.handler.timeout.WriteTimeoutHandler;
|
||||
import reactor.core.publisher.EmitterProcessor;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.FluxSink;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.netty.http.client.HttpClient;
|
||||
import reactor.netty.tcp.ProxyProvider;
|
||||
import reactor.netty.tcp.TcpClient;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.ConnectException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.elasticsearch.ElasticsearchStatusException;
|
||||
import org.elasticsearch.action.ActionRequest;
|
||||
import org.elasticsearch.action.admin.indices.close.CloseIndexRequest;
|
||||
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
|
||||
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
|
||||
import org.elasticsearch.action.admin.indices.flush.FlushRequest;
|
||||
import org.elasticsearch.action.admin.indices.flush.FlushResponse;
|
||||
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
|
||||
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
|
||||
import org.elasticsearch.action.admin.indices.open.OpenIndexRequest;
|
||||
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
|
||||
import org.elasticsearch.action.admin.indices.refresh.RefreshResponse;
|
||||
import org.elasticsearch.action.bulk.BulkRequest;
|
||||
import org.elasticsearch.action.bulk.BulkResponse;
|
||||
import org.elasticsearch.action.delete.DeleteRequest;
|
||||
import org.elasticsearch.action.delete.DeleteResponse;
|
||||
import org.elasticsearch.action.get.GetRequest;
|
||||
import org.elasticsearch.action.get.GetResponse;
|
||||
import org.elasticsearch.action.get.MultiGetRequest;
|
||||
import org.elasticsearch.action.get.MultiGetResponse;
|
||||
import org.elasticsearch.action.index.IndexRequest;
|
||||
import org.elasticsearch.action.index.IndexResponse;
|
||||
import org.elasticsearch.action.main.MainRequest;
|
||||
import org.elasticsearch.action.main.MainResponse;
|
||||
import org.elasticsearch.action.search.ClearScrollRequest;
|
||||
import org.elasticsearch.action.search.ClearScrollResponse;
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.search.SearchScrollRequest;
|
||||
import org.elasticsearch.action.support.master.AcknowledgedResponse;
|
||||
import org.elasticsearch.action.update.UpdateRequest;
|
||||
import org.elasticsearch.action.update.UpdateResponse;
|
||||
import org.elasticsearch.client.Request;
|
||||
import org.elasticsearch.client.core.CountRequest;
|
||||
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.index.get.GetResult;
|
||||
import org.elasticsearch.index.reindex.BulkByScrollResponse;
|
||||
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
|
||||
import org.elasticsearch.rest.BytesRestResponse;
|
||||
import org.elasticsearch.rest.RestStatus;
|
||||
import org.elasticsearch.search.Scroll;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
import org.elasticsearch.search.SearchHits;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.data.elasticsearch.ElasticsearchException;
|
||||
import org.springframework.data.elasticsearch.client.ClientConfiguration;
|
||||
import org.springframework.data.elasticsearch.client.ClientLogger;
|
||||
import org.springframework.data.elasticsearch.client.ElasticsearchHost;
|
||||
import org.springframework.data.elasticsearch.client.NoReachableHostException;
|
||||
import org.springframework.data.elasticsearch.client.reactive.HostProvider.Verification;
|
||||
import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient.Indices;
|
||||
import org.springframework.data.elasticsearch.client.util.RequestConverters;
|
||||
import org.springframework.data.util.Lazy;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.client.HttpServerErrorException;
|
||||
import org.springframework.web.reactive.function.BodyExtractors;
|
||||
import org.springframework.web.reactive.function.client.ClientRequest;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import org.springframework.web.reactive.function.client.WebClient.RequestBodySpec;
|
||||
|
||||
/**
|
||||
* A {@link WebClient} based {@link ReactiveElasticsearchClient} that connects to an Elasticsearch cluster using HTTP.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Huw Ayling-Miller
|
||||
* @author Henrique Amaral
|
||||
* @since 3.2
|
||||
* @see ClientConfiguration
|
||||
* @see ReactiveRestClients
|
||||
*/
|
||||
public class DefaultReactiveElasticsearchClient implements ReactiveElasticsearchClient, Indices {
|
||||
|
||||
private final HostProvider hostProvider;
|
||||
|
||||
/**
|
||||
* Create a new {@link DefaultReactiveElasticsearchClient} using the given {@link HostProvider} to obtain server
|
||||
* connections.
|
||||
*
|
||||
* @param hostProvider must not be {@literal null}.
|
||||
*/
|
||||
public DefaultReactiveElasticsearchClient(HostProvider hostProvider) {
|
||||
|
||||
Assert.notNull(hostProvider, "HostProvider must not be null");
|
||||
|
||||
this.hostProvider = hostProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link DefaultReactiveElasticsearchClient} aware of the given nodes in the cluster. <br />
|
||||
* <strong>NOTE</strong> If the cluster requires authentication be sure to provide the according {@link HttpHeaders}
|
||||
* correctly.
|
||||
*
|
||||
* @param headers Use {@link HttpHeaders} to provide eg. authentication data. Must not be {@literal null}.
|
||||
* @param hosts must not be {@literal null} nor empty!
|
||||
* @return new instance of {@link DefaultReactiveElasticsearchClient}.
|
||||
*/
|
||||
public static ReactiveElasticsearchClient create(HttpHeaders headers, String... hosts) {
|
||||
|
||||
Assert.notNull(headers, "HttpHeaders must not be null");
|
||||
Assert.notEmpty(hosts, "Elasticsearch Cluster needs to consist of at least one host");
|
||||
|
||||
ClientConfiguration clientConfiguration = ClientConfiguration.builder().connectedTo(hosts)
|
||||
.withDefaultHeaders(headers).build();
|
||||
return create(clientConfiguration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link DefaultReactiveElasticsearchClient} given {@link ClientConfiguration}. <br />
|
||||
* <strong>NOTE</strong> If the cluster requires authentication be sure to provide the according {@link HttpHeaders}
|
||||
* correctly.
|
||||
*
|
||||
* @param clientConfiguration Client configuration. Must not be {@literal null}.
|
||||
* @return new instance of {@link DefaultReactiveElasticsearchClient}.
|
||||
*/
|
||||
public static ReactiveElasticsearchClient create(ClientConfiguration clientConfiguration) {
|
||||
|
||||
Assert.notNull(clientConfiguration, "ClientConfiguration must not be null");
|
||||
|
||||
WebClientProvider provider = getWebClientProvider(clientConfiguration);
|
||||
|
||||
HostProvider hostProvider = HostProvider.provider(provider,
|
||||
clientConfiguration.getEndpoints().toArray(new InetSocketAddress[0]));
|
||||
return new DefaultReactiveElasticsearchClient(hostProvider);
|
||||
}
|
||||
|
||||
private static WebClientProvider getWebClientProvider(ClientConfiguration clientConfiguration) {
|
||||
|
||||
Duration connectTimeout = clientConfiguration.getConnectTimeout();
|
||||
Duration soTimeout = clientConfiguration.getSocketTimeout();
|
||||
|
||||
TcpClient tcpClient = TcpClient.create();
|
||||
|
||||
if (!connectTimeout.isNegative()) {
|
||||
tcpClient = tcpClient.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, Math.toIntExact(connectTimeout.toMillis()));
|
||||
}
|
||||
|
||||
if (!soTimeout.isNegative()) {
|
||||
tcpClient = tcpClient.doOnConnected(connection -> connection //
|
||||
.addHandlerLast(new ReadTimeoutHandler(soTimeout.toMillis(), TimeUnit.MILLISECONDS))
|
||||
.addHandlerLast(new WriteTimeoutHandler(soTimeout.toMillis(), TimeUnit.MILLISECONDS)));
|
||||
}
|
||||
|
||||
if (clientConfiguration.getProxy().isPresent()) {
|
||||
String proxy = clientConfiguration.getProxy().get();
|
||||
String[] hostPort = proxy.split(":");
|
||||
|
||||
if (hostPort.length != 2) {
|
||||
throw new IllegalArgumentException("invalid proxy configuration " + proxy + ", should be \"host:port\"");
|
||||
}
|
||||
tcpClient = tcpClient.proxy(proxyOptions -> proxyOptions.type(ProxyProvider.Proxy.HTTP).host(hostPort[0])
|
||||
.port(Integer.parseInt(hostPort[1])));
|
||||
}
|
||||
|
||||
String scheme = "http";
|
||||
HttpClient httpClient = HttpClient.from(tcpClient);
|
||||
|
||||
if (clientConfiguration.useSsl()) {
|
||||
|
||||
httpClient = httpClient.secure(sslConfig -> {
|
||||
|
||||
Optional<SSLContext> sslContext = clientConfiguration.getSslContext();
|
||||
sslContext.ifPresent(it -> sslConfig.sslContext(new JdkSslContext(it, true, ClientAuth.NONE)));
|
||||
});
|
||||
|
||||
scheme = "https";
|
||||
}
|
||||
|
||||
ReactorClientHttpConnector connector = new ReactorClientHttpConnector(httpClient);
|
||||
WebClientProvider provider = WebClientProvider.create(scheme, connector);
|
||||
|
||||
if (clientConfiguration.getPathPrefix() != null) {
|
||||
provider = provider.withPathPrefix(clientConfiguration.getPathPrefix());
|
||||
}
|
||||
|
||||
provider = provider.withDefaultHeaders(clientConfiguration.getDefaultHeaders()) //
|
||||
.withWebClientConfigurer(clientConfiguration.getWebClientConfigurer());
|
||||
return provider;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient#ping(org.springframework.http.HttpHeaders)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> ping(HttpHeaders headers) {
|
||||
|
||||
return sendRequest(new MainRequest(), RequestCreator.ping(), RawActionResponse.class, headers) //
|
||||
.map(response -> response.statusCode().is2xxSuccessful()) //
|
||||
.onErrorResume(NoReachableHostException.class, error -> Mono.just(false)).next();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient#info(org.springframework.http.HttpHeaders)
|
||||
*/
|
||||
@Override
|
||||
public Mono<MainResponse> info(HttpHeaders headers) {
|
||||
|
||||
return sendRequest(new MainRequest(), RequestCreator.info(), MainResponse.class, headers) //
|
||||
.next();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient#get(org.springframework.http.HttpHeaders, org.elasticsearch.action.get.GetRequest)
|
||||
*/
|
||||
@Override
|
||||
public Mono<GetResult> get(HttpHeaders headers, GetRequest getRequest) {
|
||||
|
||||
return sendRequest(getRequest, RequestCreator.get(), GetResponse.class, headers) //
|
||||
.filter(GetResponse::isExists) //
|
||||
.map(DefaultReactiveElasticsearchClient::getResponseToGetResult) //
|
||||
.next();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient#multiGet(org.springframework.http.HttpHeaders, org.elasticsearch.action.get.MultiGetRequest)
|
||||
*/
|
||||
@Override
|
||||
public Flux<GetResult> multiGet(HttpHeaders headers, MultiGetRequest multiGetRequest) {
|
||||
|
||||
return sendRequest(multiGetRequest, RequestCreator.multiGet(), MultiGetResponse.class, headers)
|
||||
.map(MultiGetResponse::getResponses) //
|
||||
.flatMap(Flux::fromArray) //
|
||||
.filter(it -> !it.isFailed() && it.getResponse().isExists()) //
|
||||
.map(it -> DefaultReactiveElasticsearchClient.getResponseToGetResult(it.getResponse()));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient#exists(org.springframework.http.HttpHeaders, org.elasticsearch.action.get.GetRequest)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> exists(HttpHeaders headers, GetRequest getRequest) {
|
||||
|
||||
return sendRequest(getRequest, RequestCreator.exists(), RawActionResponse.class, headers) //
|
||||
.map(response -> response.statusCode().is2xxSuccessful()) //
|
||||
.next();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient#ping(org.springframework.http.HttpHeaders, org.elasticsearch.action.index.IndexRequest)
|
||||
*/
|
||||
@Override
|
||||
public Mono<IndexResponse> index(HttpHeaders headers, IndexRequest indexRequest) {
|
||||
return sendRequest(indexRequest, RequestCreator.index(), IndexResponse.class, headers).publishNext();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient#indices()
|
||||
*/
|
||||
@Override
|
||||
public Indices indices() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient#ping(org.springframework.http.HttpHeaders, org.elasticsearch.action.update.UpdateRequest)
|
||||
*/
|
||||
@Override
|
||||
public Mono<UpdateResponse> update(HttpHeaders headers, UpdateRequest updateRequest) {
|
||||
return sendRequest(updateRequest, RequestCreator.update(), UpdateResponse.class, headers).publishNext();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient#ping(org.springframework.http.HttpHeaders, org.elasticsearch.action.delete.DeleteRequest)
|
||||
*/
|
||||
@Override
|
||||
public Mono<DeleteResponse> delete(HttpHeaders headers, DeleteRequest deleteRequest) {
|
||||
|
||||
return sendRequest(deleteRequest, RequestCreator.delete(), DeleteResponse.class, headers) //
|
||||
.publishNext();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient#count(org.springframework.http.HttpHeaders, org.elasticsearch.action.search.SearchRequest)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Long> count(HttpHeaders headers, SearchRequest searchRequest) {
|
||||
return sendRequest(searchRequest, RequestCreator.search(), SearchResponse.class, headers) //
|
||||
.map(SearchResponse::getHits) //
|
||||
.map(searchHits -> searchHits.getTotalHits().value) //
|
||||
.next();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient#ping(org.springframework.http.HttpHeaders, org.elasticsearch.action.search.SearchRequest)
|
||||
*/
|
||||
@Override
|
||||
public Flux<SearchHit> search(HttpHeaders headers, SearchRequest searchRequest) {
|
||||
|
||||
return sendRequest(searchRequest, RequestCreator.search(), SearchResponse.class, headers) //
|
||||
.map(SearchResponse::getHits) //
|
||||
.flatMap(Flux::fromIterable);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient#scroll(org.springframework.http.HttpHeaders, org.elasticsearch.action.search.SearchRequest)
|
||||
*/
|
||||
@Override
|
||||
public Flux<SearchHit> scroll(HttpHeaders headers, SearchRequest searchRequest) {
|
||||
|
||||
TimeValue scrollTimeout = searchRequest.scroll() != null ? searchRequest.scroll().keepAlive()
|
||||
: TimeValue.timeValueMinutes(1);
|
||||
|
||||
if (searchRequest.scroll() == null) {
|
||||
searchRequest.scroll(scrollTimeout);
|
||||
}
|
||||
|
||||
EmitterProcessor<ActionRequest> outbound = EmitterProcessor.create(false);
|
||||
FluxSink<ActionRequest> request = outbound.sink();
|
||||
|
||||
EmitterProcessor<SearchResponse> inbound = EmitterProcessor.create(false);
|
||||
|
||||
Flux<SearchResponse> exchange = outbound.startWith(searchRequest).flatMap(it -> {
|
||||
|
||||
if (it instanceof SearchRequest) {
|
||||
return sendRequest((SearchRequest) it, RequestCreator.search(), SearchResponse.class, headers);
|
||||
} else if (it instanceof SearchScrollRequest) {
|
||||
return sendRequest((SearchScrollRequest) it, RequestCreator.scroll(), SearchResponse.class, headers);
|
||||
} else if (it instanceof ClearScrollRequest) {
|
||||
return sendRequest((ClearScrollRequest) it, RequestCreator.clearScroll(), ClearScrollResponse.class, headers)
|
||||
.flatMap(discard -> Flux.empty());
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Cannot handle '%s'. Please make sure to use a 'SearchRequest' or 'SearchScrollRequest'.", it));
|
||||
});
|
||||
|
||||
return Flux.usingWhen(Mono.fromSupplier(ScrollState::new),
|
||||
|
||||
scrollState -> {
|
||||
|
||||
Flux<SearchHit> searchHits = inbound.<SearchResponse> handle((searchResponse, sink) -> {
|
||||
|
||||
scrollState.updateScrollId(searchResponse.getScrollId());
|
||||
if (isEmpty(searchResponse.getHits())) {
|
||||
|
||||
inbound.onComplete();
|
||||
outbound.onComplete();
|
||||
|
||||
} else {
|
||||
|
||||
sink.next(searchResponse);
|
||||
|
||||
SearchScrollRequest searchScrollRequest = new SearchScrollRequest(scrollState.getScrollId())
|
||||
.scroll(scrollTimeout);
|
||||
request.next(searchScrollRequest);
|
||||
}
|
||||
|
||||
}).map(SearchResponse::getHits) //
|
||||
.flatMap(Flux::fromIterable);
|
||||
|
||||
return searchHits.doOnSubscribe(ignore -> exchange.subscribe(inbound));
|
||||
|
||||
}, state -> cleanupScroll(headers, state), //
|
||||
state -> cleanupScroll(headers, state), //
|
||||
state -> cleanupScroll(headers, state)); //
|
||||
}
|
||||
|
||||
private static boolean isEmpty(@Nullable SearchHits hits) {
|
||||
return hits != null && hits.getHits() != null && hits.getHits().length == 0;
|
||||
}
|
||||
|
||||
private Publisher<?> cleanupScroll(HttpHeaders headers, ScrollState state) {
|
||||
|
||||
if (state.getScrollIds().isEmpty()) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
|
||||
clearScrollRequest.scrollIds(state.getScrollIds());
|
||||
|
||||
// just send the request, resources get cleaned up anyways after scrollTimeout has been reached.
|
||||
return sendRequest(clearScrollRequest, RequestCreator.clearScroll(), ClearScrollResponse.class, headers);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient#ping(org.springframework.http.HttpHeaders, org.elasticsearch.index.reindex.DeleteByQueryRequest)
|
||||
*/
|
||||
@Override
|
||||
public Mono<BulkByScrollResponse> deleteBy(HttpHeaders headers, DeleteByQueryRequest deleteRequest) {
|
||||
|
||||
return sendRequest(deleteRequest, RequestCreator.deleteByQuery(), BulkByScrollResponse.class, headers) //
|
||||
.publishNext();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient#bulk(org.springframework.http.HttpHeaders, org.elasticsearch.action.bulk.BulkRequest)
|
||||
*/
|
||||
@Override
|
||||
public Mono<BulkResponse> bulk(HttpHeaders headers, BulkRequest bulkRequest) {
|
||||
return sendRequest(bulkRequest, RequestCreator.bulk(), BulkResponse.class, headers) //
|
||||
.publishNext();
|
||||
}
|
||||
|
||||
// --> INDICES
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient.Indices#existsIndex(org.springframework.http.HttpHeaders, org.elasticsearch.action.admin.indices.get.GetIndexRequest)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Boolean> existsIndex(HttpHeaders headers, GetIndexRequest request) {
|
||||
|
||||
return sendRequest(request, RequestCreator.indexExists(), RawActionResponse.class, headers) //
|
||||
.map(response -> response.statusCode().is2xxSuccessful()) //
|
||||
.next();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient.Indices#deleteIndex(org.springframework.http.HttpHeaders, org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Void> deleteIndex(HttpHeaders headers, DeleteIndexRequest request) {
|
||||
|
||||
return sendRequest(request, RequestCreator.indexDelete(), AcknowledgedResponse.class, headers) //
|
||||
.then();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient.Indices#createIndex(org.springframework.http.HttpHeaders, org.elasticsearch.action.admin.indices.create.CreateIndexRequest)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Void> createIndex(HttpHeaders headers, CreateIndexRequest createIndexRequest) {
|
||||
|
||||
return sendRequest(createIndexRequest, RequestCreator.indexCreate(), AcknowledgedResponse.class, headers) //
|
||||
.then();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient.Indices#openIndex(org.springframework.http.HttpHeaders, org.elasticsearch.action.admin.indices.open.OpenIndexRequest)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Void> openIndex(HttpHeaders headers, OpenIndexRequest request) {
|
||||
|
||||
return sendRequest(request, RequestCreator.indexOpen(), AcknowledgedResponse.class, headers) //
|
||||
.then();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient.Indices#closeIndex(org.springframework.http.HttpHeaders, org.elasticsearch.action.admin.indices.close.CloseIndexRequest)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Void> closeIndex(HttpHeaders headers, CloseIndexRequest closeIndexRequest) {
|
||||
|
||||
return sendRequest(closeIndexRequest, RequestCreator.indexClose(), AcknowledgedResponse.class, headers) //
|
||||
.then();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient.Indices#refreshIndex(org.springframework.http.HttpHeaders, org.elasticsearch.action.admin.indices.refresh.RefreshRequest)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Void> refreshIndex(HttpHeaders headers, RefreshRequest refreshRequest) {
|
||||
|
||||
return sendRequest(refreshRequest, RequestCreator.indexRefresh(), RefreshResponse.class, headers) //
|
||||
.then();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient.Indices#updateMapping(org.springframework.http.HttpHeaders, org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Void> updateMapping(HttpHeaders headers, PutMappingRequest putMappingRequest) {
|
||||
|
||||
return sendRequest(putMappingRequest, RequestCreator.putMapping(), AcknowledgedResponse.class, headers) //
|
||||
.then();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient.Indices#flushIndex(org.springframework.http.HttpHeaders, org.elasticsearch.action.admin.indices.flush.FlushRequest)
|
||||
*/
|
||||
@Override
|
||||
public Mono<Void> flushIndex(HttpHeaders headers, FlushRequest flushRequest) {
|
||||
|
||||
return sendRequest(flushRequest, RequestCreator.flushIndex(), FlushResponse.class, headers) //
|
||||
.then();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient#ping(org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient.ReactiveElasticsearchClientCallback)
|
||||
*/
|
||||
@Override
|
||||
public Mono<ClientResponse> execute(ReactiveElasticsearchClientCallback callback) {
|
||||
|
||||
return this.hostProvider.getActive(Verification.LAZY) //
|
||||
.flatMap(callback::doWithClient) //
|
||||
.onErrorResume(throwable -> {
|
||||
|
||||
if (throwable instanceof ConnectException) {
|
||||
|
||||
return hostProvider.getActive(Verification.ACTIVE) //
|
||||
.flatMap(callback::doWithClient);
|
||||
}
|
||||
|
||||
return Mono.error(throwable);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Status> status() {
|
||||
|
||||
return hostProvider.clusterInfo() //
|
||||
.map(it -> new ClientStatus(it.getNodes()));
|
||||
}
|
||||
|
||||
// --> Private Response helpers
|
||||
|
||||
private static GetResult getResponseToGetResult(GetResponse response) {
|
||||
|
||||
return new GetResult(response.getIndex(), response.getType(), response.getId(), response.getSeqNo(),
|
||||
response.getPrimaryTerm(), response.getVersion(), response.isExists(), response.getSourceAsBytesRef(),
|
||||
response.getFields(), null);
|
||||
}
|
||||
|
||||
// -->
|
||||
|
||||
private <Req extends ActionRequest, Resp> Flux<Resp> sendRequest(Req request, Function<Req, Request> converter,
|
||||
Class<Resp> responseType, HttpHeaders headers) {
|
||||
return sendRequest(converter.apply(request), responseType, headers);
|
||||
}
|
||||
|
||||
private <Resp> Flux<Resp> sendRequest(Request request, Class<Resp> responseType, HttpHeaders headers) {
|
||||
|
||||
String logId = ClientLogger.newLogId();
|
||||
|
||||
return execute(webClient -> sendRequest(webClient, logId, request, headers))
|
||||
.flatMapMany(response -> readResponseBody(logId, request, response, responseType));
|
||||
}
|
||||
|
||||
private Mono<ClientResponse> sendRequest(WebClient webClient, String logId, Request request, HttpHeaders headers) {
|
||||
|
||||
RequestBodySpec requestBodySpec = webClient.method(HttpMethod.valueOf(request.getMethod().toUpperCase())) //
|
||||
.uri(builder -> {
|
||||
|
||||
builder = builder.path(request.getEndpoint());
|
||||
|
||||
if (!ObjectUtils.isEmpty(request.getParameters())) {
|
||||
for (Entry<String, String> entry : request.getParameters().entrySet()) {
|
||||
builder = builder.queryParam(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}) //
|
||||
.attribute(ClientRequest.LOG_ID_ATTRIBUTE, logId) //
|
||||
.headers(theHeaders -> {
|
||||
|
||||
// add all the headers explicitly set
|
||||
theHeaders.addAll(headers);
|
||||
|
||||
// and now those that might be set on the request.
|
||||
if (request.getOptions() != null) {
|
||||
|
||||
if (!ObjectUtils.isEmpty(request.getOptions().getHeaders())) {
|
||||
request.getOptions().getHeaders().forEach(it -> theHeaders.add(it.getName(), it.getValue()));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (request.getEntity() != null) {
|
||||
|
||||
Lazy<String> body = bodyExtractor(request);
|
||||
|
||||
ClientLogger.logRequest(logId, request.getMethod().toUpperCase(), request.getEndpoint(), request.getParameters(),
|
||||
body::get);
|
||||
|
||||
requestBodySpec.contentType(MediaType.valueOf(request.getEntity().getContentType().getValue()));
|
||||
requestBodySpec.body(Mono.fromSupplier(body::get), String.class);
|
||||
} else {
|
||||
ClientLogger.logRequest(logId, request.getMethod().toUpperCase(), request.getEndpoint(), request.getParameters());
|
||||
}
|
||||
|
||||
return requestBodySpec //
|
||||
.exchange() //
|
||||
.onErrorReturn(ConnectException.class, ClientResponse.create(HttpStatus.SERVICE_UNAVAILABLE).build());
|
||||
}
|
||||
|
||||
private Lazy<String> bodyExtractor(Request request) {
|
||||
|
||||
return Lazy.of(() -> {
|
||||
|
||||
try {
|
||||
return EntityUtils.toString(request.getEntity());
|
||||
} catch (IOException e) {
|
||||
throw new RequestBodyEncodingException("Error encoding request", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private <T> Publisher<? extends T> readResponseBody(String logId, Request request, ClientResponse response,
|
||||
Class<T> responseType) {
|
||||
|
||||
if (RawActionResponse.class.equals(responseType)) {
|
||||
|
||||
ClientLogger.logRawResponse(logId, response.statusCode());
|
||||
return Mono.just(responseType.cast(RawActionResponse.create(response)));
|
||||
}
|
||||
|
||||
if (response.statusCode().is5xxServerError()) {
|
||||
|
||||
ClientLogger.logRawResponse(logId, response.statusCode());
|
||||
return handleServerError(request, response);
|
||||
}
|
||||
|
||||
return response.body(BodyExtractors.toMono(byte[].class)) //
|
||||
.map(it -> new String(it, StandardCharsets.UTF_8)) //
|
||||
.doOnNext(it -> ClientLogger.logResponse(logId, response.statusCode(), it)) //
|
||||
.flatMap(content -> doDecode(response, responseType, content));
|
||||
}
|
||||
|
||||
private static <T> Mono<T> doDecode(ClientResponse response, Class<T> responseType, String content) {
|
||||
|
||||
String mediaType = response.headers().contentType().map(MediaType::toString).orElse(XContentType.JSON.mediaType());
|
||||
|
||||
try {
|
||||
|
||||
Method fromXContent = ReflectionUtils.findMethod(responseType, "fromXContent", XContentParser.class);
|
||||
|
||||
return Mono.justOrEmpty(responseType
|
||||
.cast(ReflectionUtils.invokeMethod(fromXContent, responseType, createParser(mediaType, content))));
|
||||
|
||||
} catch (Throwable errorParseFailure) { // cause elasticsearch also uses AssertionError
|
||||
|
||||
try {
|
||||
return Mono.error(BytesRestResponse.errorFromXContent(createParser(mediaType, content)));
|
||||
} catch (Exception e) {
|
||||
|
||||
return Mono
|
||||
.error(new ElasticsearchStatusException(content, RestStatus.fromCode(response.statusCode().value())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static XContentParser createParser(String mediaType, String content) throws IOException {
|
||||
|
||||
return XContentType.fromMediaTypeOrFormat(mediaType) //
|
||||
.xContent() //
|
||||
.createParser(NamedXContentRegistry.EMPTY, DeprecationHandler.THROW_UNSUPPORTED_OPERATION, content);
|
||||
}
|
||||
|
||||
private static <T> Publisher<? extends T> handleServerError(Request request, ClientResponse response) {
|
||||
|
||||
return Mono.error(
|
||||
new HttpServerErrorException(response.statusCode(), String.format("%s request to %s returned error code %s.",
|
||||
request.getMethod(), request.getEndpoint(), response.statusCode().value())));
|
||||
}
|
||||
|
||||
static class RequestCreator {
|
||||
|
||||
static Function<SearchRequest, Request> search() {
|
||||
return RequestConverters::search;
|
||||
}
|
||||
|
||||
static Function<SearchScrollRequest, Request> scroll() {
|
||||
return RequestConverters::searchScroll;
|
||||
}
|
||||
|
||||
static Function<ClearScrollRequest, Request> clearScroll() {
|
||||
return RequestConverters::clearScroll;
|
||||
}
|
||||
|
||||
static Function<IndexRequest, Request> index() {
|
||||
return RequestConverters::index;
|
||||
}
|
||||
|
||||
static Function<GetRequest, Request> get() {
|
||||
return RequestConverters::get;
|
||||
}
|
||||
|
||||
static Function<MainRequest, Request> ping() {
|
||||
return (request) -> RequestConverters.ping();
|
||||
}
|
||||
|
||||
static Function<MainRequest, Request> info() {
|
||||
return (request) -> RequestConverters.info();
|
||||
}
|
||||
|
||||
static Function<MultiGetRequest, Request> multiGet() {
|
||||
return RequestConverters::multiGet;
|
||||
}
|
||||
|
||||
static Function<GetRequest, Request> exists() {
|
||||
return RequestConverters::exists;
|
||||
}
|
||||
|
||||
static Function<UpdateRequest, Request> update() {
|
||||
return RequestConverters::update;
|
||||
}
|
||||
|
||||
static Function<DeleteRequest, Request> delete() {
|
||||
return RequestConverters::delete;
|
||||
}
|
||||
|
||||
static Function<DeleteByQueryRequest, Request> deleteByQuery() {
|
||||
|
||||
return request -> RequestConverters.deleteByQuery(request);
|
||||
}
|
||||
|
||||
static Function<BulkRequest, Request> bulk() {
|
||||
|
||||
return request -> {
|
||||
|
||||
try {
|
||||
return RequestConverters.bulk(request);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Could not parse request", e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// --> INDICES
|
||||
|
||||
static Function<GetIndexRequest, Request> indexExists() {
|
||||
return RequestConverters::indexExists;
|
||||
}
|
||||
|
||||
static Function<DeleteIndexRequest, Request> indexDelete() {
|
||||
return RequestConverters::indexDelete;
|
||||
}
|
||||
|
||||
static Function<CreateIndexRequest, Request> indexCreate() {
|
||||
return RequestConverters::indexCreate;
|
||||
}
|
||||
|
||||
static Function<OpenIndexRequest, Request> indexOpen() {
|
||||
return RequestConverters::indexOpen;
|
||||
}
|
||||
|
||||
static Function<CloseIndexRequest, Request> indexClose() {
|
||||
return RequestConverters::indexClose;
|
||||
}
|
||||
|
||||
static Function<RefreshRequest, Request> indexRefresh() {
|
||||
return RequestConverters::indexRefresh;
|
||||
}
|
||||
|
||||
static Function<PutMappingRequest, Request> putMapping() {
|
||||
return RequestConverters::putMapping;
|
||||
}
|
||||
|
||||
static Function<FlushRequest, Request> flushIndex() {
|
||||
return RequestConverters::flushIndex;
|
||||
}
|
||||
|
||||
static Function<CountRequest, Request> count() {
|
||||
return RequestConverters::count;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reactive client {@link ReactiveElasticsearchClient.Status} implementation.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class ClientStatus implements Status {
|
||||
|
||||
private final Collection<ElasticsearchHost> connectedHosts;
|
||||
|
||||
ClientStatus(Collection<ElasticsearchHost> connectedHosts) {
|
||||
this.connectedHosts = connectedHosts;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient.Status#hosts()
|
||||
*/
|
||||
@Override
|
||||
public Collection<ElasticsearchHost> hosts() {
|
||||
return connectedHosts;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mutable state object holding scrollId to be used for {@link SearchScrollRequest#scroll(Scroll)}
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 3.2
|
||||
*/
|
||||
private static class ScrollState {
|
||||
|
||||
private final Object lock = new Object();
|
||||
|
||||
private final List<String> pastIds = new ArrayList<>(1);
|
||||
@Nullable private String scrollId;
|
||||
|
||||
@Nullable
|
||||
String getScrollId() {
|
||||
return scrollId;
|
||||
}
|
||||
|
||||
List<String> getScrollIds() {
|
||||
|
||||
synchronized (lock) {
|
||||
return Collections.unmodifiableList(new ArrayList<>(pastIds));
|
||||
}
|
||||
}
|
||||
|
||||
void updateScrollId(String scrollId) {
|
||||
|
||||
if (StringUtils.hasText(scrollId)) {
|
||||
|
||||
synchronized (lock) {
|
||||
|
||||
this.scrollId = scrollId;
|
||||
pastIds.add(scrollId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-162
@@ -1,162 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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.reactive;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.client.reactive.ClientHttpConnector;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import org.springframework.web.reactive.function.client.WebClient.Builder;
|
||||
|
||||
/**
|
||||
* Default {@link WebClientProvider} that uses cached {@link WebClient} instances per {@code hostAndPort}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @author Huw Ayling-Miller
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 3.2
|
||||
*/
|
||||
class DefaultWebClientProvider implements WebClientProvider {
|
||||
|
||||
private final Map<InetSocketAddress, WebClient> cachedClients;
|
||||
|
||||
private final String scheme;
|
||||
private final @Nullable ClientHttpConnector connector;
|
||||
private final Consumer<Throwable> errorListener;
|
||||
private final HttpHeaders headers;
|
||||
private final String pathPrefix;
|
||||
private final Function<WebClient, WebClient> webClientConfigurer;
|
||||
|
||||
/**
|
||||
* Create new {@link DefaultWebClientProvider} with empty {@link HttpHeaders} and no-op {@literal error listener}.
|
||||
*
|
||||
* @param scheme must not be {@literal null}.
|
||||
* @param connector can be {@literal null}.
|
||||
*/
|
||||
DefaultWebClientProvider(String scheme, @Nullable ClientHttpConnector connector) {
|
||||
this(scheme, connector, e -> {}, HttpHeaders.EMPTY, null, Function.identity());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new {@link DefaultWebClientProvider} with empty {@link HttpHeaders} and no-op {@literal error listener}.
|
||||
*
|
||||
* @param pathPrefix can be {@literal null}
|
||||
* @param scheme must not be {@literal null}.
|
||||
* @param connector can be {@literal null}.
|
||||
* @param errorListener must not be {@literal null}.
|
||||
* @param headers must not be {@literal null}.
|
||||
* @param webClientConfigurer must not be {@literal null}.
|
||||
*/
|
||||
private DefaultWebClientProvider(String scheme, @Nullable ClientHttpConnector connector,
|
||||
Consumer<Throwable> errorListener, HttpHeaders headers, @Nullable String pathPrefix,
|
||||
Function<WebClient, WebClient> webClientConfigurer) {
|
||||
|
||||
Assert.notNull(scheme, "Scheme must not be null! A common scheme would be 'http'.");
|
||||
Assert.notNull(errorListener, "errorListener must not be null! You may want use a no-op one 'e -> {}' instead.");
|
||||
Assert.notNull(headers, "headers must not be null! Think about using 'HttpHeaders.EMPTY' as an alternative.");
|
||||
Assert.notNull(webClientConfigurer,
|
||||
"webClientConfigurer must not be null! You may want use a no-op one 'Function.identity()' instead.");
|
||||
|
||||
this.cachedClients = new ConcurrentHashMap<>();
|
||||
this.scheme = scheme;
|
||||
this.connector = connector;
|
||||
this.errorListener = errorListener;
|
||||
this.headers = headers;
|
||||
this.pathPrefix = pathPrefix;
|
||||
this.webClientConfigurer = webClientConfigurer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebClient get(InetSocketAddress endpoint) {
|
||||
|
||||
Assert.notNull(endpoint, "Endpoint must not be empty!");
|
||||
|
||||
return this.cachedClients.computeIfAbsent(endpoint, this::createWebClientForSocketAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getDefaultHeaders() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Consumer<Throwable> getErrorListener() {
|
||||
return this.errorListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPathPrefix() {
|
||||
return pathPrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebClientProvider withDefaultHeaders(HttpHeaders headers) {
|
||||
|
||||
Assert.notNull(headers, "HttpHeaders must not be null.");
|
||||
|
||||
HttpHeaders merged = new HttpHeaders();
|
||||
merged.addAll(this.headers);
|
||||
merged.addAll(headers);
|
||||
|
||||
return new DefaultWebClientProvider(scheme, connector, errorListener, merged, pathPrefix, webClientConfigurer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebClientProvider withErrorListener(Consumer<Throwable> errorListener) {
|
||||
|
||||
Assert.notNull(errorListener, "Error listener must not be null.");
|
||||
|
||||
Consumer<Throwable> listener = this.errorListener.andThen(errorListener);
|
||||
return new DefaultWebClientProvider(scheme, this.connector, listener, headers, pathPrefix, webClientConfigurer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebClientProvider withPathPrefix(String pathPrefix) {
|
||||
Assert.notNull(pathPrefix, "pathPrefix must not be null.");
|
||||
|
||||
return new DefaultWebClientProvider(this.scheme, this.connector, this.errorListener, this.headers, pathPrefix,
|
||||
webClientConfigurer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebClientProvider withWebClientConfigurer(Function<WebClient, WebClient> webClientConfigurer) {
|
||||
return new DefaultWebClientProvider(scheme, connector, errorListener, headers, pathPrefix, webClientConfigurer);
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected WebClient createWebClientForSocketAddress(InetSocketAddress socketAddress) {
|
||||
|
||||
Builder builder = WebClient.builder().defaultHeaders(it -> it.addAll(getDefaultHeaders()));
|
||||
|
||||
if (connector != null) {
|
||||
builder = builder.clientConnector(connector);
|
||||
}
|
||||
|
||||
String baseUrl = String.format("%s://%s:%d%s", this.scheme, socketAddress.getHostString(), socketAddress.getPort(),
|
||||
pathPrefix == null ? "" : '/' + pathPrefix);
|
||||
WebClient webClient = builder.baseUrl(baseUrl).filter((request, next) -> next.exchange(request).doOnError(errorListener)).build();
|
||||
return webClientConfigurer.apply(webClient);
|
||||
}
|
||||
}
|
||||
-149
@@ -1,149 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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.reactive;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.data.elasticsearch.client.ElasticsearchHost;
|
||||
import org.springframework.data.elasticsearch.client.NoReachableHostException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* Infrastructure helper class aware of hosts within the cluster and the health of those allowing easy selection of
|
||||
* active ones.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 3.2
|
||||
*/
|
||||
public interface HostProvider {
|
||||
|
||||
/**
|
||||
* Create a new {@link HostProvider} best suited for the given {@link WebClientProvider} and number of hosts.
|
||||
*
|
||||
* @param clientProvider must not be {@literal null} .
|
||||
* @param endpoints must not be {@literal null} nor empty.
|
||||
* @return new instance of {@link HostProvider}.
|
||||
*/
|
||||
static HostProvider provider(WebClientProvider clientProvider, InetSocketAddress... endpoints) {
|
||||
|
||||
Assert.notNull(clientProvider, "WebClientProvider must not be null");
|
||||
Assert.notEmpty(endpoints, "Please provide at least one endpoint to connect to.");
|
||||
|
||||
if (endpoints.length == 1) {
|
||||
return new SingleNodeHostProvider(clientProvider, endpoints[0]);
|
||||
} else {
|
||||
return new MultiNodeHostProvider(clientProvider, endpoints);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup an active host in {@link Verification#LAZY lazy} mode utilizing cached {@link ElasticsearchHost}.
|
||||
*
|
||||
* @return the {@link Mono} emitting the active host or {@link Mono#error(Throwable) an error} if none found.
|
||||
*/
|
||||
default Mono<InetSocketAddress> lookupActiveHost() {
|
||||
return lookupActiveHost(Verification.LAZY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup an active host in using the given {@link Verification}.
|
||||
*
|
||||
* @param verification
|
||||
* @return the {@link Mono} emitting the active host or {@link Mono#error(Throwable) an error}
|
||||
* ({@link NoReachableHostException}) if none found.
|
||||
*/
|
||||
Mono<InetSocketAddress> lookupActiveHost(Verification verification);
|
||||
|
||||
/**
|
||||
* Get the {@link WebClient} connecting to an active host utilizing cached {@link ElasticsearchHost}.
|
||||
*
|
||||
* @return the {@link Mono} emitting the client for an active host or {@link Mono#error(Throwable) an error} if none
|
||||
* found.
|
||||
*/
|
||||
default Mono<WebClient> getActive() {
|
||||
return getActive(Verification.LAZY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link WebClient} connecting to an active host.
|
||||
*
|
||||
* @param verification must not be {@literal null}.
|
||||
* @return the {@link Mono} emitting the client for an active host or {@link Mono#error(Throwable) an error} if none
|
||||
* found.
|
||||
*/
|
||||
default Mono<WebClient> getActive(Verification verification) {
|
||||
return lookupActiveHost(verification).map(this::createWebClient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link WebClient} for {@link InetSocketAddress endpoint}.
|
||||
*
|
||||
* @param endpoint must not be {@literal null}.
|
||||
* @return a {@link WebClient} using the the given endpoint as {@literal base url}.
|
||||
*/
|
||||
WebClient createWebClient(InetSocketAddress endpoint);
|
||||
|
||||
/**
|
||||
* Obtain information about known cluster nodes.
|
||||
*
|
||||
* @return the {@link Mono} emitting {@link ClusterInformation} when available.
|
||||
*/
|
||||
Mono<ClusterInformation> clusterInfo();
|
||||
|
||||
/**
|
||||
* {@link Verification} allows to influence the lookup strategy for active hosts.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 3.2
|
||||
*/
|
||||
enum Verification {
|
||||
|
||||
/**
|
||||
* Actively check for cluster node health.
|
||||
*/
|
||||
ACTIVE,
|
||||
|
||||
/**
|
||||
* Use cached data for cluster node health.
|
||||
*/
|
||||
LAZY
|
||||
}
|
||||
|
||||
/**
|
||||
* Value object accumulating information about an Elasticsearch cluster.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 3.2
|
||||
*/
|
||||
class ClusterInformation {
|
||||
|
||||
private final Set<ElasticsearchHost> nodes;
|
||||
|
||||
public ClusterInformation(Set<ElasticsearchHost> nodes) {
|
||||
this.nodes = nodes;
|
||||
}
|
||||
|
||||
public Set<ElasticsearchHost> getNodes() {
|
||||
return Collections.unmodifiableSet(nodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
-154
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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.reactive;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.util.function.Tuple2;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.data.elasticsearch.client.ElasticsearchHost;
|
||||
import org.springframework.data.elasticsearch.client.ElasticsearchHost.State;
|
||||
import org.springframework.data.elasticsearch.client.NoReachableHostException;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* {@link HostProvider} for a cluster of nodes.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 3.2
|
||||
*/
|
||||
class MultiNodeHostProvider implements HostProvider {
|
||||
|
||||
private final WebClientProvider clientProvider;
|
||||
private final Map<InetSocketAddress, ElasticsearchHost> hosts;
|
||||
|
||||
MultiNodeHostProvider(WebClientProvider clientProvider, InetSocketAddress... endpoints) {
|
||||
|
||||
this.clientProvider = clientProvider;
|
||||
this.hosts = new ConcurrentHashMap<>();
|
||||
for (InetSocketAddress endpoint : endpoints) {
|
||||
this.hosts.put(endpoint, new ElasticsearchHost(endpoint, State.UNKNOWN));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.HostProvider#clusterInfo()
|
||||
*/
|
||||
@Override
|
||||
public Mono<ClusterInformation> clusterInfo() {
|
||||
return nodes(null).map(this::updateNodeState).buffer(hosts.size())
|
||||
.then(Mono.just(new ClusterInformation(new LinkedHashSet<>(this.hosts.values()))));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.HostProvider#createWebClient(java.net.InetSocketAddress)
|
||||
*/
|
||||
@Override
|
||||
public WebClient createWebClient(InetSocketAddress endpoint) {
|
||||
return this.clientProvider.get(endpoint);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.HostProvider#lookupActiveHost(org.springframework.data.elasticsearch.client.reactive.HostProvider.Verification)
|
||||
*/
|
||||
@Override
|
||||
public Mono<InetSocketAddress> lookupActiveHost(Verification verification) {
|
||||
|
||||
if (Verification.LAZY.equals(verification)) {
|
||||
for (ElasticsearchHost entry : hosts()) {
|
||||
if (entry.isOnline()) {
|
||||
return Mono.just(entry.getEndpoint());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return findActiveHostInKnownActives() //
|
||||
.switchIfEmpty(findActiveHostInUnresolved()) //
|
||||
.switchIfEmpty(findActiveHostInDead()) //
|
||||
.switchIfEmpty(Mono.error(() -> new NoReachableHostException(new LinkedHashSet<>(getCachedHostState()))));
|
||||
}
|
||||
|
||||
Collection<ElasticsearchHost> getCachedHostState() {
|
||||
return hosts.values();
|
||||
}
|
||||
|
||||
private Mono<InetSocketAddress> findActiveHostInKnownActives() {
|
||||
return findActiveForSate(State.ONLINE);
|
||||
}
|
||||
|
||||
private Mono<InetSocketAddress> findActiveHostInUnresolved() {
|
||||
return findActiveForSate(State.UNKNOWN);
|
||||
}
|
||||
|
||||
private Mono<InetSocketAddress> findActiveHostInDead() {
|
||||
return findActiveForSate(State.OFFLINE);
|
||||
}
|
||||
|
||||
private Mono<InetSocketAddress> findActiveForSate(State state) {
|
||||
return nodes(state).map(this::updateNodeState).filter(ElasticsearchHost::isOnline)
|
||||
.map(ElasticsearchHost::getEndpoint).next();
|
||||
}
|
||||
|
||||
private ElasticsearchHost updateNodeState(Tuple2<InetSocketAddress, ClientResponse> tuple2) {
|
||||
|
||||
State state = tuple2.getT2().statusCode().isError() ? State.OFFLINE : State.ONLINE;
|
||||
ElasticsearchHost elasticsearchHost = new ElasticsearchHost(tuple2.getT1(), state);
|
||||
hosts.put(tuple2.getT1(), elasticsearchHost);
|
||||
return elasticsearchHost;
|
||||
}
|
||||
|
||||
private Flux<Tuple2<InetSocketAddress, ClientResponse>> nodes(@Nullable State state) {
|
||||
|
||||
return Flux.fromIterable(hosts()) //
|
||||
.filter(entry -> state == null || entry.getState().equals(state)) //
|
||||
.map(ElasticsearchHost::getEndpoint) //
|
||||
.flatMap(host -> {
|
||||
|
||||
Mono<ClientResponse> exchange = createWebClient(host) //
|
||||
.head().uri("/").exchange().doOnError(throwable -> {
|
||||
|
||||
hosts.put(host, new ElasticsearchHost(host, State.OFFLINE));
|
||||
clientProvider.getErrorListener().accept(throwable);
|
||||
});
|
||||
|
||||
return Mono.just(host).zipWith(exchange);
|
||||
}) //
|
||||
.onErrorContinue((throwable, o) -> clientProvider.getErrorListener().accept(throwable));
|
||||
}
|
||||
|
||||
private List<ElasticsearchHost> hosts() {
|
||||
|
||||
List<ElasticsearchHost> hosts = new ArrayList<>(this.hosts.values());
|
||||
Collections.shuffle(hosts);
|
||||
|
||||
return hosts;
|
||||
}
|
||||
}
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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.reactive;
|
||||
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.elasticsearch.action.ActionResponse;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.client.reactive.ClientHttpResponse;
|
||||
import org.springframework.web.reactive.function.BodyExtractor;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
|
||||
/**
|
||||
* Extension to {@link ActionResponse} that also delegates to {@link ClientResponse}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Mark Paluch
|
||||
* @since 3.2
|
||||
*/
|
||||
class RawActionResponse extends ActionResponse {
|
||||
|
||||
private final ClientResponse delegate;
|
||||
|
||||
private RawActionResponse(ClientResponse delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
static RawActionResponse create(ClientResponse response) {
|
||||
return new RawActionResponse(response);
|
||||
}
|
||||
|
||||
public HttpStatus statusCode() {
|
||||
return delegate.statusCode();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.web.reactive.function.client.ClientResponse#headers()
|
||||
*/
|
||||
public ClientResponse.Headers headers() {
|
||||
return delegate.headers();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.web.reactive.function.client.ClientResponse#body(org.springframework.web.reactive.function.BodyExtractor)
|
||||
*/
|
||||
public <T> T body(BodyExtractor<T, ? super ClientHttpResponse> extractor) {
|
||||
return delegate.body(extractor);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* until Elasticsearch 7.4 this empty implementation was available in the abstract base class
|
||||
*/
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
}
|
||||
}
|
||||
-909
@@ -1,909 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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.reactive;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.net.ConnectException;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.elasticsearch.action.admin.indices.close.CloseIndexRequest;
|
||||
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
|
||||
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
|
||||
import org.elasticsearch.action.admin.indices.flush.FlushRequest;
|
||||
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
|
||||
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
|
||||
import org.elasticsearch.action.admin.indices.open.OpenIndexRequest;
|
||||
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
|
||||
import org.elasticsearch.action.bulk.BulkRequest;
|
||||
import org.elasticsearch.action.bulk.BulkResponse;
|
||||
import org.elasticsearch.action.delete.DeleteRequest;
|
||||
import org.elasticsearch.action.delete.DeleteResponse;
|
||||
import org.elasticsearch.action.get.GetRequest;
|
||||
import org.elasticsearch.action.get.MultiGetRequest;
|
||||
import org.elasticsearch.action.index.IndexRequest;
|
||||
import org.elasticsearch.action.index.IndexResponse;
|
||||
import org.elasticsearch.action.main.MainResponse;
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.action.update.UpdateRequest;
|
||||
import org.elasticsearch.action.update.UpdateResponse;
|
||||
import org.elasticsearch.index.get.GetResult;
|
||||
import org.elasticsearch.index.reindex.BulkByScrollResponse;
|
||||
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
import org.springframework.data.elasticsearch.client.ClientConfiguration;
|
||||
import org.springframework.data.elasticsearch.client.ElasticsearchHost;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* A reactive client to connect to Elasticsearch.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Henrique Amaral
|
||||
* @since 3.2
|
||||
* @see ClientConfiguration
|
||||
* @see ReactiveRestClients
|
||||
*/
|
||||
public interface ReactiveElasticsearchClient {
|
||||
|
||||
/**
|
||||
* Pings the remote Elasticsearch cluster and emits {@literal true} if the ping succeeded, {@literal false} otherwise.
|
||||
*
|
||||
* @return the {@link Mono} emitting the result of the ping attempt.
|
||||
*/
|
||||
default Mono<Boolean> ping() {
|
||||
return ping(HttpHeaders.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pings the remote Elasticsearch cluster and emits {@literal true} if the ping succeeded, {@literal false} otherwise.
|
||||
*
|
||||
* @param headers Use {@link HttpHeaders} to provide eg. authentication data. Must not be {@literal null}.
|
||||
* @return the {@link Mono} emitting the result of the ping attempt.
|
||||
*/
|
||||
Mono<Boolean> ping(HttpHeaders headers);
|
||||
|
||||
/**
|
||||
* Get the cluster info otherwise provided when sending an HTTP request to port 9200.
|
||||
*
|
||||
* @return the {@link Mono} emitting the result of the info request.
|
||||
*/
|
||||
default Mono<MainResponse> info() {
|
||||
return info(HttpHeaders.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cluster info otherwise provided when sending an HTTP request to port 9200.
|
||||
*
|
||||
* @param headers Use {@link HttpHeaders} to provide eg. authentication data. Must not be {@literal null}.
|
||||
* @return the {@link Mono} emitting the result of the info request.
|
||||
*/
|
||||
Mono<MainResponse> info(HttpHeaders headers);
|
||||
|
||||
/**
|
||||
* Execute a {@link GetRequest} against the {@literal get} API to retrieve a document by id.
|
||||
*
|
||||
* @param consumer never {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-get.html">Get API on
|
||||
* elastic.co</a>
|
||||
* @return the {@link Mono} emitting the {@link GetResult result}.
|
||||
*/
|
||||
default Mono<GetResult> get(Consumer<GetRequest> consumer) {
|
||||
|
||||
GetRequest request = new GetRequest();
|
||||
consumer.accept(request);
|
||||
return get(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link GetRequest} against the {@literal get} API to retrieve a document by id.
|
||||
*
|
||||
* @param getRequest must not be {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-get.html">Get API on
|
||||
* elastic.co</a>
|
||||
* @return the {@link Mono} emitting the {@link GetResult result}.
|
||||
*/
|
||||
default Mono<GetResult> get(GetRequest getRequest) {
|
||||
return get(HttpHeaders.EMPTY, getRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link GetRequest} against the {@literal get} API to retrieve a document by id.
|
||||
*
|
||||
* @param headers Use {@link HttpHeaders} to provide eg. authentication data. Must not be {@literal null}.
|
||||
* @param getRequest must not be {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-get.html">Get API on
|
||||
* elastic.co</a>
|
||||
* @return the {@link Mono} emitting the {@link GetResult result}.
|
||||
*/
|
||||
Mono<GetResult> get(HttpHeaders headers, GetRequest getRequest);
|
||||
|
||||
/**
|
||||
* Execute a {@link MultiGetRequest} against the {@literal multi-get} API to retrieve multiple documents by id.
|
||||
*
|
||||
* @param consumer never {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-multi-get.html">Multi Get API on
|
||||
* elastic.co</a>
|
||||
* @return the {@link Flux} emitting the {@link GetResult result}.
|
||||
*/
|
||||
default Flux<GetResult> multiGet(Consumer<MultiGetRequest> consumer) {
|
||||
|
||||
MultiGetRequest request = new MultiGetRequest();
|
||||
consumer.accept(request);
|
||||
return multiGet(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link MultiGetRequest} against the {@literal multi-get} API to retrieve multiple documents by
|
||||
* id.
|
||||
*
|
||||
* @param multiGetRequest must not be {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-multi-get.html">Multi Get API on
|
||||
* elastic.co</a>
|
||||
* @return the {@link Flux} emitting the {@link GetResult result}.
|
||||
*/
|
||||
default Flux<GetResult> multiGet(MultiGetRequest multiGetRequest) {
|
||||
return multiGet(HttpHeaders.EMPTY, multiGetRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link MultiGetRequest} against the {@literal multi-get} API to retrieve multiple documents by
|
||||
* id.
|
||||
*
|
||||
* @param headers Use {@link HttpHeaders} to provide eg. authentication data. Must not be {@literal null}.
|
||||
* @param multiGetRequest must not be {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-multi-get.html">Multi Get API on
|
||||
* elastic.co</a>
|
||||
* @return the {@link Flux} emitting the {@link GetResult result}.
|
||||
*/
|
||||
Flux<GetResult> multiGet(HttpHeaders headers, MultiGetRequest multiGetRequest);
|
||||
|
||||
/**
|
||||
* Checks for the existence of a document. Emits {@literal true} if it exists, {@literal false} otherwise.
|
||||
*
|
||||
* @param consumer never {@literal null}.
|
||||
* @return the {@link Mono} emitting {@literal true} if it exists, {@literal false} otherwise.
|
||||
*/
|
||||
default Mono<Boolean> exists(Consumer<GetRequest> consumer) {
|
||||
|
||||
GetRequest request = new GetRequest();
|
||||
consumer.accept(request);
|
||||
return exists(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for the existence of a document. Emits {@literal true} if it exists, {@literal false} otherwise.
|
||||
*
|
||||
* @param getRequest must not be {@literal null}.
|
||||
* @return the {@link Mono} emitting {@literal true} if it exists, {@literal false} otherwise.
|
||||
*/
|
||||
default Mono<Boolean> exists(GetRequest getRequest) {
|
||||
return exists(HttpHeaders.EMPTY, getRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for the existence of a document. Emits {@literal true} if it exists, {@literal false} otherwise.
|
||||
*
|
||||
* @param headers Use {@link HttpHeaders} to provide eg. authentication data. Must not be {@literal null}.
|
||||
* @param getRequest must not be {@literal null}.
|
||||
* @return the {@link Mono} emitting {@literal true} if it exists, {@literal false} otherwise.
|
||||
*/
|
||||
Mono<Boolean> exists(HttpHeaders headers, GetRequest getRequest);
|
||||
|
||||
/**
|
||||
* Execute an {@link IndexRequest} against the {@literal index} API to index a document.
|
||||
*
|
||||
* @param consumer never {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-index.html">Index API on
|
||||
* elastic.co</a>
|
||||
* @return the {@link Mono} emitting the {@link IndexResponse}.
|
||||
*/
|
||||
default Mono<IndexResponse> index(Consumer<IndexRequest> consumer) {
|
||||
|
||||
IndexRequest request = new IndexRequest();
|
||||
consumer.accept(request);
|
||||
return index(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link IndexRequest} against the {@literal index} API to index a document.
|
||||
*
|
||||
* @param indexRequest must not be {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-index.html">Index API on
|
||||
* elastic.co</a>
|
||||
* @return the {@link Mono} emitting the {@link IndexResponse}.
|
||||
*/
|
||||
default Mono<IndexResponse> index(IndexRequest indexRequest) {
|
||||
return index(HttpHeaders.EMPTY, indexRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link IndexRequest} against the {@literal index} API to index a document.
|
||||
*
|
||||
* @param headers Use {@link HttpHeaders} to provide eg. authentication data. Must not be {@literal null}.
|
||||
* @param indexRequest must not be {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-index.html">Index API on
|
||||
* elastic.co</a>
|
||||
* @return the {@link Mono} emitting the {@link IndexResponse}.
|
||||
*/
|
||||
Mono<IndexResponse> index(HttpHeaders headers, IndexRequest indexRequest);
|
||||
|
||||
/**
|
||||
* Gain access to index related commands.
|
||||
*
|
||||
* @return access to index related commands.
|
||||
*/
|
||||
Indices indices();
|
||||
|
||||
/**
|
||||
* Execute an {@link UpdateRequest} against the {@literal update} API to alter a document.
|
||||
*
|
||||
* @param consumer never {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update.html">Update API on
|
||||
* elastic.co</a>
|
||||
* @return the {@link Mono} emitting the {@link UpdateResponse}.
|
||||
*/
|
||||
default Mono<UpdateResponse> update(Consumer<UpdateRequest> consumer) {
|
||||
|
||||
UpdateRequest request = new UpdateRequest();
|
||||
consumer.accept(request);
|
||||
return update(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link UpdateRequest} against the {@literal update} API to alter a document.
|
||||
*
|
||||
* @param updateRequest must not be {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update.html">Update API on
|
||||
* elastic.co</a>
|
||||
* @return the {@link Mono} emitting the {@link UpdateResponse}.
|
||||
*/
|
||||
default Mono<UpdateResponse> update(UpdateRequest updateRequest) {
|
||||
return update(HttpHeaders.EMPTY, updateRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link UpdateRequest} against the {@literal update} API to alter a document.
|
||||
*
|
||||
* @param headers Use {@link HttpHeaders} to provide eg. authentication data. Must not be {@literal null}.
|
||||
* @param updateRequest must not be {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update.html">Update API on
|
||||
* elastic.co</a>
|
||||
* @return the {@link Mono} emitting the {@link UpdateResponse}.
|
||||
*/
|
||||
Mono<UpdateResponse> update(HttpHeaders headers, UpdateRequest updateRequest);
|
||||
|
||||
/**
|
||||
* Execute a {@link DeleteRequest} against the {@literal delete} API to remove a document.
|
||||
*
|
||||
* @param consumer never {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete.html">Delete API on
|
||||
* elastic.co</a>
|
||||
* @return the {@link Mono} emitting the {@link DeleteResponse}.
|
||||
*/
|
||||
default Mono<DeleteResponse> delete(Consumer<DeleteRequest> consumer) {
|
||||
|
||||
DeleteRequest request = new DeleteRequest();
|
||||
consumer.accept(request);
|
||||
return delete(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link DeleteRequest} against the {@literal delete} API to remove a document.
|
||||
*
|
||||
* @param deleteRequest must not be {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete.html">Delete API on
|
||||
* elastic.co</a>
|
||||
* @return the {@link Mono} emitting the {@link DeleteResponse}.
|
||||
*/
|
||||
default Mono<DeleteResponse> delete(DeleteRequest deleteRequest) {
|
||||
return delete(HttpHeaders.EMPTY, deleteRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link DeleteRequest} against the {@literal delete} API to remove a document.
|
||||
*
|
||||
* @param headers Use {@link HttpHeaders} to provide eg. authentication data. Must not be {@literal null}.
|
||||
* @param deleteRequest must not be {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete.html">Delete API on
|
||||
* elastic.co</a>
|
||||
* @return the {@link Mono} emitting the {@link DeleteResponse}.
|
||||
*/
|
||||
Mono<DeleteResponse> delete(HttpHeaders headers, DeleteRequest deleteRequest);
|
||||
|
||||
/**
|
||||
* Execute a {@link SearchRequest} against the {@literal count} API.
|
||||
*
|
||||
* @param consumer new {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-count.html">Count API on
|
||||
* elastic.co</a>
|
||||
* @return the {@link Mono} emitting the count result.
|
||||
* @since 4.0
|
||||
*/
|
||||
default Mono<Long> count(Consumer<SearchRequest> consumer) {
|
||||
|
||||
SearchRequest searchRequest = new SearchRequest();
|
||||
consumer.accept(searchRequest);
|
||||
return count(searchRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a {@link SearchRequest} against the {@literal search} API.
|
||||
*
|
||||
* @param searchRequest must not be {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-count.html">Count API on
|
||||
* elastic.co</a>
|
||||
* @return the {@link Mono} emitting the count result.
|
||||
* @since 4.0
|
||||
*/
|
||||
default Mono<Long> count(SearchRequest searchRequest) {
|
||||
return count(HttpHeaders.EMPTY, searchRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a {@link SearchRequest} against the {@literal search} API.
|
||||
*
|
||||
* @param headers Use {@link HttpHeaders} to provide eg. authentication data. Must not be {@literal null}.
|
||||
* @param searchRequest must not be {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-count.html">Count API on
|
||||
* elastic.co</a>
|
||||
* @return the {@link Mono} emitting the count result.
|
||||
* @since 4.0
|
||||
*/
|
||||
Mono<Long> count(HttpHeaders headers, SearchRequest searchRequest);
|
||||
|
||||
/**
|
||||
* Execute a {@link SearchRequest} against the {@literal search} API.
|
||||
*
|
||||
* @param consumer never {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html">Search API on
|
||||
* elastic.co</a>
|
||||
* @return the {@link Flux} emitting {@link SearchHit hits} one by one.
|
||||
*/
|
||||
default Flux<SearchHit> search(Consumer<SearchRequest> consumer) {
|
||||
|
||||
SearchRequest request = new SearchRequest();
|
||||
consumer.accept(request);
|
||||
return search(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link SearchRequest} against the {@literal search} API.
|
||||
*
|
||||
* @param searchRequest must not be {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html">Search API on
|
||||
* elastic.co</a>
|
||||
* @return the {@link Flux} emitting {@link SearchHit hits} one by one.
|
||||
*/
|
||||
default Flux<SearchHit> search(SearchRequest searchRequest) {
|
||||
return search(HttpHeaders.EMPTY, searchRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link SearchRequest} against the {@literal search} API.
|
||||
*
|
||||
* @param headers Use {@link HttpHeaders} to provide eg. authentication data. Must not be {@literal null}.
|
||||
* @param searchRequest must not be {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html">Search API on
|
||||
* elastic.co</a>
|
||||
* @return the {@link Flux} emitting {@link SearchHit hits} one by one.
|
||||
*/
|
||||
Flux<SearchHit> search(HttpHeaders headers, SearchRequest searchRequest);
|
||||
|
||||
/**
|
||||
* Execute the given {@link SearchRequest} against the {@literal search scroll} API.
|
||||
*
|
||||
* @param searchRequest must not be {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-scroll.html">Search
|
||||
* Scroll API on elastic.co</a>
|
||||
* @return the {@link Flux} emitting {@link SearchHit hits} one by one.
|
||||
*/
|
||||
default Flux<SearchHit> scroll(SearchRequest searchRequest) {
|
||||
return scroll(HttpHeaders.EMPTY, searchRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link SearchRequest} against the {@literal search scroll} API. <br />
|
||||
* Scroll keeps track of {@link SearchResponse#getScrollId() scrollIds} returned by the server and provides them when
|
||||
* requesting more results via {@code _search/scroll}. All bound server resources are freed on completion.
|
||||
*
|
||||
* @param headers Use {@link HttpHeaders} to provide eg. authentication data. Must not be {@literal null}.
|
||||
* @param searchRequest must not be {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-scroll.html">Search
|
||||
* Scroll API on elastic.co</a>
|
||||
* @return the {@link Flux} emitting {@link SearchHit hits} one by one.
|
||||
*/
|
||||
Flux<SearchHit> scroll(HttpHeaders headers, SearchRequest searchRequest);
|
||||
|
||||
/**
|
||||
* Execute a {@link DeleteByQueryRequest} against the {@literal delete by query} API.
|
||||
*
|
||||
* @param consumer never {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete-by-query.html">Delete By
|
||||
* Query API on elastic.co</a>
|
||||
* @return a {@link Mono} emitting the emitting operation response.
|
||||
*/
|
||||
default Mono<BulkByScrollResponse> deleteBy(Consumer<DeleteByQueryRequest> consumer) {
|
||||
|
||||
DeleteByQueryRequest request = new DeleteByQueryRequest();
|
||||
consumer.accept(request);
|
||||
return deleteBy(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a {@link DeleteByQueryRequest} against the {@literal delete by query} API.
|
||||
*
|
||||
* @param deleteRequest must not be {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete-by-query.html">Delete By
|
||||
* Query API on elastic.co</a>
|
||||
* @return a {@link Mono} emitting the emitting operation response.
|
||||
*/
|
||||
default Mono<BulkByScrollResponse> deleteBy(DeleteByQueryRequest deleteRequest) {
|
||||
return deleteBy(HttpHeaders.EMPTY, deleteRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a {@link DeleteByQueryRequest} against the {@literal delete by query} API.
|
||||
*
|
||||
* @param headers Use {@link HttpHeaders} to provide eg. authentication data. Must not be {@literal null}.
|
||||
* @param deleteRequest must not be {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete-by-query.html">Delete By
|
||||
* Query API on elastic.co</a>
|
||||
* @return a {@link Mono} emitting operation response.
|
||||
*/
|
||||
Mono<BulkByScrollResponse> deleteBy(HttpHeaders headers, DeleteByQueryRequest deleteRequest);
|
||||
|
||||
/**
|
||||
* Execute a {@link BulkRequest} against the {@literal bulk} API.
|
||||
*
|
||||
* @param consumer never {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html">Bulk API on
|
||||
* elastic.co</a>
|
||||
* @return a {@link Mono} emitting the emitting operation response.
|
||||
*/
|
||||
default Mono<BulkResponse> bulk(Consumer<BulkRequest> consumer) {
|
||||
|
||||
BulkRequest request = new BulkRequest();
|
||||
consumer.accept(request);
|
||||
return bulk(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a {@link BulkRequest} against the {@literal bulk} API.
|
||||
*
|
||||
* @param bulkRequest must not be {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html">Bulk API on
|
||||
* elastic.co</a>
|
||||
* @return a {@link Mono} emitting the emitting operation response.
|
||||
*/
|
||||
default Mono<BulkResponse> bulk(BulkRequest bulkRequest) {
|
||||
return bulk(HttpHeaders.EMPTY, bulkRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a {@link BulkRequest} against the {@literal bulk} API.
|
||||
*
|
||||
* @param headers Use {@link HttpHeaders} to provide eg. authentication data. Must not be {@literal null}.
|
||||
* @param bulkRequest must not be {@literal null}.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html">Bulk API on
|
||||
* elastic.co</a>
|
||||
* @return a {@link Mono} emitting operation response.
|
||||
*/
|
||||
Mono<BulkResponse> bulk(HttpHeaders headers, BulkRequest bulkRequest);
|
||||
|
||||
/**
|
||||
* Compose the actual command/s to run against Elasticsearch using the underlying {@link WebClient connection}.
|
||||
* {@link #execute(ReactiveElasticsearchClientCallback) Execute} selects an active server from the available ones and
|
||||
* retries operations that fail with a {@link ConnectException} on another node if the previously selected one becomes
|
||||
* unavailable.
|
||||
*
|
||||
* @param callback the {@link ReactiveElasticsearchClientCallback callback} wielding the actual command to run.
|
||||
* @return the {@link Mono} emitting the {@link ClientResponse} once subscribed.
|
||||
*/
|
||||
Mono<ClientResponse> execute(ReactiveElasticsearchClientCallback callback);
|
||||
|
||||
/**
|
||||
* Get the current client {@link Status}. <br />
|
||||
* <strong>NOTE</strong> the actual implementation might choose to actively check the current cluster state by pinging
|
||||
* known nodes.
|
||||
*
|
||||
* @return the actual {@link Status} information.
|
||||
*/
|
||||
Mono<Status> status();
|
||||
|
||||
/**
|
||||
* Low level callback interface operating upon {@link WebClient} to send commands towards elasticsearch.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 3.2
|
||||
*/
|
||||
interface ReactiveElasticsearchClientCallback {
|
||||
Mono<ClientResponse> doWithClient(WebClient client);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cumulative client {@link ElasticsearchHost} information.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 3.2
|
||||
*/
|
||||
interface Status {
|
||||
|
||||
/**
|
||||
* Get the collection of known hosts.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
Collection<ElasticsearchHost> hosts();
|
||||
|
||||
/**
|
||||
* @return {@literal true} if at least one host is available.
|
||||
*/
|
||||
default boolean isOk() {
|
||||
|
||||
Collection<ElasticsearchHost> hosts = hosts();
|
||||
|
||||
if (CollectionUtils.isEmpty(hosts)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return hosts().stream().anyMatch(ElasticsearchHost::isOnline);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulation of methods for accessing the Indices API.
|
||||
*
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/client/java-rest/master/_indices_apis.html">Indices
|
||||
* API</a>.
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
interface Indices {
|
||||
|
||||
/**
|
||||
* Execute the given {@link GetIndexRequest} against the {@literal indices} API.
|
||||
*
|
||||
* @param consumer never {@literal null}.
|
||||
* @return the {@link Mono} emitting {@literal true} if the index exists, {@literal false} otherwise.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-exists.html"> Indices
|
||||
* Exists API on elastic.co</a>
|
||||
*/
|
||||
default Mono<Boolean> existsIndex(Consumer<GetIndexRequest> consumer) {
|
||||
|
||||
GetIndexRequest request = new GetIndexRequest();
|
||||
consumer.accept(request);
|
||||
return existsIndex(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link GetIndexRequest} against the {@literal indices} API.
|
||||
*
|
||||
* @param getIndexRequest must not be {@literal null}.
|
||||
* @return the {@link Mono} emitting {@literal true} if the index exists, {@literal false} otherwise.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-exists.html"> Indices
|
||||
* Exists API on elastic.co</a>
|
||||
*/
|
||||
default Mono<Boolean> existsIndex(GetIndexRequest getIndexRequest) {
|
||||
return existsIndex(HttpHeaders.EMPTY, getIndexRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link GetIndexRequest} against the {@literal indices} API.
|
||||
*
|
||||
* @param headers Use {@link HttpHeaders} to provide eg. authentication data. Must not be {@literal null}.
|
||||
* @param getIndexRequest must not be {@literal null}.
|
||||
* @return the {@link Mono} emitting {@literal true} if the index exists, {@literal false} otherwise.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-exists.html"> Indices
|
||||
* Exists API on elastic.co</a>
|
||||
*/
|
||||
Mono<Boolean> existsIndex(HttpHeaders headers, GetIndexRequest getIndexRequest);
|
||||
|
||||
/**
|
||||
* Execute the given {@link DeleteIndexRequest} 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-delete-index.html"> Indices
|
||||
* Delete API on elastic.co</a>
|
||||
*/
|
||||
default Mono<Void> deleteIndex(Consumer<DeleteIndexRequest> consumer) {
|
||||
|
||||
DeleteIndexRequest request = new DeleteIndexRequest();
|
||||
consumer.accept(request);
|
||||
return deleteIndex(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link DeleteIndexRequest} against the {@literal indices} API.
|
||||
*
|
||||
* @param deleteIndexRequest 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-delete-index.html"> Indices
|
||||
* Delete API on elastic.co</a>
|
||||
*/
|
||||
default Mono<Void> deleteIndex(DeleteIndexRequest deleteIndexRequest) {
|
||||
return deleteIndex(HttpHeaders.EMPTY, deleteIndexRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link DeleteIndexRequest} against the {@literal indices} API.
|
||||
*
|
||||
* @param headers Use {@link HttpHeaders} to provide eg. authentication data. Must not be {@literal null}.
|
||||
* @param deleteIndexRequest 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-delete-index.html"> Indices
|
||||
* Delete API on elastic.co</a>
|
||||
*/
|
||||
Mono<Void> deleteIndex(HttpHeaders headers, DeleteIndexRequest deleteIndexRequest);
|
||||
|
||||
/**
|
||||
* Execute the given {@link CreateIndexRequest} 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
|
||||
* already exist.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-create-index.html"> Indices
|
||||
* Create API on elastic.co</a>
|
||||
*/
|
||||
default Mono<Void> createIndex(Consumer<CreateIndexRequest> consumer) {
|
||||
|
||||
CreateIndexRequest request = new CreateIndexRequest();
|
||||
consumer.accept(request);
|
||||
return createIndex(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link CreateIndexRequest} against the {@literal indices} API.
|
||||
*
|
||||
* @param createIndexRequest must not be {@literal null}.
|
||||
* @return a {@link Mono} signalling operation completion or an {@link Mono#error(Throwable) error} if eg. the index
|
||||
* already exist.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-create-index.html"> Indices
|
||||
* Create API on elastic.co</a>
|
||||
*/
|
||||
default Mono<Void> createIndex(CreateIndexRequest createIndexRequest) {
|
||||
return createIndex(HttpHeaders.EMPTY, createIndexRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link CreateIndexRequest} against the {@literal indices} API.
|
||||
*
|
||||
* @param headers Use {@link HttpHeaders} to provide eg. authentication data. Must not be {@literal null}.
|
||||
* @param createIndexRequest must not be {@literal null}.
|
||||
* @return a {@link Mono} signalling operation completion or an {@link Mono#error(Throwable) error} if eg. the index
|
||||
* already exist.
|
||||
* @see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-create-index.html"> Indices
|
||||
* Create API on elastic.co</a>
|
||||
*/
|
||||
Mono<Void> createIndex(HttpHeaders headers, CreateIndexRequest createIndexRequest);
|
||||
|
||||
/**
|
||||
* Execute the given {@link OpenIndexRequest} 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-open-close.html"> Indices
|
||||
* Open API on elastic.co</a>
|
||||
*/
|
||||
default Mono<Void> openIndex(Consumer<OpenIndexRequest> consumer) {
|
||||
|
||||
OpenIndexRequest request = new OpenIndexRequest();
|
||||
consumer.accept(request);
|
||||
return openIndex(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link OpenIndexRequest} against the {@literal indices} API.
|
||||
*
|
||||
* @param openIndexRequest 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-open-close.html"> Indices
|
||||
* Open API on elastic.co</a>
|
||||
*/
|
||||
default Mono<Void> openIndex(OpenIndexRequest openIndexRequest) {
|
||||
return openIndex(HttpHeaders.EMPTY, openIndexRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link OpenIndexRequest} against the {@literal indices} API.
|
||||
*
|
||||
* @param headers Use {@link HttpHeaders} to provide eg. authentication data. Must not be {@literal null}.
|
||||
* @param openIndexRequest 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-open-close.html"> Indices
|
||||
* Open API on elastic.co</a>
|
||||
*/
|
||||
Mono<Void> openIndex(HttpHeaders headers, OpenIndexRequest openIndexRequest);
|
||||
|
||||
/**
|
||||
* Execute the given {@link CloseIndexRequest} 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-open-close.html"> Indices
|
||||
* Close API on elastic.co</a>
|
||||
*/
|
||||
default Mono<Void> closeIndex(Consumer<CloseIndexRequest> consumer) {
|
||||
|
||||
CloseIndexRequest request = new CloseIndexRequest();
|
||||
consumer.accept(request);
|
||||
return closeIndex(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link CloseIndexRequest} against the {@literal indices} API.
|
||||
*
|
||||
* @param closeIndexRequest 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-open-close.html"> Indices
|
||||
* Close API on elastic.co</a>
|
||||
*/
|
||||
default Mono<Void> closeIndex(CloseIndexRequest closeIndexRequest) {
|
||||
return closeIndex(HttpHeaders.EMPTY, closeIndexRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link CloseIndexRequest} against the {@literal indices} API.
|
||||
*
|
||||
* @param headers Use {@link HttpHeaders} to provide eg. authentication data. Must not be {@literal null}.
|
||||
* @param closeIndexRequest 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-open-close.html"> Indices
|
||||
* CLose API on elastic.co</a>
|
||||
*/
|
||||
Mono<Void> closeIndex(HttpHeaders headers, CloseIndexRequest closeIndexRequest);
|
||||
|
||||
/**
|
||||
* Execute the given {@link RefreshRequest} 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-refresh.html"> Indices
|
||||
* Refresh API on elastic.co</a>
|
||||
*/
|
||||
default Mono<Void> refreshIndex(Consumer<RefreshRequest> consumer) {
|
||||
|
||||
RefreshRequest request = new RefreshRequest();
|
||||
consumer.accept(request);
|
||||
return refreshIndex(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link RefreshRequest} against the {@literal indices} API.
|
||||
*
|
||||
* @param refreshRequest 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-refresh.html"> Indices
|
||||
* Refresh API on elastic.co</a>
|
||||
*/
|
||||
default Mono<Void> refreshIndex(RefreshRequest refreshRequest) {
|
||||
return refreshIndex(HttpHeaders.EMPTY, refreshRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link RefreshRequest} against the {@literal indices} API.
|
||||
*
|
||||
* @param headers Use {@link HttpHeaders} to provide eg. authentication data. Must not be {@literal null}.
|
||||
* @param refreshRequest 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-refresh.html"> Indices
|
||||
* Refresh API on elastic.co</a>
|
||||
*/
|
||||
Mono<Void> refreshIndex(HttpHeaders headers, RefreshRequest refreshRequest);
|
||||
|
||||
/**
|
||||
* Execute the given {@link 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>
|
||||
*/
|
||||
default Mono<Void> updateMapping(Consumer<PutMappingRequest> consumer) {
|
||||
|
||||
PutMappingRequest request = new PutMappingRequest();
|
||||
consumer.accept(request);
|
||||
return updateMapping(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link 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>
|
||||
*/
|
||||
default Mono<Void> updateMapping(PutMappingRequest putMappingRequest) {
|
||||
return updateMapping(HttpHeaders.EMPTY, putMappingRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link 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>
|
||||
*/
|
||||
Mono<Void> updateMapping(HttpHeaders headers, PutMappingRequest putMappingRequest);
|
||||
|
||||
/**
|
||||
* Execute the given {@link FlushRequest} 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-flush.html"> Indices Flush
|
||||
* API on elastic.co</a>
|
||||
*/
|
||||
default Mono<Void> flushIndex(Consumer<FlushRequest> consumer) {
|
||||
|
||||
FlushRequest request = new FlushRequest();
|
||||
consumer.accept(request);
|
||||
return flushIndex(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link RefreshRequest} against the {@literal indices} API.
|
||||
*
|
||||
* @param flushRequest 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-flush.html"> Indices Flush
|
||||
* API on elastic.co</a>
|
||||
*/
|
||||
default Mono<Void> flushIndex(FlushRequest flushRequest) {
|
||||
return flushIndex(HttpHeaders.EMPTY, flushRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@link RefreshRequest} against the {@literal indices} API.
|
||||
*
|
||||
* @param headers Use {@link HttpHeaders} to provide eg. authentication data. Must not be {@literal null}.
|
||||
* @param flushRequest 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-flush.html"> Indices Flush
|
||||
* API on elastic.co</a>
|
||||
*/
|
||||
Mono<Void> flushIndex(HttpHeaders headers, FlushRequest flushRequest);
|
||||
}
|
||||
}
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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.reactive;
|
||||
|
||||
import org.springframework.data.elasticsearch.client.ClientConfiguration;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Utility class for common access to reactive Elasticsearch clients. {@link ReactiveRestClients} consolidates set up
|
||||
* routines for the various drivers into a single place.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 3.2
|
||||
*/
|
||||
public final class ReactiveRestClients {
|
||||
|
||||
private ReactiveRestClients() {}
|
||||
|
||||
/**
|
||||
* Start here to create a new client tailored to your needs.
|
||||
*
|
||||
* @return new instance of {@link ReactiveElasticsearchClient}.
|
||||
*/
|
||||
public static ReactiveElasticsearchClient create(ClientConfiguration clientConfiguration) {
|
||||
|
||||
Assert.notNull(clientConfiguration, "ClientConfiguration must not be null!");
|
||||
|
||||
return DefaultReactiveElasticsearchClient.create(clientConfiguration);
|
||||
}
|
||||
}
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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.reactive;
|
||||
|
||||
import org.springframework.web.reactive.function.client.WebClientException;
|
||||
|
||||
/**
|
||||
* Exception thrown if the request body cannot be properly encoded.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 3.2
|
||||
*/
|
||||
public class RequestBodyEncodingException extends WebClientException {
|
||||
|
||||
private static final long serialVersionUID = 472776714118912855L;
|
||||
|
||||
/**
|
||||
* Construct a new instance of {@link RequestBodyEncodingException} with the given message and exception.
|
||||
*
|
||||
* @param msg the message
|
||||
* @param ex the exception
|
||||
*/
|
||||
public RequestBodyEncodingException(String msg, Throwable ex) {
|
||||
super(msg, ex);
|
||||
}
|
||||
}
|
||||
-108
@@ -1,108 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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.reactive;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.data.elasticsearch.client.ElasticsearchHost;
|
||||
import org.springframework.data.elasticsearch.client.ElasticsearchHost.State;
|
||||
import org.springframework.data.elasticsearch.client.NoReachableHostException;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* {@link HostProvider} for a single host.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 3.2
|
||||
*/
|
||||
class SingleNodeHostProvider implements HostProvider {
|
||||
|
||||
private final WebClientProvider clientProvider;
|
||||
private final InetSocketAddress endpoint;
|
||||
private volatile ElasticsearchHost state;
|
||||
|
||||
SingleNodeHostProvider(WebClientProvider clientProvider, InetSocketAddress endpoint) {
|
||||
|
||||
this.clientProvider = clientProvider;
|
||||
this.endpoint = endpoint;
|
||||
this.state = new ElasticsearchHost(this.endpoint, State.UNKNOWN);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.HostProvider#clusterInfo()
|
||||
*/
|
||||
@Override
|
||||
public Mono<ClusterInformation> clusterInfo() {
|
||||
|
||||
return createWebClient(endpoint) //
|
||||
.head().uri("/").exchange() //
|
||||
.flatMap(it -> {
|
||||
|
||||
if (it.statusCode().isError()) {
|
||||
state = ElasticsearchHost.offline(endpoint);
|
||||
} else {
|
||||
state = ElasticsearchHost.online(endpoint);
|
||||
}
|
||||
return Mono.just(state);
|
||||
}).onErrorResume(throwable -> {
|
||||
|
||||
state = ElasticsearchHost.offline(endpoint);
|
||||
clientProvider.getErrorListener().accept(throwable);
|
||||
return Mono.just(state);
|
||||
}) //
|
||||
.flatMap(it -> Mono.just(new ClusterInformation(Collections.singleton(it))));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.HostProvider#createWebClient(java.net.InetSocketAddress)
|
||||
*/
|
||||
@Override
|
||||
public WebClient createWebClient(InetSocketAddress endpoint) {
|
||||
return this.clientProvider.get(endpoint);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.client.reactive.HostProvider#lookupActiveHost(org.springframework.data.elasticsearch.client.reactive.HostProvider.Verification)
|
||||
*/
|
||||
@Override
|
||||
public Mono<InetSocketAddress> lookupActiveHost(Verification verification) {
|
||||
|
||||
if (Verification.LAZY.equals(verification) && state.isOnline()) {
|
||||
return Mono.just(endpoint);
|
||||
}
|
||||
|
||||
return clusterInfo().flatMap(it -> {
|
||||
|
||||
ElasticsearchHost host = it.getNodes().iterator().next();
|
||||
if (host.isOnline()) {
|
||||
return Mono.just(host.getEndpoint());
|
||||
}
|
||||
|
||||
return Mono.error(() -> new NoReachableHostException(Collections.singleton(host)));
|
||||
});
|
||||
}
|
||||
|
||||
ElasticsearchHost getCachedHostState() {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
-145
@@ -1,145 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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.reactive;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.client.reactive.ClientHttpConnector;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* Provider for {@link WebClient}s using a pre-configured {@code scheme}. This class returns {@link WebClient} for a
|
||||
* specific {@link InetSocketAddress endpoint} and encapsulates common configuration aspects of {@link WebClient} so
|
||||
* that code using {@link WebClient} is not required to apply further configuration to the actual client.
|
||||
* <p/>
|
||||
* Client instances are typically cached allowing reuse of pooled connections if configured on the
|
||||
* {@link ClientHttpConnector}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @author Huw Ayling-Miller
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 3.2
|
||||
*/
|
||||
public interface WebClientProvider {
|
||||
|
||||
/**
|
||||
* Creates a new {@link WebClientProvider} using the {@code http} scheme and a default {@link ClientHttpConnector}.
|
||||
*
|
||||
* @return the resulting {@link WebClientProvider}.
|
||||
*/
|
||||
static WebClientProvider http() {
|
||||
return create("http");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link WebClientProvider} using the given {@code scheme} and a default {@link ClientHttpConnector}.
|
||||
*
|
||||
* @param scheme protocol scheme such as {@literal http} or {@literal https}.
|
||||
* @return the resulting {@link WebClientProvider}.
|
||||
*/
|
||||
static WebClientProvider create(String scheme) {
|
||||
|
||||
Assert.hasText(scheme, "Protocol scheme must not be empty");
|
||||
|
||||
return new DefaultWebClientProvider(scheme, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link WebClientProvider} given {@code scheme} and {@link ClientHttpConnector}.
|
||||
*
|
||||
* @param scheme protocol scheme such as {@literal http} or {@literal https}.
|
||||
* @param connector the HTTP connector to use. Can be {@literal null}.
|
||||
* @return the resulting {@link WebClientProvider}.
|
||||
*/
|
||||
static WebClientProvider create(String scheme, @Nullable ClientHttpConnector connector) {
|
||||
|
||||
Assert.hasText(scheme, "Protocol scheme must not be empty");
|
||||
|
||||
return new DefaultWebClientProvider(scheme, connector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the {@link WebClient} configured with {@link #withDefaultHeaders(HttpHeaders) default HTTP headers} and
|
||||
* {@link Consumer} error callback for a given {@link InetSocketAddress endpoint}.
|
||||
*
|
||||
* @return the {@link WebClient} for the given {@link InetSocketAddress endpoint}.
|
||||
*/
|
||||
WebClient get(InetSocketAddress endpoint);
|
||||
|
||||
/**
|
||||
* Obtain the {@link HttpHeaders} to be used by default.
|
||||
*
|
||||
* @return never {@literal null}. {@link HttpHeaders#EMPTY} by default.
|
||||
*/
|
||||
HttpHeaders getDefaultHeaders();
|
||||
|
||||
/**
|
||||
* Obtain the {@link Consumer error listener} to be used;
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
Consumer<Throwable> getErrorListener();
|
||||
|
||||
/**
|
||||
* Obtain the {@link String pathPrefix} to be used.
|
||||
*
|
||||
* @return the pathPrefix if set.
|
||||
* @since 4.0
|
||||
*/
|
||||
@Nullable
|
||||
String getPathPrefix();
|
||||
|
||||
/**
|
||||
* Create a new instance of {@link WebClientProvider} applying the given headers by default.
|
||||
*
|
||||
* @param headers must not be {@literal null}.
|
||||
* @return new instance of {@link WebClientProvider}.
|
||||
*/
|
||||
WebClientProvider withDefaultHeaders(HttpHeaders headers);
|
||||
|
||||
/**
|
||||
* Create a new instance of {@link WebClientProvider} calling the given {@link Consumer} on error.
|
||||
*
|
||||
* @param errorListener must not be {@literal null}.
|
||||
* @return new instance of {@link WebClientProvider}.
|
||||
*/
|
||||
WebClientProvider withErrorListener(Consumer<Throwable> errorListener);
|
||||
|
||||
/**
|
||||
* Create a new instance of {@link WebClientProvider} where HTTP requests are called with the given path prefix.
|
||||
*
|
||||
* @param pathPrefix Path prefix to add to requests
|
||||
* @return new instance of {@link WebClientProvider}
|
||||
* @since 4.0
|
||||
*/
|
||||
WebClientProvider withPathPrefix(String pathPrefix);
|
||||
|
||||
/**
|
||||
* Create a new instance of {@link WebClientProvider} calling the given {@link Function} to configure the
|
||||
* {@link WebClient}.
|
||||
*
|
||||
* @param webClientConfigurer configuration function
|
||||
* @return new instance of {@link WebClientProvider}
|
||||
* @since 4.0
|
||||
*/
|
||||
WebClientProvider withWebClientConfigurer(Function<WebClient, WebClient> webClientConfigurer);
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
@org.springframework.lang.NonNullApi
|
||||
@org.springframework.lang.NonNullFields
|
||||
package org.springframework.data.elasticsearch.client.reactive;
|
||||
-1211
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
@org.springframework.lang.NonNullApi
|
||||
@org.springframework.lang.NonNullFields
|
||||
package org.springframework.data.elasticsearch.client.util;
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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.elasticsearch.client.RestHighLevelClient;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 3.2
|
||||
* @see ElasticsearchConfigurationSupport
|
||||
*/
|
||||
public abstract class AbstractElasticsearchConfiguration extends ElasticsearchConfigurationSupport {
|
||||
|
||||
/**
|
||||
* Return the {@link RestHighLevelClient} instance used to connect to the cluster. <br />
|
||||
* Annotate with {@link Bean} in case you want to expose a {@link RestHighLevelClient} instance to the
|
||||
* {@link org.springframework.context.ApplicationContext}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public abstract RestHighLevelClient elasticsearchClient();
|
||||
|
||||
/**
|
||||
* Creates {@link ElasticsearchOperations}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
@Bean(name = { "elasticsearchOperations", "elasticsearchTemplate" })
|
||||
public ElasticsearchOperations elasticsearchOperations(ElasticsearchConverter elasticsearchConverter) {
|
||||
return new ElasticsearchRestTemplate(elasticsearchClient(), elasticsearchConverter);
|
||||
}
|
||||
}
|
||||
-82
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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.elasticsearch.action.support.IndicesOptions;
|
||||
import org.elasticsearch.action.support.WriteRequest.RefreshPolicy;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient;
|
||||
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchTemplate;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 3.2
|
||||
* @see ElasticsearchConfigurationSupport
|
||||
*/
|
||||
@Configuration
|
||||
public abstract class AbstractReactiveElasticsearchConfiguration extends ElasticsearchConfigurationSupport {
|
||||
|
||||
/**
|
||||
* Return the {@link ReactiveElasticsearchClient} instance used to connect to the cluster. <br />
|
||||
* Annotate with {@link Bean} in case you want to expose a {@link ReactiveElasticsearchClient} instance to the
|
||||
* {@link org.springframework.context.ApplicationContext}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public abstract ReactiveElasticsearchClient reactiveElasticsearchClient();
|
||||
|
||||
/**
|
||||
* Creates {@link ReactiveElasticsearchOperations}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
@Bean
|
||||
public ReactiveElasticsearchOperations reactiveElasticsearchTemplate(ElasticsearchConverter elasticsearchConverter) {
|
||||
|
||||
ReactiveElasticsearchTemplate template = new ReactiveElasticsearchTemplate(reactiveElasticsearchClient(),
|
||||
elasticsearchConverter);
|
||||
template.setIndicesOptions(indicesOptions());
|
||||
template.setRefreshPolicy(refreshPolicy());
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the write {@link RefreshPolicy}. Default is set to {@link RefreshPolicy#IMMEDIATE}.
|
||||
*
|
||||
* @return {@literal null} to use the server defaults.
|
||||
*/
|
||||
@Nullable
|
||||
protected RefreshPolicy refreshPolicy() {
|
||||
return RefreshPolicy.IMMEDIATE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the read {@link IndicesOptions}. Default is set to {@link IndicesOptions#strictExpandOpenAndForbidClosed()}.
|
||||
*
|
||||
* @return {@literal null} to use the server defaults.
|
||||
*/
|
||||
@Nullable
|
||||
protected IndicesOptions indicesOptions() {
|
||||
return IndicesOptions.strictExpandOpenAndForbidClosed();
|
||||
}
|
||||
|
||||
}
|
||||
-151
@@ -1,151 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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 lombok.SneakyThrows;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
|
||||
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.convert.ElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchCustomConversions;
|
||||
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 3.2
|
||||
*/
|
||||
@Configuration
|
||||
public class ElasticsearchConfigurationSupport {
|
||||
|
||||
@Bean
|
||||
public ElasticsearchConverter elasticsearchEntityMapper(
|
||||
SimpleElasticsearchMappingContext elasticsearchMappingContext) {
|
||||
MappingElasticsearchConverter elasticsearchConverter = new MappingElasticsearchConverter(
|
||||
elasticsearchMappingContext);
|
||||
elasticsearchConverter.setConversions(elasticsearchCustomConversions());
|
||||
return elasticsearchConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link SimpleElasticsearchMappingContext} equipped with entity classes scanned from the mapping base
|
||||
* package.
|
||||
*
|
||||
* @see #getMappingBasePackages()
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
@Bean
|
||||
@SneakyThrows
|
||||
public SimpleElasticsearchMappingContext elasticsearchMappingContext() {
|
||||
|
||||
SimpleElasticsearchMappingContext mappingContext = new SimpleElasticsearchMappingContext();
|
||||
mappingContext.setInitialEntitySet(getInitialEntitySet());
|
||||
mappingContext.setSimpleTypeHolder(elasticsearchCustomConversions().getSimpleTypeHolder());
|
||||
|
||||
return mappingContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register custom {@link Converter}s in a {@link ElasticsearchCustomConversions} object if required.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
@Bean
|
||||
public ElasticsearchCustomConversions elasticsearchCustomConversions() {
|
||||
return new ElasticsearchCustomConversions(Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the base packages to scan for Elasticsearch mapped entities at startup. Will return the package name of the
|
||||
* configuration class' (the concrete class, not this one here) by default. So if you have a
|
||||
* {@code com.acme.AppConfig} extending {@link ElasticsearchConfigurationSupport} the base package will be considered
|
||||
* {@code com.acme} unless the method is overridden to implement alternate behavior.
|
||||
*
|
||||
* @return the base packages to scan for mapped {@link Document} classes or an empty collection to not enable scanning
|
||||
* for entities.
|
||||
*/
|
||||
protected Collection<String> getMappingBasePackages() {
|
||||
|
||||
Package mappingBasePackage = getClass().getPackage();
|
||||
return Collections.singleton(mappingBasePackage == null ? null : mappingBasePackage.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans the mapping base package for classes annotated with {@link Document}. By default, it scans for entities in
|
||||
* all packages returned by {@link #getMappingBasePackages()}.
|
||||
*
|
||||
* @see #getMappingBasePackages()
|
||||
* @return never {@literal null}.
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
protected Set<Class<?>> getInitialEntitySet() throws ClassNotFoundException {
|
||||
|
||||
Set<Class<?>> initialEntitySet = new HashSet<>();
|
||||
|
||||
for (String basePackage : getMappingBasePackages()) {
|
||||
initialEntitySet.addAll(scanForEntities(basePackage));
|
||||
}
|
||||
|
||||
return initialEntitySet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans the given base package for entities, i.e. Elasticsearch specific types annotated with {@link Document} and
|
||||
* {@link Persistent}.
|
||||
*
|
||||
* @param basePackage must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
protected Set<Class<?>> scanForEntities(String basePackage) throws ClassNotFoundException {
|
||||
|
||||
if (!StringUtils.hasText(basePackage)) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
Set<Class<?>> initialEntitySet = new HashSet<>();
|
||||
|
||||
if (StringUtils.hasText(basePackage)) {
|
||||
|
||||
ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(
|
||||
false);
|
||||
componentProvider.addIncludeFilter(new AnnotationTypeFilter(Document.class));
|
||||
componentProvider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class));
|
||||
|
||||
for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) {
|
||||
|
||||
initialEntitySet.add(ClassUtils.forName(candidate.getBeanClassName(),
|
||||
AbstractReactiveElasticsearchConfiguration.class.getClassLoader()));
|
||||
}
|
||||
}
|
||||
|
||||
return initialEntitySet;
|
||||
}
|
||||
}
|
||||
+41
-42
@@ -1,42 +1,41 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.xml.NamespaceHandlerSupport;
|
||||
import org.springframework.data.elasticsearch.repository.config.ElasticsearchRepositoryConfigExtension;
|
||||
import org.springframework.data.repository.config.RepositoryBeanDefinitionParser;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationExtension;
|
||||
|
||||
/**
|
||||
* ElasticsearchNamespaceHandler
|
||||
*
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Don Wellington
|
||||
*/
|
||||
public class ElasticsearchNamespaceHandler extends NamespaceHandlerSupport {
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
RepositoryConfigurationExtension extension = new ElasticsearchRepositoryConfigExtension();
|
||||
RepositoryBeanDefinitionParser parser = new RepositoryBeanDefinitionParser(extension);
|
||||
|
||||
registerBeanDefinitionParser("repositories", parser);
|
||||
registerBeanDefinitionParser("node-client", new NodeClientBeanDefinitionParser());
|
||||
registerBeanDefinitionParser("transport-client", new TransportClientBeanDefinitionParser());
|
||||
registerBeanDefinitionParser("rest-client", new RestClientBeanDefinitionParser());
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright 2013 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
|
||||
*
|
||||
* 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 "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.xml.NamespaceHandlerSupport;
|
||||
import org.springframework.data.elasticsearch.repository.config.ElasticsearchRepositoryConfigExtension;
|
||||
import org.springframework.data.repository.config.RepositoryBeanDefinitionParser;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationExtension;
|
||||
|
||||
/**
|
||||
* ElasticsearchNamespaceHandler
|
||||
*
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
*/
|
||||
|
||||
public class ElasticsearchNamespaceHandler extends NamespaceHandlerSupport {
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
RepositoryConfigurationExtension extension = new ElasticsearchRepositoryConfigExtension();
|
||||
RepositoryBeanDefinitionParser parser = new RepositoryBeanDefinitionParser(extension);
|
||||
|
||||
registerBeanDefinitionParser("repositories", parser);
|
||||
registerBeanDefinitionParser("node-client", new NodeClientBeanDefinitionParser());
|
||||
registerBeanDefinitionParser("transport-client", new TransportClientBeanDefinitionParser());
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2015-2020 the original author or authors.
|
||||
* Copyright 2015 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
|
||||
* 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 "AS IS" BASIS,
|
||||
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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.xml.AbstractBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.data.elasticsearch.client.RestClientFactoryBean;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* @author Don Wellington
|
||||
*/
|
||||
public class RestClientBeanDefinitionParser extends AbstractBeanDefinitionParser {
|
||||
|
||||
@Override
|
||||
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(RestClientFactoryBean.class);
|
||||
setConfigurations(element, builder);
|
||||
return getSourcedBeanDefinition(builder, element, parserContext);
|
||||
}
|
||||
|
||||
private void setConfigurations(Element element, BeanDefinitionBuilder builder) {
|
||||
builder.addPropertyValue("hosts", element.getAttribute("hosts"));
|
||||
}
|
||||
|
||||
private AbstractBeanDefinition getSourcedBeanDefinition(BeanDefinitionBuilder builder, Element source,
|
||||
ParserContext context) {
|
||||
AbstractBeanDefinition definition = builder.getBeanDefinition();
|
||||
definition.setSource(context.extractSource(source));
|
||||
return definition;
|
||||
}
|
||||
}
|
||||
+56
-57
@@ -1,57 +1,56 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.TransportClientFactoryBean;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* TransportClientBeanDefinitionParser
|
||||
*
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @deprecated as of 4.0
|
||||
*/
|
||||
@Deprecated
|
||||
public class TransportClientBeanDefinitionParser extends AbstractBeanDefinitionParser {
|
||||
|
||||
@Override
|
||||
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(TransportClientFactoryBean.class);
|
||||
setConfigurations(element, builder);
|
||||
return getSourcedBeanDefinition(builder, element, parserContext);
|
||||
}
|
||||
|
||||
private void setConfigurations(Element element, BeanDefinitionBuilder builder) {
|
||||
builder.addPropertyValue("clusterNodes", element.getAttribute("cluster-nodes"));
|
||||
builder.addPropertyValue("clusterName", element.getAttribute("cluster-name"));
|
||||
builder.addPropertyValue("clientTransportSniff", Boolean.valueOf(element.getAttribute("client-transport-sniff")));
|
||||
builder.addPropertyValue("clientIgnoreClusterName", Boolean.valueOf(element.getAttribute("client-transport-ignore-cluster-name")));
|
||||
builder.addPropertyValue("clientPingTimeout", element.getAttribute("client-transport-ping-timeout"));
|
||||
builder.addPropertyValue("clientNodesSamplerInterval", element.getAttribute("client-transport-nodes-sampler-interval"));
|
||||
}
|
||||
|
||||
private AbstractBeanDefinition getSourcedBeanDefinition(BeanDefinitionBuilder builder, Element source,
|
||||
ParserContext context) {
|
||||
AbstractBeanDefinition definition = builder.getBeanDefinition();
|
||||
definition.setSource(context.extractSource(source));
|
||||
return definition;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright 2013 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
|
||||
*
|
||||
* 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 "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.TransportClientFactoryBean;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* TransportClientBeanDefinitionParser
|
||||
*
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
*/
|
||||
|
||||
public class TransportClientBeanDefinitionParser extends AbstractBeanDefinitionParser {
|
||||
|
||||
@Override
|
||||
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(TransportClientFactoryBean.class);
|
||||
setConfigurations(element, builder);
|
||||
return getSourcedBeanDefinition(builder, element, parserContext);
|
||||
}
|
||||
|
||||
private void setConfigurations(Element element, BeanDefinitionBuilder builder) {
|
||||
builder.addPropertyValue("clusterNodes", element.getAttribute("cluster-nodes"));
|
||||
builder.addPropertyValue("clusterName", element.getAttribute("cluster-name"));
|
||||
builder.addPropertyValue("clientTransportSniff", Boolean.valueOf(element.getAttribute("client-transport-sniff")));
|
||||
builder.addPropertyValue("clientIgnoreClusterName", Boolean.valueOf(element.getAttribute("client-transport-ignore-cluster-name")));
|
||||
builder.addPropertyValue("clientPingTimeout", element.getAttribute("client-transport-ping-timeout"));
|
||||
builder.addPropertyValue("clientNodesSamplerInterval", element.getAttribute("client-transport-nodes-sampler-interval"));
|
||||
}
|
||||
|
||||
private AbstractBeanDefinition getSourcedBeanDefinition(BeanDefinitionBuilder builder, Element source,
|
||||
ParserContext context) {
|
||||
AbstractBeanDefinition definition = builder.getBeanDefinition();
|
||||
definition.setSource(context.extractSource(source));
|
||||
return definition;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
@org.springframework.lang.NonNullApi
|
||||
@org.springframework.lang.NonNullFields
|
||||
package org.springframework.data.elasticsearch.config;
|
||||
-267
@@ -1,267 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2020 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 static org.springframework.util.StringUtils.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
|
||||
import org.elasticsearch.cluster.metadata.AliasMetaData;
|
||||
import org.elasticsearch.common.collect.MapBuilder;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.elasticsearch.ElasticsearchException;
|
||||
import org.springframework.data.elasticsearch.annotations.Mapping;
|
||||
import org.springframework.data.elasticsearch.annotations.Setting;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.document.Document;
|
||||
import org.springframework.data.elasticsearch.core.index.MappingBuilder;
|
||||
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.StringUtils;
|
||||
|
||||
/**
|
||||
* Base implementation of {@link IndexOperations} common to Transport and Rest based Implementations of IndexOperations.
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Sascha Woo
|
||||
* @since 4.0
|
||||
*/
|
||||
abstract class AbstractDefaultIndexOperations implements IndexOperations {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractDefaultIndexOperations.class);
|
||||
|
||||
protected final ElasticsearchConverter elasticsearchConverter;
|
||||
protected final RequestFactory requestFactory;
|
||||
|
||||
@Nullable protected final Class<?> boundClass;
|
||||
protected final IndexCoordinates boundIndex;
|
||||
|
||||
public AbstractDefaultIndexOperations(ElasticsearchConverter elasticsearchConverter, Class<?> boundClass) {
|
||||
this.elasticsearchConverter = elasticsearchConverter;
|
||||
requestFactory = new RequestFactory(elasticsearchConverter);
|
||||
|
||||
this.boundClass = boundClass;
|
||||
this.boundIndex = getIndexCoordinatesFor(boundClass);
|
||||
}
|
||||
|
||||
public AbstractDefaultIndexOperations(ElasticsearchConverter elasticsearchConverter, IndexCoordinates boundIndex) {
|
||||
this.elasticsearchConverter = elasticsearchConverter;
|
||||
requestFactory = new RequestFactory(elasticsearchConverter);
|
||||
|
||||
this.boundClass = null;
|
||||
this.boundIndex = boundIndex;
|
||||
}
|
||||
|
||||
protected Class<?> checkForBoundClass() {
|
||||
if (boundClass == null) {
|
||||
throw new InvalidDataAccessApiUsageException("IndexOperations are not bound");
|
||||
}
|
||||
return boundClass;
|
||||
}
|
||||
|
||||
// region IndexOperations
|
||||
|
||||
@Override
|
||||
public boolean create() {
|
||||
|
||||
if (boundClass != null) {
|
||||
Class<?> clazz = boundClass;
|
||||
String indexName = boundIndex.getIndexName();
|
||||
|
||||
if (clazz.isAnnotationPresent(Setting.class)) {
|
||||
String settingPath = clazz.getAnnotation(Setting.class).settingPath();
|
||||
|
||||
if (hasText(settingPath)) {
|
||||
String settings = ResourceUtil.readFileFromClasspath(settingPath);
|
||||
|
||||
if (hasText(settings)) {
|
||||
return doCreate(indexName, Document.parse(settings));
|
||||
}
|
||||
} else {
|
||||
LOGGER.info("settingPath in @Setting has to be defined. Using default instead.");
|
||||
}
|
||||
}
|
||||
return doCreate(indexName, getDefaultSettings(getRequiredPersistentEntity(clazz)));
|
||||
}
|
||||
return doCreate(boundIndex.getIndexName(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean create(Document settings) {
|
||||
return doCreate(boundIndex.getIndexName(), settings);
|
||||
}
|
||||
|
||||
protected abstract boolean doCreate(String indexName, @Nullable Document settings);
|
||||
|
||||
@Override
|
||||
public boolean delete() {
|
||||
return doDelete(boundIndex.getIndexName());
|
||||
}
|
||||
|
||||
protected abstract boolean doDelete(String indexName);
|
||||
|
||||
@Override
|
||||
public boolean exists() {
|
||||
return doExists(boundIndex.getIndexName());
|
||||
}
|
||||
|
||||
protected abstract boolean doExists(String indexName);
|
||||
|
||||
@Override
|
||||
public boolean putMapping(Document mapping) {
|
||||
return doPutMapping(boundIndex, mapping);
|
||||
}
|
||||
|
||||
protected abstract boolean doPutMapping(IndexCoordinates index, Document mapping);
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getMapping() {
|
||||
return doGetMapping(boundIndex);
|
||||
}
|
||||
|
||||
abstract protected Map<String, Object> doGetMapping(IndexCoordinates index);
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getSettings() {
|
||||
return getSettings(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getSettings(boolean includeDefaults) {
|
||||
return doGetSettings(boundIndex.getIndexName(), includeDefaults);
|
||||
}
|
||||
|
||||
protected abstract Map<String, Object> doGetSettings(String indexName, boolean includeDefaults);
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
doRefresh(boundIndex);
|
||||
}
|
||||
|
||||
protected abstract void doRefresh(IndexCoordinates indexCoordinates);
|
||||
|
||||
@Override
|
||||
public boolean addAlias(AliasQuery query) {
|
||||
return doAddAlias(query, boundIndex);
|
||||
}
|
||||
|
||||
protected abstract boolean doAddAlias(AliasQuery query, IndexCoordinates index);
|
||||
|
||||
@Override
|
||||
public List<AliasMetaData> queryForAlias() {
|
||||
return doQueryForAlias(boundIndex.getIndexName());
|
||||
}
|
||||
|
||||
protected abstract List<AliasMetaData> doQueryForAlias(String indexName);
|
||||
|
||||
@Override
|
||||
public boolean removeAlias(AliasQuery query) {
|
||||
return doRemoveAlias(query, boundIndex);
|
||||
}
|
||||
|
||||
protected abstract boolean doRemoveAlias(AliasQuery query, IndexCoordinates index);
|
||||
|
||||
@Override
|
||||
public Document createMapping() {
|
||||
return createMapping(checkForBoundClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Document createMapping(Class<?> clazz) {
|
||||
return buildMapping(clazz);
|
||||
}
|
||||
|
||||
protected Document buildMapping(Class<?> clazz) {
|
||||
|
||||
// load mapping specified in Mapping annotation if present
|
||||
if (clazz.isAnnotationPresent(Mapping.class)) {
|
||||
String mappingPath = clazz.getAnnotation(Mapping.class).mappingPath();
|
||||
|
||||
if (!StringUtils.isEmpty(mappingPath)) {
|
||||
String mappings = ResourceUtil.readFileFromClasspath(mappingPath);
|
||||
|
||||
if (!StringUtils.isEmpty(mappings)) {
|
||||
return Document.parse(mappings);
|
||||
}
|
||||
} else {
|
||||
LOGGER.info("mappingPath in @Mapping has to be defined. Building mappings using @Field");
|
||||
}
|
||||
}
|
||||
|
||||
// build mapping from field annotations
|
||||
try {
|
||||
String mapping = new MappingBuilder(elasticsearchConverter).buildPropertyMapping(clazz);
|
||||
return Document.parse(mapping);
|
||||
} catch (Exception e) {
|
||||
throw new ElasticsearchException("Failed to build mapping for " + clazz.getSimpleName(), e);
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Helper functions
|
||||
private <T> Document getDefaultSettings(ElasticsearchPersistentEntity<T> persistentEntity) {
|
||||
|
||||
if (persistentEntity.isUseServerConfiguration()) {
|
||||
return Document.create();
|
||||
}
|
||||
|
||||
Map<String, String> map = new MapBuilder<String, String>()
|
||||
.put("index.number_of_shards", String.valueOf(persistentEntity.getShards()))
|
||||
.put("index.number_of_replicas", String.valueOf(persistentEntity.getReplicas()))
|
||||
.put("index.refresh_interval", persistentEntity.getRefreshInterval())
|
||||
.put("index.store.type", persistentEntity.getIndexStoreType()).map();
|
||||
return Document.from(map);
|
||||
}
|
||||
|
||||
ElasticsearchPersistentEntity<?> getRequiredPersistentEntity(Class<?> clazz) {
|
||||
return elasticsearchConverter.getMappingContext().getRequiredPersistentEntity(clazz);
|
||||
}
|
||||
|
||||
public IndexCoordinates getIndexCoordinatesFor(Class<?> clazz) {
|
||||
return getRequiredPersistentEntity(clazz).getIndexCoordinates();
|
||||
}
|
||||
|
||||
protected Map<String, Object> convertSettingsResponseToMap(GetSettingsResponse response, String indexName) {
|
||||
|
||||
Map<String, Object> settings = new HashMap<>();
|
||||
|
||||
if (!response.getIndexToDefaultSettings().isEmpty()) {
|
||||
Settings defaultSettings = response.getIndexToDefaultSettings().get(indexName);
|
||||
for (String key : defaultSettings.keySet()) {
|
||||
settings.put(key, defaultSettings.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
if (!response.getIndexToSettings().isEmpty()) {
|
||||
Settings customSettings = response.getIndexToSettings().get(indexName);
|
||||
for (String key : customSettings.keySet()) {
|
||||
settings.put(key, customSettings.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
// endregion
|
||||
|
||||
}
|
||||
-393
@@ -1,393 +0,0 @@
|
||||
package org.springframework.data.elasticsearch.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
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.common.unit.TimeValue;
|
||||
import org.elasticsearch.index.query.MoreLikeThisQueryBuilder;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.data.elasticsearch.ElasticsearchException;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;
|
||||
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.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
|
||||
import org.springframework.data.elasticsearch.core.query.GetQuery;
|
||||
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.util.CloseableIterator;
|
||||
import org.springframework.data.util.Streamable;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* AbstractElasticsearchTemplate
|
||||
*
|
||||
* @author Sascha Woo
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
public abstract class AbstractElasticsearchTemplate implements ElasticsearchOperations, ApplicationContextAware {
|
||||
|
||||
protected @Nullable ElasticsearchConverter elasticsearchConverter;
|
||||
protected @Nullable RequestFactory requestFactory;
|
||||
|
||||
// region Initialization
|
||||
protected void initialize(ElasticsearchConverter elasticsearchConverter) {
|
||||
|
||||
Assert.notNull(elasticsearchConverter, "elasticsearchConverter must not be null.");
|
||||
|
||||
this.elasticsearchConverter = elasticsearchConverter;
|
||||
requestFactory = new RequestFactory(elasticsearchConverter);
|
||||
}
|
||||
|
||||
protected ElasticsearchConverter createElasticsearchConverter() {
|
||||
MappingElasticsearchConverter mappingElasticsearchConverter = new MappingElasticsearchConverter(
|
||||
new SimpleElasticsearchMappingContext());
|
||||
mappingElasticsearchConverter.afterPropertiesSet();
|
||||
return mappingElasticsearchConverter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext context) throws BeansException {
|
||||
|
||||
if (elasticsearchConverter instanceof ApplicationContextAware) {
|
||||
((ApplicationContextAware) elasticsearchConverter).setApplicationContext(context);
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region DocumentOperations
|
||||
|
||||
@Override
|
||||
public <T> T save(T entity) {
|
||||
|
||||
Assert.notNull(entity, "entity must not be null");
|
||||
|
||||
return save(entity, getIndexCoordinatesFor(entity.getClass()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T save(T entity, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(entity, "entity must not be null");
|
||||
Assert.notNull(index, "index must not be null");
|
||||
|
||||
index(getIndexQuery(entity), index);
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Iterable<T> save(Iterable<T> entities) {
|
||||
|
||||
Assert.notNull(entities, "entities must not be null");
|
||||
|
||||
Iterator<T> iterator = entities.iterator();
|
||||
if (iterator.hasNext()) {
|
||||
return save(entities, getIndexCoordinatesFor(iterator.next().getClass()));
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Iterable<T> save(Iterable<T> entities, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(entities, "entities must not be null");
|
||||
Assert.notNull(index, "index must not be null");
|
||||
|
||||
List<IndexQuery> indexQueries = Streamable.of(entities).stream().map(this::getIndexQuery)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (!indexQueries.isEmpty()) {
|
||||
List<String> ids = bulkIndex(indexQueries, index);
|
||||
Iterator<String> idIterator = ids.iterator();
|
||||
entities.forEach(entity -> {
|
||||
setPersistentEntityId(entity, idIterator.next());
|
||||
});
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Iterable<T> save(T... entities) {
|
||||
return save(Arrays.asList(entities));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public <T> T get(String id, Class<T> clazz) {
|
||||
return get(id, clazz, getIndexCoordinatesFor(clazz));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public <T> T get(GetQuery query, Class<T> clazz, IndexCoordinates index) {
|
||||
return get(query.getId(), clazz, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(String id, Class<?> clazz) {
|
||||
return exists(id, getIndexCoordinatesFor(clazz));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(String id, IndexCoordinates index) {
|
||||
return doExists(id, index);
|
||||
}
|
||||
|
||||
abstract protected boolean doExists(String id, IndexCoordinates index);
|
||||
|
||||
@Override
|
||||
public String delete(String id, Class<?> entityType) {
|
||||
|
||||
Assert.notNull(id, "id must not be null");
|
||||
Assert.notNull(entityType, "entityType must not be null");
|
||||
|
||||
return this.delete(id, getIndexCoordinatesFor(entityType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String delete(Object entity) {
|
||||
return delete(entity, getIndexCoordinatesFor(entity.getClass()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String delete(Object entity, IndexCoordinates index) {
|
||||
return this.delete(getEntityId(entity), index);
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region SearchOperations
|
||||
@Override
|
||||
public long count(Query query, Class<?> clazz) {
|
||||
return count(query, clazz, getIndexCoordinatesFor(clazz));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> CloseableIterator<T> stream(Query query, Class<T> clazz, IndexCoordinates index) {
|
||||
long scrollTimeInMillis = TimeValue.timeValueMinutes(1).millis();
|
||||
return (CloseableIterator<T>) SearchHitSupport.unwrapSearchHits(searchForStream(query, clazz, index));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> CloseableIterator<SearchHit<T>> searchForStream(Query query, Class<T> clazz) {
|
||||
return searchForStream(query, clazz, getIndexCoordinatesFor(clazz));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> CloseableIterator<SearchHit<T>> searchForStream(Query query, Class<T> clazz, IndexCoordinates index) {
|
||||
long scrollTimeInMillis = TimeValue.timeValueMinutes(1).millis();
|
||||
return StreamQueries.streamResults(searchScrollStart(scrollTimeInMillis, query, clazz, index),
|
||||
scrollId -> searchScrollContinue(scrollId, scrollTimeInMillis, clazz), this::searchScrollClear);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> SearchHits<T> search(MoreLikeThisQuery query, Class<T> clazz) {
|
||||
return search(query, clazz, getIndexCoordinatesFor(clazz));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> SearchHits<T> search(MoreLikeThisQuery query, Class<T> clazz, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(query.getId(), "No document id defined for MoreLikeThisQuery");
|
||||
|
||||
MoreLikeThisQueryBuilder moreLikeThisQueryBuilder = requestFactory.moreLikeThisQueryBuilder(query, index);
|
||||
return search(new NativeSearchQueryBuilder().withQuery(moreLikeThisQueryBuilder).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);
|
||||
|
||||
List<SearchHits<T>> res = new ArrayList<>(queries.size());
|
||||
int c = 0;
|
||||
for (Query query : queries) {
|
||||
res.add(elasticsearchConverter.read(clazz, SearchDocumentResponse.from(items[c++].getResponse())));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SearchHits<?>> multiSearch(List<? extends Query> queries, List<Class<?>> classes,
|
||||
IndexCoordinates index) {
|
||||
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) {
|
||||
res.add(elasticsearchConverter.read(it1.next(), SearchDocumentResponse.from(items[c++].getResponse())));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> SearchHits<T> search(Query query, Class<T> clazz) {
|
||||
return search(query, clazz, getIndexCoordinatesFor(clazz));
|
||||
}
|
||||
|
||||
/*
|
||||
* internal use only, not for public API
|
||||
*/
|
||||
abstract protected <T> ScrolledPage<SearchHit<T>> searchScrollStart(long scrollTimeInMillis, Query query,
|
||||
Class<T> clazz, IndexCoordinates index);
|
||||
|
||||
/*
|
||||
* internal use only, not for public API
|
||||
*/
|
||||
abstract protected <T> ScrolledPage<SearchHit<T>> searchScrollContinue(@Nullable String scrollId,
|
||||
long scrollTimeInMillis, Class<T> clazz);
|
||||
|
||||
/*
|
||||
* internal use only, not for public API
|
||||
*/
|
||||
abstract protected void searchScrollClear(String scrollId);
|
||||
|
||||
abstract protected MultiSearchResponse.Item[] getMultiSearchResult(MultiSearchRequest request);
|
||||
// endregion
|
||||
|
||||
// region Helper methods
|
||||
@Override
|
||||
public ElasticsearchConverter getElasticsearchConverter() {
|
||||
|
||||
Assert.notNull(elasticsearchConverter, "elasticsearchConverter is not initialized.");
|
||||
|
||||
return elasticsearchConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 4.0
|
||||
*/
|
||||
public RequestFactory getRequestFactory() {
|
||||
|
||||
Assert.notNull(requestFactory, "requestfactory not initialized");
|
||||
|
||||
return requestFactory;
|
||||
}
|
||||
|
||||
protected static String[] toArray(List<String> values) {
|
||||
String[] valuesAsArray = new String[values.size()];
|
||||
return values.toArray(valuesAsArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clazz the entity class
|
||||
* @return the IndexCoordinates defined on the entity.
|
||||
* @since 4.0
|
||||
*/
|
||||
@Override
|
||||
public IndexCoordinates getIndexCoordinatesFor(Class<?> clazz) {
|
||||
return getRequiredPersistentEntity(clazz).getIndexCoordinates();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bulkResponse
|
||||
* @return the list of the item id's
|
||||
*/
|
||||
protected List<String> 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 ElasticsearchException(
|
||||
"Bulk operation has failures. Use ElasticsearchException.getFailedDocuments() for detailed messages ["
|
||||
+ failedDocuments + ']',
|
||||
failedDocuments);
|
||||
}
|
||||
|
||||
return Stream.of(bulkResponse.getItems()).map(BulkItemResponse::getId).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
protected void setPersistentEntityId(Object entity, String id) {
|
||||
|
||||
ElasticsearchPersistentEntity<?> persistentEntity = getRequiredPersistentEntity(entity.getClass());
|
||||
ElasticsearchPersistentProperty idProperty = persistentEntity.getIdProperty();
|
||||
|
||||
// Only deal with text because ES generated Ids are strings!
|
||||
if (idProperty != null && idProperty.getType().isAssignableFrom(String.class)) {
|
||||
persistentEntity.getPropertyAccessor(entity).setProperty(idProperty, id);
|
||||
}
|
||||
}
|
||||
|
||||
ElasticsearchPersistentEntity<?> getRequiredPersistentEntity(Class<?> clazz) {
|
||||
return elasticsearchConverter.getMappingContext().getRequiredPersistentEntity(clazz);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getEntityId(Object entity) {
|
||||
ElasticsearchPersistentEntity<?> persistentEntity = getRequiredPersistentEntity(entity.getClass());
|
||||
ElasticsearchPersistentProperty idProperty = persistentEntity.getIdProperty();
|
||||
|
||||
if (idProperty != null) {
|
||||
return stringIdRepresentation(persistentEntity.getPropertyAccessor(entity).getProperty(idProperty));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Long getEntityVersion(Object entity) {
|
||||
ElasticsearchPersistentEntity<?> persistentEntity = getRequiredPersistentEntity(entity.getClass());
|
||||
ElasticsearchPersistentProperty versionProperty = persistentEntity.getVersionProperty();
|
||||
|
||||
if (versionProperty != null) {
|
||||
Object version = persistentEntity.getPropertyAccessor(entity).getProperty(versionProperty);
|
||||
|
||||
if (version != null && Long.class.isAssignableFrom(version.getClass())) {
|
||||
return ((Long) version);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private <T> IndexQuery getIndexQuery(T entity) {
|
||||
String id = getEntityId(entity);
|
||||
|
||||
if (id != null) {
|
||||
id = elasticsearchConverter.convertId(id);
|
||||
}
|
||||
|
||||
return new IndexQueryBuilder() //
|
||||
.withId(id) //
|
||||
.withVersion(getEntityVersion(entity)) //
|
||||
.withObject(entity) //
|
||||
.build();
|
||||
}
|
||||
|
||||
// endregion
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2014 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
|
||||
*
|
||||
* 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 "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 static org.apache.commons.lang.StringUtils.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.data.elasticsearch.ElasticsearchException;
|
||||
|
||||
/**
|
||||
* @author Artur Konczak
|
||||
*/
|
||||
public abstract class AbstractResultMapper implements ResultsMapper {
|
||||
|
||||
private EntityMapper entityMapper;
|
||||
|
||||
public AbstractResultMapper(EntityMapper entityMapper) {
|
||||
this.entityMapper = entityMapper;
|
||||
}
|
||||
|
||||
public <T> T mapEntity(String source, Class<T> clazz) {
|
||||
if (isBlank(source)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return entityMapper.mapToObject(source, clazz);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("failed to map source [ " + source + "] to class " + clazz.getSimpleName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityMapper getEntityMapper() {
|
||||
return this.entityMapper;
|
||||
}
|
||||
}
|
||||
+34
-45
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
* Copyright 2013-2017 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
|
||||
* 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 "AS IS" BASIS,
|
||||
@@ -20,13 +20,10 @@ import static org.springframework.data.elasticsearch.core.query.Criteria.*;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
import org.elasticsearch.common.geo.GeoDistance;
|
||||
import org.elasticsearch.index.query.BoolQueryBuilder;
|
||||
import org.elasticsearch.index.query.GeoBoundingBoxQueryBuilder;
|
||||
import org.elasticsearch.index.query.GeoDistanceQueryBuilder;
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.index.query.*;
|
||||
import org.springframework.data.elasticsearch.core.geo.GeoBox;
|
||||
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
|
||||
import org.springframework.data.elasticsearch.core.query.Criteria;
|
||||
@@ -34,7 +31,6 @@ import org.springframework.data.geo.Box;
|
||||
import org.springframework.data.geo.Distance;
|
||||
import org.springframework.data.geo.Metrics;
|
||||
import org.springframework.data.geo.Point;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -43,25 +39,28 @@ import org.springframework.util.Assert;
|
||||
* @author Franck Marchand
|
||||
* @author Mohsin Husen
|
||||
* @author Artur Konczak
|
||||
* @author Peter-Josef Meisch
|
||||
*
|
||||
*/
|
||||
class CriteriaFilterProcessor {
|
||||
|
||||
|
||||
QueryBuilder createFilterFromCriteria(Criteria criteria) {
|
||||
List<QueryBuilder> fbList = new LinkedList<>();
|
||||
QueryBuilder filter = null;
|
||||
|
||||
for (Criteria chainedCriteria : criteria.getCriteriaChain()) {
|
||||
ListIterator<Criteria> chainIterator = criteria.getCriteriaChain().listIterator();
|
||||
|
||||
while (chainIterator.hasNext()) {
|
||||
QueryBuilder fb = null;
|
||||
Criteria chainedCriteria = chainIterator.next();
|
||||
if (chainedCriteria.isOr()) {
|
||||
fb = QueryBuilders.boolQuery();
|
||||
for (QueryBuilder f : createFilterFragmentForCriteria(chainedCriteria)) {
|
||||
((BoolQueryBuilder) fb).should(f);
|
||||
for(QueryBuilder f: createFilterFragmentForCriteria(chainedCriteria)){
|
||||
((BoolQueryBuilder)fb).should(f);
|
||||
}
|
||||
fbList.add(fb);
|
||||
} else if (chainedCriteria.isNegating()) {
|
||||
List<QueryBuilder> negationFilters = buildNegationFilter(criteria.getField().getName(),
|
||||
criteria.getFilterCriteriaEntries().iterator());
|
||||
List<QueryBuilder> negationFilters = buildNegationFilter(criteria.getField().getName(), criteria.getFilterCriteriaEntries().iterator());
|
||||
|
||||
if (!negationFilters.isEmpty()) {
|
||||
fbList.addAll(negationFilters);
|
||||
@@ -76,14 +75,15 @@ class CriteriaFilterProcessor {
|
||||
filter = fbList.get(0);
|
||||
} else {
|
||||
filter = QueryBuilders.boolQuery();
|
||||
for (QueryBuilder f : fbList) {
|
||||
((BoolQueryBuilder) filter).must(f);
|
||||
for(QueryBuilder f: fbList) {
|
||||
((BoolQueryBuilder)filter).must(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
|
||||
|
||||
private List<QueryBuilder> createFilterFragmentForCriteria(Criteria chainedCriteria) {
|
||||
Iterator<Criteria.CriteriaEntry> it = chainedCriteria.getFilterCriteriaEntries().iterator();
|
||||
List<QueryBuilder> filterList = new LinkedList<>();
|
||||
@@ -101,9 +101,8 @@ class CriteriaFilterProcessor {
|
||||
return filterList;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private QueryBuilder processCriteriaEntry(OperationKey key, Object value, String fieldName) {
|
||||
|
||||
private QueryBuilder processCriteriaEntry(OperationKey key, Object value, String fieldName) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -117,10 +116,8 @@ class CriteriaFilterProcessor {
|
||||
Object[] valArray = (Object[]) value;
|
||||
Assert.noNullElements(valArray, "Geo distance filter takes 2 not null elements array as parameter.");
|
||||
Assert.isTrue(valArray.length == 2, "Geo distance filter takes a 2-elements array as parameter.");
|
||||
Assert.isTrue(valArray[0] instanceof GeoPoint || valArray[0] instanceof String || valArray[0] instanceof Point,
|
||||
"First element of a geo distance filter must be a GeoPoint, a Point or a text");
|
||||
Assert.isTrue(valArray[1] instanceof String || valArray[1] instanceof Distance,
|
||||
"Second element of a geo distance filter must be a text or a Distance");
|
||||
Assert.isTrue(valArray[0] instanceof GeoPoint || valArray[0] instanceof String || valArray[0] instanceof Point, "First element of a geo distance filter must be a GeoPoint, a Point or a text");
|
||||
Assert.isTrue(valArray[1] instanceof String || valArray[1] instanceof Distance, "Second element of a geo distance filter must be a text or a Distance");
|
||||
|
||||
StringBuilder dist = new StringBuilder();
|
||||
|
||||
@@ -132,18 +129,15 @@ class CriteriaFilterProcessor {
|
||||
|
||||
if (valArray[0] instanceof GeoPoint) {
|
||||
GeoPoint loc = (GeoPoint) valArray[0];
|
||||
geoDistanceQueryBuilder.point(loc.getLat(), loc.getLon()).distance(dist.toString())
|
||||
.geoDistance(GeoDistance.PLANE);
|
||||
geoDistanceQueryBuilder.point(loc.getLat(),loc.getLon()).distance(dist.toString()).geoDistance(GeoDistance.PLANE);
|
||||
} else if (valArray[0] instanceof Point) {
|
||||
GeoPoint loc = GeoPoint.fromPoint((Point) valArray[0]);
|
||||
geoDistanceQueryBuilder.point(loc.getLat(), loc.getLon()).distance(dist.toString())
|
||||
.geoDistance(GeoDistance.PLANE);
|
||||
geoDistanceQueryBuilder.point(loc.getLat(), loc.getLon()).distance(dist.toString()).geoDistance(GeoDistance.PLANE);
|
||||
} else {
|
||||
String loc = (String) valArray[0];
|
||||
if (loc.contains(",")) {
|
||||
String[] c = loc.split(",");
|
||||
geoDistanceQueryBuilder.point(Double.parseDouble(c[0]), Double.parseDouble(c[1])).distance(dist.toString())
|
||||
.geoDistance(GeoDistance.PLANE);
|
||||
String c[] = loc.split(",");
|
||||
geoDistanceQueryBuilder.point(Double.parseDouble(c[0]), Double.parseDouble(c[1])).distance(dist.toString()).geoDistance(GeoDistance.PLANE);
|
||||
} else {
|
||||
geoDistanceQueryBuilder.geohash(loc).distance(dist.toString()).geoDistance(GeoDistance.PLANE);
|
||||
}
|
||||
@@ -156,22 +150,20 @@ class CriteriaFilterProcessor {
|
||||
case BBOX: {
|
||||
filter = QueryBuilders.geoBoundingBoxQuery(fieldName);
|
||||
|
||||
Assert.isTrue(value instanceof Object[],
|
||||
"Value of a boundedBy filter should be an array of one or two values.");
|
||||
Assert.isTrue(value instanceof Object[], "Value of a boundedBy filter should be an array of one or two values.");
|
||||
Object[] valArray = (Object[]) value;
|
||||
Assert.noNullElements(valArray, "Geo boundedBy filter takes a not null element array as parameter.");
|
||||
|
||||
if (valArray.length == 1) {
|
||||
// GeoEnvelop
|
||||
//GeoEnvelop
|
||||
oneParameterBBox((GeoBoundingBoxQueryBuilder) filter, valArray[0]);
|
||||
} else if (valArray.length == 2) {
|
||||
// 2x GeoPoint
|
||||
// 2x text
|
||||
//2x GeoPoint
|
||||
//2x text
|
||||
twoParameterBBox((GeoBoundingBoxQueryBuilder) filter, valArray);
|
||||
} else {
|
||||
// error
|
||||
Assert.isTrue(false,
|
||||
"Geo distance filter takes a 1-elements array(GeoBox) or 2-elements array(GeoPoints or Strings(format lat,lon or geohash)).");
|
||||
//error
|
||||
Assert.isTrue(false, "Geo distance filter takes a 1-elements array(GeoBox) or 2-elements array(GeoPoints or Strings(format lat,lon or geohash)).");
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -180,6 +172,7 @@ class CriteriaFilterProcessor {
|
||||
return filter;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* extract the distance string from a {@link org.springframework.data.geo.Distance} object.
|
||||
*
|
||||
@@ -203,8 +196,7 @@ class CriteriaFilterProcessor {
|
||||
}
|
||||
|
||||
private void oneParameterBBox(GeoBoundingBoxQueryBuilder filter, Object value) {
|
||||
Assert.isTrue(value instanceof GeoBox || value instanceof Box,
|
||||
"single-element of boundedBy filter must be type of GeoBox or Box");
|
||||
Assert.isTrue(value instanceof GeoBox || value instanceof Box, "single-element of boundedBy filter must be type of GeoBox or Box");
|
||||
|
||||
GeoBox geoBBox;
|
||||
if (value instanceof Box) {
|
||||
@@ -214,8 +206,7 @@ class CriteriaFilterProcessor {
|
||||
geoBBox = (GeoBox) value;
|
||||
}
|
||||
|
||||
filter.setCorners(geoBBox.getTopLeft().getLat(), geoBBox.getTopLeft().getLon(), geoBBox.getBottomRight().getLat(),
|
||||
geoBBox.getBottomRight().getLon());
|
||||
filter.setCorners(geoBBox.getTopLeft().getLat(), geoBBox.getTopLeft().getLon(), geoBBox.getBottomRight().getLat(), geoBBox.getBottomRight().getLon());
|
||||
}
|
||||
|
||||
private static boolean isType(Object[] array, Class clazz) {
|
||||
@@ -228,8 +219,7 @@ class CriteriaFilterProcessor {
|
||||
}
|
||||
|
||||
private void twoParameterBBox(GeoBoundingBoxQueryBuilder filter, Object[] values) {
|
||||
Assert.isTrue(isType(values, GeoPoint.class) || isType(values, String.class),
|
||||
" both elements of boundedBy filter must be type of GeoPoint or text(format lat,lon or geohash)");
|
||||
Assert.isTrue(isType(values, GeoPoint.class) || isType(values, String.class), " both elements of boundedBy filter must be type of GeoPoint or text(format lat,lon or geohash)");
|
||||
if (values[0] instanceof GeoPoint) {
|
||||
GeoPoint topLeft = (GeoPoint) values[0];
|
||||
GeoPoint bottomRight = (GeoPoint) values[1];
|
||||
@@ -246,8 +236,7 @@ class CriteriaFilterProcessor {
|
||||
|
||||
while (it.hasNext()) {
|
||||
Criteria.CriteriaEntry criteriaEntry = it.next();
|
||||
QueryBuilder notFilter = QueryBuilders.boolQuery()
|
||||
.mustNot(processCriteriaEntry(criteriaEntry.getKey(), criteriaEntry.getValue(), fieldName));
|
||||
QueryBuilder notFilter = QueryBuilders.boolQuery().mustNot(processCriteriaEntry(criteriaEntry.getKey(), criteriaEntry.getValue(), fieldName));
|
||||
notFilterList.add(notFilter);
|
||||
}
|
||||
|
||||
|
||||
+206
-217
@@ -1,217 +1,206 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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 static org.elasticsearch.index.query.Operator.*;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.*;
|
||||
import static org.springframework.data.elasticsearch.core.query.Criteria.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
import org.apache.lucene.queryparser.flexible.core.util.StringUtils;
|
||||
import org.apache.lucene.queryparser.flexible.standard.QueryParserUtil;
|
||||
import org.elasticsearch.index.query.BoolQueryBuilder;
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.Criteria;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* CriteriaQueryProcessor
|
||||
*
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Franck Marchand
|
||||
* @author Artur Konczak
|
||||
* @author Rasmus Faber-Espensen
|
||||
* @author James Bodkin
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
class CriteriaQueryProcessor {
|
||||
|
||||
QueryBuilder createQueryFromCriteria(Criteria criteria) {
|
||||
|
||||
Assert.notNull(criteria, "criteria must not be null");
|
||||
|
||||
List<QueryBuilder> shouldQueryBuilderList = new LinkedList<>();
|
||||
List<QueryBuilder> mustNotQueryBuilderList = new LinkedList<>();
|
||||
List<QueryBuilder> mustQueryBuilderList = new LinkedList<>();
|
||||
|
||||
ListIterator<Criteria> chainIterator = criteria.getCriteriaChain().listIterator();
|
||||
|
||||
QueryBuilder firstQuery = null;
|
||||
boolean negateFirstQuery = false;
|
||||
|
||||
while (chainIterator.hasNext()) {
|
||||
Criteria chainedCriteria = chainIterator.next();
|
||||
QueryBuilder queryFragmentForCriteria = createQueryFragmentForCriteria(chainedCriteria);
|
||||
if (queryFragmentForCriteria != null) {
|
||||
if (firstQuery == null) {
|
||||
firstQuery = queryFragmentForCriteria;
|
||||
negateFirstQuery = chainedCriteria.isNegating();
|
||||
continue;
|
||||
}
|
||||
if (chainedCriteria.isOr()) {
|
||||
shouldQueryBuilderList.add(queryFragmentForCriteria);
|
||||
} else if (chainedCriteria.isNegating()) {
|
||||
mustNotQueryBuilderList.add(queryFragmentForCriteria);
|
||||
} else {
|
||||
mustQueryBuilderList.add(queryFragmentForCriteria);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (firstQuery != null) {
|
||||
if (!shouldQueryBuilderList.isEmpty() && mustNotQueryBuilderList.isEmpty() && mustQueryBuilderList.isEmpty()) {
|
||||
shouldQueryBuilderList.add(0, firstQuery);
|
||||
} else {
|
||||
if (negateFirstQuery) {
|
||||
mustNotQueryBuilderList.add(0, firstQuery);
|
||||
} else {
|
||||
mustQueryBuilderList.add(0, firstQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BoolQueryBuilder query = null;
|
||||
|
||||
if (!shouldQueryBuilderList.isEmpty() || !mustNotQueryBuilderList.isEmpty() || !mustQueryBuilderList.isEmpty()) {
|
||||
|
||||
query = boolQuery();
|
||||
|
||||
for (QueryBuilder qb : shouldQueryBuilderList) {
|
||||
query.should(qb);
|
||||
}
|
||||
for (QueryBuilder qb : mustNotQueryBuilderList) {
|
||||
query.mustNot(qb);
|
||||
}
|
||||
for (QueryBuilder qb : mustQueryBuilderList) {
|
||||
query.must(qb);
|
||||
}
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private QueryBuilder createQueryFragmentForCriteria(Criteria chainedCriteria) {
|
||||
if (chainedCriteria.getQueryCriteriaEntries().isEmpty())
|
||||
return null;
|
||||
|
||||
Iterator<Criteria.CriteriaEntry> it = chainedCriteria.getQueryCriteriaEntries().iterator();
|
||||
boolean singeEntryCriteria = (chainedCriteria.getQueryCriteriaEntries().size() == 1);
|
||||
|
||||
String fieldName = chainedCriteria.getField().getName();
|
||||
Assert.notNull(fieldName, "Unknown field");
|
||||
QueryBuilder query = null;
|
||||
|
||||
if (singeEntryCriteria) {
|
||||
Criteria.CriteriaEntry entry = it.next();
|
||||
query = processCriteriaEntry(entry, fieldName);
|
||||
} else {
|
||||
query = boolQuery();
|
||||
while (it.hasNext()) {
|
||||
Criteria.CriteriaEntry entry = it.next();
|
||||
((BoolQueryBuilder) query).must(processCriteriaEntry(entry, fieldName));
|
||||
}
|
||||
}
|
||||
|
||||
addBoost(query, chainedCriteria.getBoost());
|
||||
return query;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private QueryBuilder processCriteriaEntry(Criteria.CriteriaEntry entry, String fieldName) {
|
||||
OperationKey key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
|
||||
if (value == null) {
|
||||
|
||||
if (key == OperationKey.EXISTS) {
|
||||
return existsQuery(fieldName);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
String searchText = QueryParserUtil.escape(value.toString());
|
||||
|
||||
QueryBuilder query = null;
|
||||
|
||||
switch (key) {
|
||||
case EQUALS:
|
||||
query = queryStringQuery(searchText).field(fieldName).defaultOperator(AND);
|
||||
break;
|
||||
case CONTAINS:
|
||||
query = queryStringQuery('*' + searchText + '*').field(fieldName).analyzeWildcard(true);
|
||||
break;
|
||||
case STARTS_WITH:
|
||||
query = queryStringQuery(searchText + '*').field(fieldName).analyzeWildcard(true);
|
||||
break;
|
||||
case ENDS_WITH:
|
||||
query = queryStringQuery('*' + searchText).field(fieldName).analyzeWildcard(true);
|
||||
break;
|
||||
case EXPRESSION:
|
||||
query = queryStringQuery(value.toString()).field(fieldName);
|
||||
break;
|
||||
case LESS_EQUAL:
|
||||
query = rangeQuery(fieldName).lte(value);
|
||||
break;
|
||||
case GREATER_EQUAL:
|
||||
query = rangeQuery(fieldName).gte(value);
|
||||
break;
|
||||
case BETWEEN:
|
||||
Object[] ranges = (Object[]) value;
|
||||
query = rangeQuery(fieldName).from(ranges[0]).to(ranges[1]);
|
||||
break;
|
||||
case LESS:
|
||||
query = rangeQuery(fieldName).lt(value);
|
||||
break;
|
||||
case GREATER:
|
||||
query = rangeQuery(fieldName).gt(value);
|
||||
break;
|
||||
case FUZZY:
|
||||
query = fuzzyQuery(fieldName, searchText);
|
||||
break;
|
||||
case IN:
|
||||
query = boolQuery().must(termsQuery(fieldName, toStringList((Iterable<Object>) value)));
|
||||
break;
|
||||
case NOT_IN:
|
||||
query = boolQuery().mustNot(termsQuery(fieldName, toStringList((Iterable<Object>) value)));
|
||||
break;
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
private static List<String> toStringList(Iterable<?> iterable) {
|
||||
List<String> list = new ArrayList<>();
|
||||
for (Object item : iterable) {
|
||||
list.add(StringUtils.toString(item));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private void addBoost(QueryBuilder query, float boost) {
|
||||
if (Float.isNaN(boost)) {
|
||||
return;
|
||||
}
|
||||
query.boost(boost);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright 2013-2017 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
|
||||
*
|
||||
* 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 "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 static org.elasticsearch.index.query.Operator.AND;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.*;
|
||||
import static org.springframework.data.elasticsearch.core.query.Criteria.*;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
import org.apache.lucene.queryparser.flexible.core.util.StringUtils;
|
||||
import org.elasticsearch.index.query.*;
|
||||
import org.springframework.data.elasticsearch.core.query.Criteria;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* CriteriaQueryProcessor
|
||||
*
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Franck Marchand
|
||||
* @author Artur Konczak
|
||||
*/
|
||||
class CriteriaQueryProcessor {
|
||||
|
||||
|
||||
QueryBuilder createQueryFromCriteria(Criteria criteria) {
|
||||
if (criteria == null)
|
||||
return null;
|
||||
|
||||
List<QueryBuilder> shouldQueryBuilderList = new LinkedList<>();
|
||||
List<QueryBuilder> mustNotQueryBuilderList = new LinkedList<>();
|
||||
List<QueryBuilder> mustQueryBuilderList = new LinkedList<>();
|
||||
|
||||
ListIterator<Criteria> chainIterator = criteria.getCriteriaChain().listIterator();
|
||||
|
||||
QueryBuilder firstQuery = null;
|
||||
boolean negateFirstQuery = false;
|
||||
|
||||
while (chainIterator.hasNext()) {
|
||||
Criteria chainedCriteria = chainIterator.next();
|
||||
QueryBuilder queryFragmentForCriteria = createQueryFragmentForCriteria(chainedCriteria);
|
||||
if (queryFragmentForCriteria != null) {
|
||||
if (firstQuery == null) {
|
||||
firstQuery = queryFragmentForCriteria;
|
||||
negateFirstQuery = chainedCriteria.isNegating();
|
||||
continue;
|
||||
}
|
||||
if (chainedCriteria.isOr()) {
|
||||
shouldQueryBuilderList.add(queryFragmentForCriteria);
|
||||
} else if (chainedCriteria.isNegating()) {
|
||||
mustNotQueryBuilderList.add(queryFragmentForCriteria);
|
||||
} else {
|
||||
mustQueryBuilderList.add(queryFragmentForCriteria);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (firstQuery != null) {
|
||||
if (!shouldQueryBuilderList.isEmpty() && mustNotQueryBuilderList.isEmpty() && mustQueryBuilderList.isEmpty()) {
|
||||
shouldQueryBuilderList.add(0, firstQuery);
|
||||
} else {
|
||||
if (negateFirstQuery) {
|
||||
mustNotQueryBuilderList.add(0, firstQuery);
|
||||
} else {
|
||||
mustQueryBuilderList.add(0, firstQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BoolQueryBuilder query = null;
|
||||
|
||||
if (!shouldQueryBuilderList.isEmpty() || !mustNotQueryBuilderList.isEmpty() || !mustQueryBuilderList.isEmpty()) {
|
||||
|
||||
query = boolQuery();
|
||||
|
||||
for (QueryBuilder qb : shouldQueryBuilderList) {
|
||||
query.should(qb);
|
||||
}
|
||||
for (QueryBuilder qb : mustNotQueryBuilderList) {
|
||||
query.mustNot(qb);
|
||||
}
|
||||
for (QueryBuilder qb : mustQueryBuilderList) {
|
||||
query.must(qb);
|
||||
}
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
|
||||
private QueryBuilder createQueryFragmentForCriteria(Criteria chainedCriteria) {
|
||||
if (chainedCriteria.getQueryCriteriaEntries().isEmpty())
|
||||
return null;
|
||||
|
||||
Iterator<Criteria.CriteriaEntry> it = chainedCriteria.getQueryCriteriaEntries().iterator();
|
||||
boolean singeEntryCriteria = (chainedCriteria.getQueryCriteriaEntries().size() == 1);
|
||||
|
||||
String fieldName = chainedCriteria.getField().getName();
|
||||
Assert.notNull(fieldName, "Unknown field");
|
||||
QueryBuilder query = null;
|
||||
|
||||
if (singeEntryCriteria) {
|
||||
Criteria.CriteriaEntry entry = it.next();
|
||||
query = processCriteriaEntry(entry, fieldName);
|
||||
} else {
|
||||
query = boolQuery();
|
||||
while (it.hasNext()) {
|
||||
Criteria.CriteriaEntry entry = it.next();
|
||||
((BoolQueryBuilder) query).must(processCriteriaEntry(entry, fieldName));
|
||||
}
|
||||
}
|
||||
|
||||
addBoost(query, chainedCriteria.getBoost());
|
||||
return query;
|
||||
}
|
||||
|
||||
|
||||
private QueryBuilder processCriteriaEntry(Criteria.CriteriaEntry entry,/* OperationKey key, Object value,*/ String fieldName) {
|
||||
Object value = entry.getValue();
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
OperationKey key = entry.getKey();
|
||||
QueryBuilder query = null;
|
||||
|
||||
String searchText = StringUtils.toString(value);
|
||||
|
||||
Iterable<Object> collection = null;
|
||||
|
||||
switch (key) {
|
||||
case EQUALS:
|
||||
query = queryStringQuery(searchText).field(fieldName).defaultOperator(AND);
|
||||
break;
|
||||
case CONTAINS:
|
||||
query = queryStringQuery("*" + searchText + "*").field(fieldName).analyzeWildcard(true);
|
||||
break;
|
||||
case STARTS_WITH:
|
||||
query = queryStringQuery(searchText + "*").field(fieldName).analyzeWildcard(true);
|
||||
break;
|
||||
case ENDS_WITH:
|
||||
query = queryStringQuery("*" + searchText).field(fieldName).analyzeWildcard(true);
|
||||
break;
|
||||
case EXPRESSION:
|
||||
query = queryStringQuery(searchText).field(fieldName);
|
||||
break;
|
||||
case LESS_EQUAL:
|
||||
query = rangeQuery(fieldName).lte(value);
|
||||
break;
|
||||
case GREATER_EQUAL:
|
||||
query = rangeQuery(fieldName).gte(value);
|
||||
break;
|
||||
case BETWEEN:
|
||||
Object[] ranges = (Object[]) value;
|
||||
query = rangeQuery(fieldName).from(ranges[0]).to(ranges[1]);
|
||||
break;
|
||||
case LESS:
|
||||
query = rangeQuery(fieldName).lt(value);
|
||||
break;
|
||||
case GREATER:
|
||||
query = rangeQuery(fieldName).gt(value);
|
||||
break;
|
||||
case FUZZY:
|
||||
query = fuzzyQuery(fieldName, searchText);
|
||||
break;
|
||||
case IN:
|
||||
query = boolQuery();
|
||||
collection = (Iterable<Object>) value;
|
||||
for (Object item : collection) {
|
||||
((BoolQueryBuilder) query).should(queryStringQuery(item.toString()).field(fieldName));
|
||||
}
|
||||
break;
|
||||
case NOT_IN:
|
||||
query = boolQuery();
|
||||
collection = (Iterable<Object>) value;
|
||||
for (Object item : collection) {
|
||||
((BoolQueryBuilder) query).mustNot(queryStringQuery(item.toString()).field(fieldName));
|
||||
}
|
||||
break;
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
private void addBoost(QueryBuilder query, float boost) {
|
||||
if (Float.isNaN(boost)) {
|
||||
return;
|
||||
}
|
||||
query.boost(boost);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2014 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
|
||||
*
|
||||
* 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 "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.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.core.*;
|
||||
import com.fasterxml.jackson.databind.*;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.core.geo.CustomGeoModule;
|
||||
import org.springframework.data.geo.*;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.core.Version;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
|
||||
|
||||
/**
|
||||
* DocumentMapper using jackson
|
||||
*
|
||||
* @author Artur Konczak
|
||||
* @author Petar Tahchiev
|
||||
*/
|
||||
public class DefaultEntityMapper implements EntityMapper {
|
||||
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
public DefaultEntityMapper() {
|
||||
objectMapper = new ObjectMapper();
|
||||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
|
||||
objectMapper.registerModule(new CustomGeoModule());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mapToString(Object object) throws IOException {
|
||||
return objectMapper.writeValueAsString(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T mapToObject(String source, Class<T> clazz) throws IOException {
|
||||
return objectMapper.readValue(source, clazz);
|
||||
}
|
||||
}
|
||||
-268
@@ -1,268 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2020 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 static org.elasticsearch.client.Requests.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
|
||||
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest;
|
||||
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
|
||||
import org.elasticsearch.client.Request;
|
||||
import org.elasticsearch.client.RequestOptions;
|
||||
import org.elasticsearch.client.Response;
|
||||
import org.elasticsearch.client.RestClient;
|
||||
import org.elasticsearch.client.RestHighLevelClient;
|
||||
import org.elasticsearch.client.indices.CreateIndexRequest;
|
||||
import org.elasticsearch.client.indices.GetIndexRequest;
|
||||
import org.elasticsearch.client.indices.PutMappingRequest;
|
||||
import org.elasticsearch.cluster.metadata.AliasMetaData;
|
||||
import org.springframework.data.elasticsearch.ElasticsearchException;
|
||||
import org.springframework.data.elasticsearch.core.client.support.AliasData;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.document.Document;
|
||||
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;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* {@link IndexOperations} implementation using the RestClient.
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Sascha Woo
|
||||
* @since 4.0
|
||||
*/
|
||||
class DefaultIndexOperations extends AbstractDefaultIndexOperations implements IndexOperations {
|
||||
|
||||
private RestHighLevelClient client;
|
||||
|
||||
public DefaultIndexOperations(RestHighLevelClient client, ElasticsearchConverter elasticsearchConverter,
|
||||
Class<?> boundClass) {
|
||||
super(elasticsearchConverter, boundClass);
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public DefaultIndexOperations(RestHighLevelClient client, ElasticsearchConverter elasticsearchConverter,
|
||||
IndexCoordinates boundIndex) {
|
||||
super(elasticsearchConverter, boundIndex);
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doCreate(String indexName, @Nullable Document settings) {
|
||||
CreateIndexRequest request = requestFactory.createIndexRequest(indexName, settings);
|
||||
try {
|
||||
return client.indices().create(request, RequestOptions.DEFAULT).isAcknowledged();
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException(
|
||||
"Error for creating index: " + indexName + ", client: " + client.getLowLevelClient().getNodes(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doDelete(String indexName) {
|
||||
|
||||
Assert.notNull(indexName, "No index defined for delete operation");
|
||||
|
||||
if (doExists(indexName)) {
|
||||
DeleteIndexRequest request = new DeleteIndexRequest(indexName);
|
||||
try {
|
||||
return client.indices().delete(request, RequestOptions.DEFAULT).isAcknowledged();
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error while deleting index request: " + request.toString(), e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doExists(String indexName) {
|
||||
GetIndexRequest request = new GetIndexRequest(indexName);
|
||||
try {
|
||||
return client.indices().exists(request, RequestOptions.DEFAULT);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error while for indexExists request: " + request.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doPutMapping(IndexCoordinates index, Document mapping) {
|
||||
|
||||
Assert.notNull(index, "No index defined for putMapping()");
|
||||
|
||||
PutMappingRequest request = requestFactory.putMappingRequest(index, mapping);
|
||||
try {
|
||||
return client.indices().putMapping(request, RequestOptions.DEFAULT).isAcknowledged();
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Failed to put mapping for " + index.getIndexName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> doGetMapping(IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(index, "No index defined for getMapping()");
|
||||
|
||||
RestClient restClient = client.getLowLevelClient();
|
||||
try {
|
||||
Request request = new Request("GET", '/' + index.getIndexName() + "/_mapping");
|
||||
Response response = restClient.performRequest(request);
|
||||
return convertMappingResponse(EntityUtils.toString(response.getEntity()));
|
||||
} catch (Exception e) {
|
||||
throw new ElasticsearchException("Error while getting mapping for indexName : " + index.getIndexName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doAddAlias(AliasQuery query, IndexCoordinates index) {
|
||||
IndicesAliasesRequest request = requestFactory.indicesAddAliasesRequest(query, index);
|
||||
try {
|
||||
return client.indices().updateAliases(request, RequestOptions.DEFAULT).isAcknowledged();
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("failed to update aliases with request: " + request, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
try {
|
||||
return client.indices().updateAliases(indicesAliasesRequest, RequestOptions.DEFAULT).isAcknowledged();
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException(
|
||||
"failed to update aliases with indicesRemoveAliasesRequest: " + indicesAliasesRequest, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<AliasMetaData> doQueryForAlias(String indexName) {
|
||||
List<AliasMetaData> aliases = null;
|
||||
RestClient restClient = client.getLowLevelClient();
|
||||
Response response;
|
||||
String aliasResponse;
|
||||
|
||||
try {
|
||||
response = restClient.performRequest(new Request("GET", '/' + indexName + "/_alias/*"));
|
||||
aliasResponse = EntityUtils.toString(response.getEntity());
|
||||
} catch (Exception e) {
|
||||
throw new ElasticsearchException("Error while getting mapping for indexName : " + indexName, e);
|
||||
}
|
||||
|
||||
return convertAliasResponse(aliasResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> doGetSettings(String indexName, boolean includeDefaults) {
|
||||
|
||||
Assert.notNull(indexName, "No index defined for getSettings");
|
||||
|
||||
GetSettingsRequest request = new GetSettingsRequest() //
|
||||
.indices(indexName) //
|
||||
.includeDefaults(includeDefaults);
|
||||
|
||||
try {
|
||||
GetSettingsResponse response = client.indices() //
|
||||
.getSettings(request, RequestOptions.DEFAULT);
|
||||
|
||||
return convertSettingsResponseToMap(response, indexName);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("failed to get settings for index: " + indexName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRefresh(IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(index, "No index defined for refresh()");
|
||||
|
||||
try {
|
||||
client.indices().refresh(refreshRequest(index.getIndexNames()), RequestOptions.DEFAULT);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("failed to refresh index: " + index, e);
|
||||
}
|
||||
}
|
||||
|
||||
// region Helper methods
|
||||
private Map<String, Object> convertMappingResponse(String mappingResponse) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
try {
|
||||
Map<String, Object> result = null;
|
||||
JsonNode node = mapper.readTree(mappingResponse);
|
||||
|
||||
node = node.findValue("mappings");
|
||||
result = mapper.readValue(mapper.writeValueAsString(node), HashMap.class);
|
||||
|
||||
return result;
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Could not map alias response : " + mappingResponse, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* It takes two steps to create a List<AliasMetadata> from the elasticsearch http response because the aliases field
|
||||
* is actually a Map by alias name, but the alias name is on the AliasMetadata.
|
||||
*
|
||||
* @param aliasResponse
|
||||
* @return
|
||||
*/
|
||||
private List<AliasMetaData> convertAliasResponse(String aliasResponse) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
try {
|
||||
JsonNode node = mapper.readTree(aliasResponse);
|
||||
node = node.findValue("aliases");
|
||||
|
||||
if (node == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
Map<String, AliasData> aliasData = mapper.readValue(mapper.writeValueAsString(node),
|
||||
new TypeReference<Map<String, AliasData>>() {});
|
||||
|
||||
Iterable<Map.Entry<String, AliasData>> aliasIter = aliasData.entrySet();
|
||||
List<AliasMetaData> aliasMetaDataList = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<String, AliasData> aliasentry : aliasIter) {
|
||||
AliasData data = aliasentry.getValue();
|
||||
aliasMetaDataList.add(AliasMetaData.newAliasMetaDataBuilder(aliasentry.getKey()).filter(data.getFilter())
|
||||
.routing(data.getRouting()).searchRouting(data.getSearch_routing()).indexRouting(data.getIndex_routing())
|
||||
.build());
|
||||
}
|
||||
return aliasMetaDataList;
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Could not map alias response : " + aliasResponse, e);
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright 2014-2017 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
|
||||
*
|
||||
* 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 "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.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.elasticsearch.action.get.GetResponse;
|
||||
import org.elasticsearch.action.get.MultiGetItemResponse;
|
||||
import org.elasticsearch.action.get.MultiGetResponse;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.search.SearchHit;
|
||||
import org.elasticsearch.search.SearchHitField;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.elasticsearch.ElasticsearchException;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.ScriptedField;
|
||||
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
|
||||
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonEncoding;
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
|
||||
/**
|
||||
* @author Artur Konczak
|
||||
* @author Petar Tahchiev
|
||||
* @author Young Gu
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class DefaultResultMapper extends AbstractResultMapper {
|
||||
|
||||
private MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext;
|
||||
|
||||
public DefaultResultMapper() {
|
||||
super(new DefaultEntityMapper());
|
||||
}
|
||||
|
||||
public DefaultResultMapper(MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext) {
|
||||
super(new DefaultEntityMapper());
|
||||
this.mappingContext = mappingContext;
|
||||
}
|
||||
|
||||
public DefaultResultMapper(EntityMapper entityMapper) {
|
||||
super(entityMapper);
|
||||
}
|
||||
|
||||
public DefaultResultMapper(
|
||||
MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext,
|
||||
EntityMapper entityMapper) {
|
||||
super(entityMapper);
|
||||
this.mappingContext = mappingContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
|
||||
long totalHits = response.getHits().totalHits();
|
||||
List<T> results = new ArrayList<>();
|
||||
for (SearchHit hit : response.getHits()) {
|
||||
if (hit != null) {
|
||||
T result = null;
|
||||
if (StringUtils.isNotBlank(hit.sourceAsString())) {
|
||||
result = mapEntity(hit.sourceAsString(), clazz);
|
||||
} else {
|
||||
result = mapEntity(hit.getFields().values(), clazz);
|
||||
}
|
||||
setPersistentEntityId(result, hit.getId(), clazz);
|
||||
populateScriptFields(result, hit);
|
||||
results.add(result);
|
||||
}
|
||||
}
|
||||
|
||||
return new AggregatedPageImpl<T>(results, pageable, totalHits, response.getAggregations(), response.getScrollId());
|
||||
}
|
||||
|
||||
private <T> void populateScriptFields(T result, SearchHit hit) {
|
||||
if (hit.getFields() != null && !hit.getFields().isEmpty() && result != null) {
|
||||
for (java.lang.reflect.Field field : result.getClass().getDeclaredFields()) {
|
||||
ScriptedField scriptedField = field.getAnnotation(ScriptedField.class);
|
||||
if (scriptedField != null) {
|
||||
String name = scriptedField.name().isEmpty() ? field.getName() : scriptedField.name();
|
||||
SearchHitField searchHitField = hit.getFields().get(name);
|
||||
if (searchHitField != null) {
|
||||
field.setAccessible(true);
|
||||
try {
|
||||
field.set(result, searchHitField.getValue());
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new ElasticsearchException("failed to set scripted field: " + name + " with value: "
|
||||
+ searchHitField.getValue(), e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new ElasticsearchException("failed to access scripted field: " + name, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private <T> T mapEntity(Collection<SearchHitField> values, Class<T> clazz) {
|
||||
return mapEntity(buildJSONFromFields(values), clazz);
|
||||
}
|
||||
|
||||
private String buildJSONFromFields(Collection<SearchHitField> values) {
|
||||
JsonFactory nodeFactory = new JsonFactory();
|
||||
try {
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
JsonGenerator generator = nodeFactory.createGenerator(stream, JsonEncoding.UTF8);
|
||||
generator.writeStartObject();
|
||||
for (SearchHitField value : values) {
|
||||
if (value.getValues().size() > 1) {
|
||||
generator.writeArrayFieldStart(value.getName());
|
||||
for (Object val : value.getValues()) {
|
||||
generator.writeObject(val);
|
||||
}
|
||||
generator.writeEndArray();
|
||||
} else {
|
||||
generator.writeObjectField(value.getName(), value.getValue());
|
||||
}
|
||||
}
|
||||
generator.writeEndObject();
|
||||
generator.flush();
|
||||
return new String(stream.toByteArray(), Charset.forName("UTF-8"));
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T mapResult(GetResponse response, Class<T> clazz) {
|
||||
T result = mapEntity(response.getSourceAsString(), clazz);
|
||||
if (result != null) {
|
||||
setPersistentEntityId(result, response.getId(), clazz);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> LinkedList<T> mapResults(MultiGetResponse responses, Class<T> clazz) {
|
||||
LinkedList<T> list = new LinkedList<>();
|
||||
for (MultiGetItemResponse response : responses.getResponses()) {
|
||||
if (!response.isFailed() && response.getResponse().isExists()) {
|
||||
T result = mapEntity(response.getResponse().getSourceAsString(), clazz);
|
||||
setPersistentEntityId(result, response.getResponse().getId(), clazz);
|
||||
list.add(result);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private <T> void setPersistentEntityId(T result, String id, Class<T> clazz) {
|
||||
|
||||
if (mappingContext != null && clazz.isAnnotationPresent(Document.class)) {
|
||||
|
||||
ElasticsearchPersistentEntity<?> persistentEntity = mappingContext.getRequiredPersistentEntity(clazz);
|
||||
ElasticsearchPersistentProperty idProperty = persistentEntity.getIdProperty();
|
||||
|
||||
// Only deal with String because ES generated Ids are strings !
|
||||
if (idProperty != null && idProperty.getType().isAssignableFrom(String.class)) {
|
||||
persistentEntity.getPropertyAccessor(result).setProperty(idProperty, id);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
-158
@@ -1,158 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2020 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 static org.elasticsearch.client.Requests.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
|
||||
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
|
||||
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
|
||||
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest;
|
||||
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequestBuilder;
|
||||
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest;
|
||||
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.cluster.metadata.AliasMetaData;
|
||||
import org.springframework.data.elasticsearch.ElasticsearchException;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.document.Document;
|
||||
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;
|
||||
|
||||
/**
|
||||
* {@link IndexOperations} implementation using the TransportClient.
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Sascha Woo
|
||||
* @since 4.0
|
||||
*/
|
||||
class DefaultTransportIndexOperations extends AbstractDefaultIndexOperations implements IndexOperations {
|
||||
|
||||
private final Client client;
|
||||
|
||||
public DefaultTransportIndexOperations(Client client, ElasticsearchConverter elasticsearchConverter,
|
||||
Class<?> boundClass) {
|
||||
super(elasticsearchConverter, boundClass);
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public DefaultTransportIndexOperations(Client client, ElasticsearchConverter elasticsearchConverter,
|
||||
IndexCoordinates boundIndex) {
|
||||
super(elasticsearchConverter, boundIndex);
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doCreate(String indexName, @Nullable Document settings) {
|
||||
CreateIndexRequestBuilder createIndexRequestBuilder = requestFactory.createIndexRequestBuilder(client, indexName,
|
||||
settings);
|
||||
return createIndexRequestBuilder.execute().actionGet().isAcknowledged();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doDelete(String indexName) {
|
||||
|
||||
Assert.notNull(indexName, "No index defined for delete operation");
|
||||
|
||||
if (doExists(indexName)) {
|
||||
return client.admin().indices().delete(new DeleteIndexRequest(indexName)).actionGet().isAcknowledged();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doExists(String indexName) {
|
||||
return client.admin().indices().exists(indicesExistsRequest(indexName)).actionGet().isExists();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doPutMapping(IndexCoordinates index, Document mapping) {
|
||||
|
||||
Assert.notNull(index, "No index defined for putMapping()");
|
||||
|
||||
PutMappingRequestBuilder requestBuilder = requestFactory.putMappingRequestBuilder(client, index, mapping);
|
||||
return requestBuilder.execute().actionGet().isAcknowledged();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> doGetMapping(IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(index, "No index defined for getMapping()");
|
||||
|
||||
try {
|
||||
return client.admin().indices().getMappings( //
|
||||
new GetMappingsRequest().indices(index.getIndexNames())).actionGet() //
|
||||
.getMappings().get(index.getIndexName()).get(IndexCoordinates.TYPE) //
|
||||
.getSourceAsMap();
|
||||
} catch (Exception e) {
|
||||
throw new ElasticsearchException("Error while getting mapping for indexName : " + index.getIndexName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@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
|
||||
protected boolean doRemoveAlias(AliasQuery query, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(index, "No index defined for Alias");
|
||||
Assert.notNull(query.getAliasName(), "No alias defined");
|
||||
|
||||
return client.admin().indices().prepareAliases().removeAlias(index.getIndexName(), query.getAliasName()).execute()
|
||||
.actionGet().isAcknowledged();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<AliasMetaData> doQueryForAlias(String indexName) {
|
||||
return client.admin().indices().getAliases(new GetAliasesRequest().indices(indexName)).actionGet().getAliases()
|
||||
.get(indexName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> doGetSettings(String indexName, boolean includeDefaults) {
|
||||
|
||||
Assert.notNull(indexName, "No index defined for getSettings");
|
||||
|
||||
GetSettingsRequest request = new GetSettingsRequest() //
|
||||
.indices(indexName) //
|
||||
.includeDefaults(includeDefaults);
|
||||
|
||||
GetSettingsResponse response = client.admin() //
|
||||
.indices() //
|
||||
.getSettings(request) //
|
||||
.actionGet();
|
||||
|
||||
return convertSettingsResponseToMap(response, indexName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doRefresh(IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(index, "No index defined for refresh()");
|
||||
|
||||
client.admin().indices().refresh(refreshRequest(index.getIndexNames())).actionGet();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,261 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2020 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.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.query.BulkOptions;
|
||||
import org.springframework.data.elasticsearch.core.query.DeleteQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.GetQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.IndexQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.UpdateResponse;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* The operations for the
|
||||
* <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs.html">Elasticsearch Document APIs</a>.
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.0
|
||||
*/
|
||||
public interface DocumentOperations {
|
||||
|
||||
/**
|
||||
* Saves an entity to the index specified in the entity's Document annotation
|
||||
*
|
||||
* @param entity the entity to save, must not be {@literal null}
|
||||
* @param <T> the entity type
|
||||
* @return the saved entity
|
||||
*/
|
||||
<T> T save(T entity);
|
||||
|
||||
/**
|
||||
* Saves an entity to the index specified in the entity's Document annotation
|
||||
*
|
||||
* @param entity the entity to save, must not be {@literal null}
|
||||
* @param index the index to save the entity in, must not be {@literal null}
|
||||
* @param <T> the entity type
|
||||
* @return the saved entity
|
||||
*/
|
||||
<T> T save(T entity, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* saves the given entities to the index retrieved from the entities' Document annotation
|
||||
*
|
||||
* @param entities must not be {@literal null}
|
||||
* @param <T> the entity type
|
||||
* @return the saved entites
|
||||
*/
|
||||
<T> Iterable<T> save(Iterable<T> entities);
|
||||
|
||||
/**
|
||||
* saves the given entities to the given index
|
||||
*
|
||||
* @param entities must not be {@literal null}
|
||||
* @param index the index to save the entities in, must not be {@literal null}
|
||||
* @param <T> the entity type
|
||||
* @return the saved entities
|
||||
*/
|
||||
<T> Iterable<T> save(Iterable<T> entities, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* saves the given entities to the index retrieved from the entities' Document annotation
|
||||
*
|
||||
* @param entities must not be {@literal null}
|
||||
* @param <T> the entity type
|
||||
* @return the saved entities as Iterable
|
||||
*/
|
||||
<T> Iterable<T> save(T... entities);
|
||||
|
||||
/**
|
||||
* Index an object. Will do save or update.
|
||||
*
|
||||
* @param query the query defining the object
|
||||
* @param index the index where the object is stored.
|
||||
* @return returns the document id
|
||||
*/
|
||||
String index(IndexQuery query, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* Retrieves an object from the index specified in the entity's Document annotation.
|
||||
*
|
||||
* @param id the id of the object
|
||||
* @param clazz the entity class,
|
||||
* @param <T> the entity type
|
||||
* @return the entity
|
||||
*/
|
||||
@Nullable
|
||||
<T> T get(String id, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Retrieves an object from the index specified in the entity's Document annotation.
|
||||
*
|
||||
* @param id the id of the object
|
||||
* @param clazz the entity class,
|
||||
* @param index the index from which the object is read.
|
||||
* @return the entity
|
||||
*/
|
||||
@Nullable
|
||||
<T> T get(String id, Class<T> clazz, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* Execute a multiGet against elasticsearch for the given ids.
|
||||
*
|
||||
* @param query the query defining the ids of the objects to get
|
||||
* @param clazz the type of the object to be returned
|
||||
* @param index the index(es) from which the objects are read.
|
||||
* @return list of objects
|
||||
*/
|
||||
<T> List<T> multiGet(Query query, Class<T> clazz, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* Check if an entity with given {@literal id} exists.
|
||||
*
|
||||
* @param id the {@literal _id} of the document to look for.
|
||||
* @param clazz the domain type used.
|
||||
* @return {@literal true} if a matching document exists, {@literal false} otherwise.
|
||||
*/
|
||||
boolean exists(String id, Class<?> clazz);
|
||||
|
||||
/**
|
||||
* Check if an entity with given {@literal id} exists.
|
||||
*
|
||||
* @param id the {@literal _id} of the document to look for.
|
||||
* @param index the target index, must not be {@literal null}
|
||||
* @return {@literal true} if a matching document exists, {@literal false} otherwise.
|
||||
*/
|
||||
boolean exists(String id, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* Bulk index all objects. Will do save or update.
|
||||
*
|
||||
* @param queries the queries to execute in bulk
|
||||
* @return the ids of the indexed objects
|
||||
*/
|
||||
default List<String> bulkIndex(List<IndexQuery> queries, IndexCoordinates index) {
|
||||
return bulkIndex(queries, BulkOptions.defaultOptions(), index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk index all objects. Will do save or update.
|
||||
*
|
||||
* @param queries the queries to execute in bulk
|
||||
* @param bulkOptions options to be added to the bulk request
|
||||
* @return the ids of the indexed objects
|
||||
*/
|
||||
List<String> bulkIndex(List<IndexQuery> queries, BulkOptions bulkOptions, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* Bulk update all objects. Will do update.
|
||||
*
|
||||
* @param queries the queries to execute in bulk
|
||||
*/
|
||||
default void bulkUpdate(List<UpdateQuery> queries, IndexCoordinates index) {
|
||||
bulkUpdate(queries, BulkOptions.defaultOptions(), index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk update all objects. Will do update.
|
||||
*
|
||||
* @param queries the queries to execute in bulk
|
||||
* @param bulkOptions options to be added to the bulk request
|
||||
*/
|
||||
void bulkUpdate(List<UpdateQuery> queries, BulkOptions bulkOptions, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* Delete the one object with provided id.
|
||||
*
|
||||
* @param id the document ot delete
|
||||
* @param index the index from which to delete
|
||||
* @return documentId of the document deleted
|
||||
*/
|
||||
String delete(String id, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* Delete the one object with provided id.
|
||||
*
|
||||
* @param id the document ot delete
|
||||
* @param entityType must not be {@literal null}.
|
||||
* @return documentId of the document deleted
|
||||
*/
|
||||
String delete(String id, Class<?> entityType);
|
||||
|
||||
/**
|
||||
* Deletes the given entity
|
||||
*
|
||||
* @param entity the entity to delete
|
||||
* @return documentId of the document deleted
|
||||
*/
|
||||
String delete(Object entity);
|
||||
|
||||
/**
|
||||
* Deletes the given entity
|
||||
*
|
||||
* @param entity the entity to delete
|
||||
* @param index the index from which to delete
|
||||
* @return documentId of the document deleted
|
||||
*/
|
||||
String delete(Object entity, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* 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}
|
||||
* @param index the index from which to delete
|
||||
*/
|
||||
void delete(Query query, Class<?> clazz, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* Partial update of the document.
|
||||
*
|
||||
* @param updateQuery query defining the update
|
||||
* @param index the index where to update the records
|
||||
* @return the update response
|
||||
*/
|
||||
UpdateResponse update(UpdateQuery updateQuery, IndexCoordinates index);
|
||||
|
||||
// region deprecated
|
||||
/**
|
||||
* Delete all records matching the query.
|
||||
*
|
||||
* @param query query defining the objects
|
||||
* @param index the index where to delete the records
|
||||
* @deprecated since 4.0, use {@link #delete(Query, Class, IndexCoordinates)}
|
||||
*/
|
||||
@Deprecated
|
||||
void delete(DeleteQuery query, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* Retrieves an object from an index.
|
||||
*
|
||||
* @param query the query defining the id of the object to get
|
||||
* @param clazz the type of the object to be returned
|
||||
* @param index the index from which the object is read.
|
||||
* @return the found object
|
||||
* @deprecated since 4.0, use {@link #getById(String, Class, IndexCoordinates)}
|
||||
*/
|
||||
@Deprecated
|
||||
@Nullable
|
||||
<T> T get(GetQuery query, Class<T> clazz, IndexCoordinates index);
|
||||
|
||||
// endregion
|
||||
}
|
||||
-68
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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.net.ConnectException;
|
||||
import java.util.List;
|
||||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.ElasticsearchStatusException;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.data.elasticsearch.NoSuchIndexException;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 3.2
|
||||
*/
|
||||
public class ElasticsearchExceptionTranslator implements PersistenceExceptionTranslator {
|
||||
|
||||
@Override
|
||||
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
|
||||
|
||||
if (ex instanceof ElasticsearchException) {
|
||||
|
||||
ElasticsearchException elasticsearchException = (ElasticsearchException) ex;
|
||||
|
||||
if (!indexAvailable(elasticsearchException)) {
|
||||
return new NoSuchIndexException(ObjectUtils.nullSafeToString(elasticsearchException.getMetadata("es.index")),
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (ex.getCause() instanceof ConnectException) {
|
||||
return new DataAccessResourceFailureException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean indexAvailable(ElasticsearchException ex) {
|
||||
|
||||
List<String> metadata = ex.getMetadata("es.index_uuid");
|
||||
if (metadata == null) {
|
||||
if (ex instanceof ElasticsearchStatusException) {
|
||||
return StringUtils.hasText(ObjectUtils.nullSafeToString(((ElasticsearchStatusException) ex).getIndex()));
|
||||
}
|
||||
}
|
||||
return !CollectionUtils.contains(metadata.iterator(), "_na_");
|
||||
}
|
||||
}
|
||||
+577
-409
@@ -1,409 +1,577 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.elasticsearch.cluster.metadata.AliasMetaData;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.document.Document;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.query.AliasQuery;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* ElasticsearchOperations. Since 4.0 this interface only contains common helper functions, the other methods have been
|
||||
* moved to the different interfaces that are extended by ElasticsearchOperations. The interfaces now reflect the
|
||||
* <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/rest-apis.html">REST API structure</a> of
|
||||
* Elasticsearch.
|
||||
*
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Kevin Leturc
|
||||
* @author Zetang Zeng
|
||||
* @author Dmitriy Yakovlev
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
public interface ElasticsearchOperations extends DocumentOperations, SearchOperations {
|
||||
|
||||
/**
|
||||
* get an {@link IndexOperations} that is bound to the given class
|
||||
*
|
||||
* @return IndexOperations
|
||||
*/
|
||||
IndexOperations indexOps(Class<?> clazz);
|
||||
|
||||
/**
|
||||
* get an {@link IndexOperations} that is bound to the given class
|
||||
*
|
||||
* @return IndexOperations
|
||||
*/
|
||||
IndexOperations indexOps(IndexCoordinates index);
|
||||
|
||||
ElasticsearchConverter getElasticsearchConverter();
|
||||
|
||||
IndexCoordinates getIndexCoordinatesFor(Class<?> clazz);
|
||||
|
||||
// region IndexOperations
|
||||
/**
|
||||
* Create an index for given indexName if it does not already exist.
|
||||
*
|
||||
* @param indexName the name of the index
|
||||
* @return {@literal true} if the index was created
|
||||
* @deprecated since 4.0, use {@link IndexOperations#create()}
|
||||
*/
|
||||
@Deprecated
|
||||
default boolean createIndex(String indexName) {
|
||||
return indexOps(IndexCoordinates.of(indexName)).create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an index for given indexName and Settings.
|
||||
*
|
||||
* @param indexName the name of the index
|
||||
* @param settings the index settings
|
||||
* @return {@literal true} if the index was created
|
||||
* @deprecated since 4.0, use {@link IndexOperations#create(Document)}
|
||||
*/
|
||||
@Deprecated
|
||||
default boolean createIndex(String indexName, Object settings) {
|
||||
return indexOps(IndexCoordinates.of(indexName)).create(getDocument(settings));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an index for a class if it does not already exist.
|
||||
*
|
||||
* @param clazz The entity class, must be annotated with
|
||||
* {@link org.springframework.data.elasticsearch.annotations.Document}
|
||||
* @return {@literal true} if the index was created
|
||||
* @deprecated since 4.0, use {@link IndexOperations#create()}
|
||||
*/
|
||||
@Deprecated
|
||||
default boolean createIndex(Class<?> clazz) {
|
||||
return indexOps(clazz).create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an index for given class and Settings.
|
||||
*
|
||||
* @param clazz The entity class, must be annotated with
|
||||
* {@link org.springframework.data.elasticsearch.annotations.Document}
|
||||
* @param settings the index settings
|
||||
* @return {@literal true} if the index was created
|
||||
* @deprecated since 4.0, use {@link IndexOperations#create(Document)}
|
||||
*/
|
||||
@Deprecated
|
||||
default boolean createIndex(Class<?> clazz, Object settings) {
|
||||
return indexOps(clazz).create(getDocument(settings));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an index for given entity.
|
||||
*
|
||||
* @param clazz The entity class, must be annotated with
|
||||
* {@link org.springframework.data.elasticsearch.annotations.Document}
|
||||
* @return {@literal true} if the index was deleted
|
||||
* @deprecated since 4.0, use {@link IndexOperations#delete()}
|
||||
*/
|
||||
@Deprecated
|
||||
default boolean deleteIndex(Class<?> clazz) {
|
||||
return indexOps(clazz).delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an index.
|
||||
*
|
||||
* @param indexName the name of the index to delete
|
||||
* @return {@literal true} if the index was deleted
|
||||
* @deprecated since 4.0, use {@link IndexOperations#delete()}
|
||||
*/
|
||||
@Deprecated
|
||||
default boolean deleteIndex(String indexName) {
|
||||
return indexOps(IndexCoordinates.of(indexName)).delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an index for an IndexCoordinate
|
||||
*
|
||||
* @param index the index to delete
|
||||
* @return {@literal true} if the index was deleted
|
||||
* @deprecated since 4.0 use {@link #indexOps(IndexCoordinates)} and {@link IndexOperations#delete()}
|
||||
*/
|
||||
@Deprecated
|
||||
default boolean deleteIndex(IndexCoordinates index) {
|
||||
return indexOps(index).delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* check if index exists.
|
||||
*
|
||||
* @param indexName the name of the index
|
||||
* @return {@literal true} if the index exists
|
||||
* @deprecated since 4.0, use {@link #indexOps(IndexCoordinates)} and {@link IndexOperations#exists()}
|
||||
*/
|
||||
@Deprecated
|
||||
default boolean indexExists(String indexName) {
|
||||
return indexOps(IndexCoordinates.of(indexName)).exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* check if index is exists.
|
||||
*
|
||||
* @param clazz The entity class, must be annotated with
|
||||
* {@link org.springframework.data.elasticsearch.annotations.Document}
|
||||
* @return {@literal true} if the index exists
|
||||
* @deprecated since 4.0, use {@link #indexOps(Class)} and {@link IndexOperations#exists()}
|
||||
*/
|
||||
@Deprecated
|
||||
default boolean indexExists(Class<?> clazz) {
|
||||
return indexOps(clazz).exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create mapping for a class and store it to the index.
|
||||
*
|
||||
* @param clazz The entity class, must be annotated with
|
||||
* {@link org.springframework.data.elasticsearch.annotations.Document}
|
||||
* @return {@literal true} if the mapping could be stored
|
||||
* @deprecated since 4.0, use {@link #indexOps(Class)}, {@link IndexOperations#createMapping(Class)} and
|
||||
* {@link IndexOperations#putMapping(Document)}
|
||||
*/
|
||||
@Deprecated
|
||||
default boolean putMapping(Class<?> clazz) {
|
||||
IndexOperations indexOps = indexOps(clazz);
|
||||
return indexOps.putMapping(indexOps.createMapping(clazz));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create mapping for the given class and put the mapping to the given index.
|
||||
*
|
||||
* @param index the index to store the mapping to
|
||||
* @param clazz The entity class, must be annotated with
|
||||
* {@link org.springframework.data.elasticsearch.annotations.Document}
|
||||
* @return {@literal true} if the mapping could be stored
|
||||
* @deprecated since 4.0, use {@link #indexOps(IndexCoordinates)}, {@link IndexOperations#createMapping(Class)} and
|
||||
* {@link IndexOperations#putMapping(Document)}
|
||||
*/
|
||||
@Deprecated
|
||||
default boolean putMapping(IndexCoordinates index, Class<?> clazz) {
|
||||
IndexOperations indexOps = indexOps(index);
|
||||
return indexOps.putMapping(indexOps.createMapping(clazz));
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a mapping to an index.
|
||||
*
|
||||
* @param index the index to store the mapping to
|
||||
* @param mappings can be a JSON String or a {@link Map}
|
||||
* @return {@literal true} if the mapping could be stored
|
||||
* @deprecated since 4.0, use {@link #indexOps(IndexCoordinates)} and {@link IndexOperations#putMapping(Document)}
|
||||
*/
|
||||
@Deprecated
|
||||
default boolean putMapping(IndexCoordinates index, Object mappings) {
|
||||
return indexOps(index).putMapping(getDocument(mappings));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create mapping for a class Stores a mapping to an index.
|
||||
*
|
||||
* @param clazz The entity class, must be annotated with
|
||||
* {@link org.springframework.data.elasticsearch.annotations.Document}
|
||||
* @param mappings can be a JSON String or a {@link Map}
|
||||
* @return {@literal true} if the mapping could be stored
|
||||
* @deprecated since 4.0, use {@link #indexOps(Class)} and {@link IndexOperations#putMapping(Document)}
|
||||
*/
|
||||
@Deprecated
|
||||
default boolean putMapping(Class<?> clazz, Object mappings) {
|
||||
return indexOps(clazz).putMapping(getDocument(mappings));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mapping for an index defined by a class.
|
||||
*
|
||||
* @param clazz The entity class, must be annotated with
|
||||
* {@link org.springframework.data.elasticsearch.annotations.Document}.
|
||||
* @return the mapping
|
||||
* @deprecated since 4.0, use {@link #indexOps(Class)} and {@link IndexOperations#getMapping()}
|
||||
*/
|
||||
@Deprecated
|
||||
default Map<String, Object> getMapping(Class<?> clazz) {
|
||||
return indexOps(clazz).getMapping();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mapping for a given index.
|
||||
*
|
||||
* @param index the index to read the mapping from
|
||||
* @return the mapping
|
||||
* @deprecated since 4.0, use {@link #indexOps(IndexCoordinates)} and {@link IndexOperations#getMapping()}
|
||||
*/
|
||||
@Deprecated
|
||||
default Map<String, Object> getMapping(IndexCoordinates index) {
|
||||
return indexOps(index).getMapping();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an alias.
|
||||
*
|
||||
* @param query query defining the alias
|
||||
* @param index the index for which to add an alias
|
||||
* @return true if the alias was created
|
||||
* @deprecated since 4.0, use {@link #indexOps(IndexCoordinates)} and {@link IndexOperations#addAlias(AliasQuery)}
|
||||
*/
|
||||
@Deprecated
|
||||
default boolean addAlias(AliasQuery query, IndexCoordinates index) {
|
||||
return indexOps(index).addAlias(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an alias.
|
||||
*
|
||||
* @param query query defining the alias
|
||||
* @param index the index for which to remove an alias
|
||||
* @return true if the alias was removed
|
||||
* @deprecated since 4.0, use {@link #indexOps(IndexCoordinates)} {@link IndexOperations#removeAlias(AliasQuery)}
|
||||
*/
|
||||
@Deprecated
|
||||
default boolean removeAlias(AliasQuery query, IndexCoordinates index) {
|
||||
return indexOps(index).removeAlias(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the alias informations for a specified index.
|
||||
*
|
||||
* @param indexName the name of the index
|
||||
* @return alias information
|
||||
* @deprecated since 4.0, use {@link #indexOps(IndexCoordinates)} and {@link IndexOperations#queryForAlias()}
|
||||
*/
|
||||
@Deprecated
|
||||
default List<AliasMetaData> queryForAlias(String indexName) {
|
||||
return indexOps(IndexCoordinates.of(indexName)).queryForAlias();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get settings for a given indexName.
|
||||
*
|
||||
* @param indexName the name of the index
|
||||
* @return the settings
|
||||
* @deprecated since 4.0, use {@link #indexOps(IndexCoordinates)} and {@link IndexOperations#getSettings()} )}
|
||||
*/
|
||||
@Deprecated
|
||||
default Map<String, Object> getSetting(String indexName) {
|
||||
return indexOps(IndexCoordinates.of(indexName)).getSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get settings for a given class.
|
||||
*
|
||||
* @param clazz The entity class, must be annotated with
|
||||
* {@link org.springframework.data.elasticsearch.annotations.Document}
|
||||
* @return the settings
|
||||
* @deprecated since 4.0, use {@link #indexOps(Class)} and {@link IndexOperations#getSettings()}
|
||||
*/
|
||||
@Deprecated
|
||||
default Map<String, Object> getSetting(Class<?> clazz) {
|
||||
return indexOps(clazz).getSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get settings for a given indexName.
|
||||
*
|
||||
* @param indexName the name of the index
|
||||
* @param includeDefaults whether or not to include all the default settings
|
||||
* @deprecated since 4.0 use {@link #indexOps(IndexCoordinates)} and {@link IndexOperations#getSettings(boolean)} ()}
|
||||
* @return the settings
|
||||
*/
|
||||
@Deprecated
|
||||
default Map<String, Object> getSettings(String indexName, boolean includeDefaults) {
|
||||
return indexOps(IndexCoordinates.of(indexName)).getSettings(includeDefaults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get settings for a given class.
|
||||
*
|
||||
* @param clazz The entity class, must be annotated with
|
||||
* {@link org.springframework.data.elasticsearch.annotations.Document}
|
||||
* @param includeDefaults whether or not to include all the default settings
|
||||
* @return the settings
|
||||
* @deprecated since 4.0 use {@link #indexOps(Class)} and {@link IndexOperations#getSettings(boolean)} ()}
|
||||
*/
|
||||
default Map<String, Object> getSettings(Class<?> clazz, boolean includeDefaults) {
|
||||
return indexOps(clazz).getSettings(includeDefaults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the index(es).
|
||||
*
|
||||
* @param index the index to refresh
|
||||
* @deprecated since 4.0, use {@link #indexOps(IndexCoordinates)} and {@link IndexOperations#refresh()} instead}
|
||||
*/
|
||||
@Deprecated
|
||||
default void refresh(IndexCoordinates index) {
|
||||
indexOps(index).refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the index.
|
||||
*
|
||||
* @param clazz The entity class, must be annotated with
|
||||
* {@link org.springframework.data.elasticsearch.annotations.Document}
|
||||
* @deprecated since 4.0, use {@link #indexOps(Class)} and {@link IndexOperations#refresh()} instead}
|
||||
*/
|
||||
@Deprecated
|
||||
default void refresh(Class<?> clazz) {
|
||||
indexOps(clazz).refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* converts an object to a Document
|
||||
*
|
||||
* @param object
|
||||
* @return
|
||||
* @deprecated since 4.0, helper method for deprecated functions
|
||||
*/
|
||||
@Deprecated
|
||||
@Nullable
|
||||
default Document getDocument(Object object) {
|
||||
Document document = null;
|
||||
|
||||
try {
|
||||
if (object instanceof String) {
|
||||
document = Document.parse((String) object);
|
||||
} else if (object instanceof Map) {
|
||||
document = Document.from((Map<String, Object>) object);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("object cannot be converted to Document", e);
|
||||
}
|
||||
return document;
|
||||
} // endregion
|
||||
|
||||
// region helper
|
||||
/**
|
||||
* gets the String representation for an id.
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
* @since 4.0
|
||||
*/
|
||||
@Nullable
|
||||
default String stringIdRepresentation(@Nullable Object id) {
|
||||
return Objects.toString(id, null);
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
/*
|
||||
* Copyright 2013-2016 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
|
||||
*
|
||||
* 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 "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 org.elasticsearch.action.update.UpdateResponse;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.cluster.metadata.AliasMetaData;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
|
||||
import org.springframework.data.elasticsearch.core.query.*;
|
||||
import org.springframework.data.util.CloseableIterator;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* ElasticsearchOperations
|
||||
*
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Kevin Leturc
|
||||
*/
|
||||
public interface ElasticsearchOperations {
|
||||
|
||||
/**
|
||||
* @return Converter in use
|
||||
*/
|
||||
ElasticsearchConverter getElasticsearchConverter();
|
||||
|
||||
/**
|
||||
* @return elasticsearch client
|
||||
*/
|
||||
Client getClient();
|
||||
|
||||
/**
|
||||
* Create an index for a class
|
||||
*
|
||||
* @param clazz
|
||||
* @param <T>
|
||||
*/
|
||||
<T> boolean createIndex(Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Create an index for given indexName
|
||||
*
|
||||
* @param indexName
|
||||
*/
|
||||
boolean createIndex(String indexName);
|
||||
|
||||
/**
|
||||
* Create an index for given indexName and Settings
|
||||
*
|
||||
* @param indexName
|
||||
* @param settings
|
||||
*/
|
||||
boolean createIndex(String indexName, Object settings);
|
||||
|
||||
/**
|
||||
* Create an index for given class and Settings
|
||||
*
|
||||
* @param clazz
|
||||
* @param settings
|
||||
*/
|
||||
<T> boolean createIndex(Class<T> clazz, Object settings);
|
||||
|
||||
/**
|
||||
* Create mapping for a class
|
||||
*
|
||||
* @param clazz
|
||||
* @param <T>
|
||||
*/
|
||||
<T> boolean putMapping(Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Create mapping for a given indexName and type
|
||||
*
|
||||
* @param indexName
|
||||
* @param type
|
||||
* @param mappings
|
||||
*/
|
||||
boolean putMapping(String indexName, String type, Object mappings);
|
||||
|
||||
/**
|
||||
* Create mapping for a class
|
||||
*
|
||||
* @param clazz
|
||||
* @param mappings
|
||||
*/
|
||||
<T> boolean putMapping(Class<T> clazz, Object mappings);
|
||||
|
||||
|
||||
/**
|
||||
* Get mapping for a class
|
||||
*
|
||||
* @param clazz
|
||||
* @param <T>
|
||||
*/
|
||||
<T> Map getMapping(Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Get mapping for a given indexName and type
|
||||
*
|
||||
* @param indexName
|
||||
* @param type
|
||||
*/
|
||||
Map getMapping(String indexName, String type);
|
||||
|
||||
/**
|
||||
* Get settings for a given indexName
|
||||
*
|
||||
* @param indexName
|
||||
*/
|
||||
Map getSetting(String indexName);
|
||||
|
||||
/**
|
||||
* Get settings for a given class
|
||||
*
|
||||
* @param clazz
|
||||
*/
|
||||
<T> Map getSetting(Class<T> clazz);
|
||||
|
||||
|
||||
/**
|
||||
* Execute the query against elasticsearch and return the first returned object
|
||||
*
|
||||
* @param query
|
||||
* @param clazz
|
||||
* @return the first matching object
|
||||
*/
|
||||
<T> T queryForObject(GetQuery query, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Execute the query against elasticsearch and return the first returned object using custom mapper
|
||||
*
|
||||
* @param query
|
||||
* @param clazz
|
||||
* @param mapper
|
||||
* @return the first matching object
|
||||
*/
|
||||
<T> T queryForObject(GetQuery query, Class<T> clazz, GetResultMapper mapper);
|
||||
|
||||
/**
|
||||
* Execute the query against elasticsearch and return the first returned object
|
||||
*
|
||||
* @param query
|
||||
* @param clazz
|
||||
* @return the first matching object
|
||||
*/
|
||||
<T> T queryForObject(CriteriaQuery query, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Execute the query against elasticsearch and return the first returned object
|
||||
*
|
||||
* @param query
|
||||
* @param clazz
|
||||
* @return the first matching object
|
||||
*/
|
||||
<T> T queryForObject(StringQuery query, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Execute the query against elasticsearch and return result as {@link Page}
|
||||
*
|
||||
* @param query
|
||||
* @param clazz
|
||||
* @return
|
||||
*/
|
||||
<T> Page<T> queryForPage(SearchQuery query, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Execute the query against elasticsearch and return result as {@link Page} using custom mapper
|
||||
*
|
||||
* @param query
|
||||
* @param clazz
|
||||
* @return
|
||||
*/
|
||||
<T> Page<T> queryForPage(SearchQuery query, Class<T> clazz, SearchResultMapper mapper);
|
||||
|
||||
/**
|
||||
* Execute the query against elasticsearch and return result as {@link Page}
|
||||
*
|
||||
* @param query
|
||||
* @param clazz
|
||||
* @return
|
||||
*/
|
||||
<T> Page<T> queryForPage(CriteriaQuery query, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Execute the query against elasticsearch and return result as {@link Page}
|
||||
*
|
||||
* @param query
|
||||
* @param clazz
|
||||
* @return
|
||||
*/
|
||||
<T> Page<T> queryForPage(StringQuery query, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Execute the query against elasticsearch and return result as {@link Page} using custom mapper
|
||||
*
|
||||
* @param query
|
||||
* @param clazz
|
||||
* @return
|
||||
*/
|
||||
<T> Page<T> queryForPage(StringQuery query, Class<T> clazz, SearchResultMapper mapper);
|
||||
|
||||
/**
|
||||
* Executes the given {@link CriteriaQuery} against elasticsearch and return result as {@link CloseableIterator}.
|
||||
* <p>
|
||||
* Returns a {@link CloseableIterator} that wraps an Elasticsearch scroll context that needs to be closed in case of error.
|
||||
*
|
||||
* @param <T> element return type
|
||||
* @param query
|
||||
* @param clazz
|
||||
* @return
|
||||
* @since 1.3
|
||||
*/
|
||||
<T> CloseableIterator<T> stream(CriteriaQuery query, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Executes the given {@link SearchQuery} against elasticsearch and return result as {@link CloseableIterator}.
|
||||
* <p>
|
||||
* Returns a {@link CloseableIterator} that wraps an Elasticsearch scroll context that needs to be closed in case of error.
|
||||
*
|
||||
* @param <T> element return type
|
||||
* @param query
|
||||
* @param clazz
|
||||
* @return
|
||||
* @since 1.3
|
||||
*/
|
||||
<T> CloseableIterator<T> stream(SearchQuery query, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Executes the given {@link SearchQuery} against elasticsearch and return result as {@link CloseableIterator} using custom mapper.
|
||||
* <p>
|
||||
* Returns a {@link CloseableIterator} that wraps an Elasticsearch scroll context that needs to be closed in case of error.
|
||||
*
|
||||
* @param <T> element return type
|
||||
* @param query
|
||||
* @param clazz
|
||||
* @param mapper
|
||||
* @return
|
||||
* @since 1.3
|
||||
*/
|
||||
<T> CloseableIterator<T> stream(SearchQuery query, Class<T> clazz, SearchResultMapper mapper);
|
||||
|
||||
/**
|
||||
* Execute the criteria query against elasticsearch and return result as {@link List}
|
||||
*
|
||||
* @param query
|
||||
* @param clazz
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
<T> List<T> queryForList(CriteriaQuery query, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Execute the string query against elasticsearch and return result as {@link List}
|
||||
*
|
||||
* @param query
|
||||
* @param clazz
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
<T> List<T> queryForList(StringQuery query, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Execute the search query against elasticsearch and return result as {@link List}
|
||||
*
|
||||
* @param query
|
||||
* @param clazz
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
<T> List<T> queryForList(SearchQuery query, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Execute the query against elasticsearch and return ids
|
||||
*
|
||||
* @param query
|
||||
* @return
|
||||
*/
|
||||
<T> List<String> queryForIds(SearchQuery query);
|
||||
|
||||
/**
|
||||
* return number of elements found by given query
|
||||
*
|
||||
* @param query
|
||||
* @param clazz
|
||||
* @return
|
||||
*/
|
||||
<T> long count(CriteriaQuery query, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* return number of elements found by given query
|
||||
*
|
||||
* @param query
|
||||
* @return
|
||||
*/
|
||||
<T> long count(CriteriaQuery query);
|
||||
|
||||
/**
|
||||
* return number of elements found by given query
|
||||
*
|
||||
* @param query
|
||||
* @param clazz
|
||||
* @return
|
||||
*/
|
||||
<T> long count(SearchQuery query, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* return number of elements found by given query
|
||||
*
|
||||
* @param query
|
||||
* @return
|
||||
*/
|
||||
<T> long count(SearchQuery query);
|
||||
|
||||
/**
|
||||
* Execute a multiGet against elasticsearch for the given ids
|
||||
*
|
||||
* @param searchQuery
|
||||
* @param clazz
|
||||
* @return
|
||||
*/
|
||||
<T> LinkedList<T> multiGet(SearchQuery searchQuery, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Execute a multiGet against elasticsearch for the given ids with MultiGetResultMapper
|
||||
*
|
||||
* @param searchQuery
|
||||
* @param clazz
|
||||
* @param multiGetResultMapper
|
||||
* @return
|
||||
*/
|
||||
<T> LinkedList<T> multiGet(SearchQuery searchQuery, Class<T> clazz, MultiGetResultMapper multiGetResultMapper);
|
||||
|
||||
/**
|
||||
* Index an object. Will do save or update
|
||||
*
|
||||
* @param query
|
||||
* @return returns the document id
|
||||
*/
|
||||
String index(IndexQuery query);
|
||||
|
||||
/**
|
||||
* Partial update of the document
|
||||
*
|
||||
* @param updateQuery
|
||||
* @return
|
||||
*/
|
||||
UpdateResponse update(UpdateQuery updateQuery);
|
||||
|
||||
/**
|
||||
* Bulk index all objects. Will do save or update
|
||||
*
|
||||
* @param queries
|
||||
*/
|
||||
void bulkIndex(List<IndexQuery> queries);
|
||||
|
||||
/**
|
||||
* Bulk update all objects. Will do update
|
||||
*
|
||||
* @param queries
|
||||
*/
|
||||
void bulkUpdate(List<UpdateQuery> queries);
|
||||
|
||||
/**
|
||||
* Delete the one object with provided id
|
||||
*
|
||||
* @param indexName
|
||||
* @param type
|
||||
* @param id
|
||||
* @return documentId of the document deleted
|
||||
*/
|
||||
String delete(String indexName, String type, String id);
|
||||
|
||||
|
||||
/**
|
||||
* Delete all records matching the criteria
|
||||
*
|
||||
* @param clazz
|
||||
* @param criteriaQuery
|
||||
*/
|
||||
<T> void delete(CriteriaQuery criteriaQuery, Class<T> clazz);
|
||||
/**
|
||||
* Delete the one object with provided id
|
||||
*
|
||||
* @param clazz
|
||||
* @param id
|
||||
* @return documentId of the document deleted
|
||||
*/
|
||||
<T> String delete(Class<T> clazz, String id);
|
||||
|
||||
/**
|
||||
* Delete all records matching the query
|
||||
*
|
||||
* @param clazz
|
||||
* @param query
|
||||
*/
|
||||
<T> void delete(DeleteQuery query, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Delete all records matching the query
|
||||
*
|
||||
* @param query
|
||||
*/
|
||||
void delete(DeleteQuery query);
|
||||
|
||||
/**
|
||||
* Deletes an index for given entity
|
||||
*
|
||||
* @param clazz
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
<T> boolean deleteIndex(Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Deletes an index for given indexName
|
||||
*
|
||||
* @param indexName
|
||||
* @return
|
||||
*/
|
||||
boolean deleteIndex(String indexName);
|
||||
|
||||
/**
|
||||
* check if index is exists
|
||||
*
|
||||
* @param clazz
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
<T> boolean indexExists(Class<T> clazz);
|
||||
|
||||
/**
|
||||
* check if index is exists for given IndexName
|
||||
*
|
||||
* @param indexName
|
||||
* @return
|
||||
*/
|
||||
boolean indexExists(String indexName);
|
||||
|
||||
/**
|
||||
* check if type is exists in an index
|
||||
*
|
||||
* @param index
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
boolean typeExists(String index, String type);
|
||||
|
||||
/**
|
||||
* refresh the index
|
||||
*
|
||||
* @param indexName
|
||||
*
|
||||
*/
|
||||
void refresh(String indexName);
|
||||
|
||||
/**
|
||||
* refresh the index
|
||||
*
|
||||
* @param clazz
|
||||
*
|
||||
*/
|
||||
<T> void refresh(Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Returns scrolled page for given query
|
||||
*
|
||||
* @param query The search query.
|
||||
* @param scrollTimeInMillis The time in millisecond for scroll feature
|
||||
* {@link org.elasticsearch.action.search.SearchRequestBuilder#setScroll(org.elasticsearch.common.unit.TimeValue)}.
|
||||
* @param clazz The class of entity to retrieve.
|
||||
* @return The scan id for input query.
|
||||
*/
|
||||
<T> Page<T> startScroll(long scrollTimeInMillis, SearchQuery query, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Returns scrolled page for given query
|
||||
*
|
||||
* @param query The search query.
|
||||
* @param scrollTimeInMillis The time in millisecond for scroll feature
|
||||
* {@link org.elasticsearch.action.search.SearchRequestBuilder#setScroll(org.elasticsearch.common.unit.TimeValue)}.
|
||||
* @param mapper Custom impl to map result to entities
|
||||
* @return The scan id for input query.
|
||||
*/
|
||||
<T> Page<T> startScroll(long scrollTimeInMillis, SearchQuery query, Class<T> clazz, SearchResultMapper mapper);
|
||||
|
||||
/**
|
||||
* Returns scrolled page for given query
|
||||
*
|
||||
* @param criteriaQuery The search query.
|
||||
* @param scrollTimeInMillis The time in millisecond for scroll feature
|
||||
* {@link org.elasticsearch.action.search.SearchRequestBuilder#setScroll(org.elasticsearch.common.unit.TimeValue)}.
|
||||
* @param clazz The class of entity to retrieve.
|
||||
* @return The scan id for input query.
|
||||
*/
|
||||
<T> Page<T> startScroll(long scrollTimeInMillis, CriteriaQuery criteriaQuery, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Returns scrolled page for given query
|
||||
*
|
||||
* @param criteriaQuery The search query.
|
||||
* @param scrollTimeInMillis The time in millisecond for scroll feature
|
||||
* {@link org.elasticsearch.action.search.SearchRequestBuilder#setScroll(org.elasticsearch.common.unit.TimeValue)}.
|
||||
* @param mapper Custom impl to map result to entities
|
||||
* @return The scan id for input query.
|
||||
*/
|
||||
<T> Page<T> startScroll(long scrollTimeInMillis, CriteriaQuery criteriaQuery, Class<T> clazz, SearchResultMapper mapper);
|
||||
|
||||
|
||||
<T> Page<T> continueScroll(@Nullable String scrollId, long scrollTimeInMillis, Class<T> clazz);
|
||||
<T> Page<T> continueScroll(@Nullable String scrollId, long scrollTimeInMillis, Class<T> clazz, SearchResultMapper mapper);
|
||||
/**
|
||||
* Clears the search contexts associated with specified scroll ids.
|
||||
*
|
||||
* @param scrollId
|
||||
*
|
||||
*/
|
||||
<T> void clearScroll(String scrollId);
|
||||
|
||||
/**
|
||||
* more like this query to search for documents that are "like" a specific document.
|
||||
*
|
||||
* @param query
|
||||
* @param clazz
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
<T> Page<T> moreLikeThis(MoreLikeThisQuery query, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* adding new alias
|
||||
*
|
||||
* @param query
|
||||
* @return
|
||||
*/
|
||||
Boolean addAlias(AliasQuery query);
|
||||
|
||||
/**
|
||||
* removing previously created alias
|
||||
*
|
||||
* @param query
|
||||
* @return
|
||||
*/
|
||||
Boolean removeAlias(AliasQuery query);
|
||||
|
||||
/**
|
||||
* get all the alias pointing to specified index
|
||||
*
|
||||
* @param indexName
|
||||
* @return
|
||||
*/
|
||||
List<AliasMetaData> queryForAlias(String indexName);
|
||||
|
||||
|
||||
<T> T query(SearchQuery query, ResultsExtractor<T> resultsExtractor);
|
||||
|
||||
|
||||
ElasticsearchPersistentEntity getPersistentEntityFor(Class clazz);
|
||||
}
|
||||
|
||||
-364
@@ -1,364 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.elasticsearch.action.bulk.BulkRequest;
|
||||
import org.elasticsearch.action.delete.DeleteRequest;
|
||||
import org.elasticsearch.action.get.GetRequest;
|
||||
import org.elasticsearch.action.get.GetResponse;
|
||||
import org.elasticsearch.action.get.MultiGetRequest;
|
||||
import org.elasticsearch.action.get.MultiGetResponse;
|
||||
import org.elasticsearch.action.index.IndexRequest;
|
||||
import org.elasticsearch.action.search.ClearScrollRequest;
|
||||
import org.elasticsearch.action.search.MultiSearchRequest;
|
||||
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.update.UpdateRequest;
|
||||
import org.elasticsearch.client.RequestOptions;
|
||||
import org.elasticsearch.client.RestHighLevelClient;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
|
||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||
import org.elasticsearch.search.suggest.SuggestBuilder;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.elasticsearch.ElasticsearchException;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.document.DocumentAdapters;
|
||||
import org.springframework.data.elasticsearch.core.document.SearchDocumentResponse;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.query.BulkOptions;
|
||||
import org.springframework.data.elasticsearch.core.query.DeleteQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.IndexQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.UpdateResponse;
|
||||
import org.springframework.data.elasticsearch.support.SearchHitsUtil;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* ElasticsearchRestTemplate
|
||||
*
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Artur Konczak
|
||||
* @author Kevin Leturc
|
||||
* @author Mason Chan
|
||||
* @author Young Gu
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Janssen
|
||||
* @author Chris White
|
||||
* @author Mark Paluch
|
||||
* @author Ilkang Na
|
||||
* @author Alen Turkovic
|
||||
* @author Sascha Woo
|
||||
* @author Ted Liang
|
||||
* @author Don Wellington
|
||||
* @author Zetang Zeng
|
||||
* @author Peter Nowak
|
||||
* @author Ivan Greene
|
||||
* @author Christoph Strobl
|
||||
* @author Lorenzo Spinelli
|
||||
* @author Dmitriy Yakovlev
|
||||
* @author Roman Puchkovskiy
|
||||
* @author Martin Choraine
|
||||
* @author Farid Azaza
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Mathias Teier
|
||||
* @author Gyula Attila Csorogi
|
||||
* @author Massimiliano Poggi
|
||||
*/
|
||||
public class ElasticsearchRestTemplate extends AbstractElasticsearchTemplate {
|
||||
|
||||
private RestHighLevelClient client;
|
||||
|
||||
// region Initialization
|
||||
public ElasticsearchRestTemplate(RestHighLevelClient client) {
|
||||
this.client = client;
|
||||
initialize(client, createElasticsearchConverter());
|
||||
}
|
||||
|
||||
public ElasticsearchRestTemplate(RestHighLevelClient client, ElasticsearchConverter elasticsearchConverter) {
|
||||
this.client = client;
|
||||
initialize(client, elasticsearchConverter);
|
||||
}
|
||||
|
||||
private void initialize(RestHighLevelClient client, ElasticsearchConverter elasticsearchConverter) {
|
||||
|
||||
Assert.notNull(client, "Client must not be null!");
|
||||
|
||||
this.client = client;
|
||||
initialize(elasticsearchConverter);
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region IndexOperations
|
||||
@Override
|
||||
public IndexOperations indexOps(Class<?> clazz) {
|
||||
|
||||
Assert.notNull(clazz, "clazz must not be null");
|
||||
|
||||
return new DefaultIndexOperations(client, elasticsearchConverter, clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndexOperations indexOps(IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(index, "index must not be null");
|
||||
|
||||
return new DefaultIndexOperations(client, elasticsearchConverter, index);
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region DocumentOperations
|
||||
@Override
|
||||
public String index(IndexQuery query, IndexCoordinates index) {
|
||||
IndexRequest request = requestFactory.indexRequest(query, index);
|
||||
try {
|
||||
String documentId = client.index(request, RequestOptions.DEFAULT).getId();
|
||||
|
||||
// We should call this because we are not going through a mapper.
|
||||
if (query.getObject() != null) {
|
||||
setPersistentEntityId(query.getObject(), documentId);
|
||||
}
|
||||
return documentId;
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error while index for request: " + request.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public <T> T get(String id, Class<T> clazz, IndexCoordinates index) {
|
||||
GetRequest request = requestFactory.getRequest(id, index);
|
||||
try {
|
||||
GetResponse response = client.get(request, RequestOptions.DEFAULT);
|
||||
return elasticsearchConverter.mapDocument(DocumentAdapters.from(response), clazz);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error while getting for request: " + request.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> multiGet(Query query, Class<T> clazz, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(index, "index must not be null");
|
||||
Assert.notEmpty(query.getIds(), "No Id define for Query");
|
||||
|
||||
MultiGetRequest request = requestFactory.multiGetRequest(query, index);
|
||||
try {
|
||||
MultiGetResponse result = client.mget(request, RequestOptions.DEFAULT);
|
||||
return elasticsearchConverter.mapDocuments(DocumentAdapters.from(result), clazz);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error while multiget for request: " + request.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doExists(String id, IndexCoordinates index) {
|
||||
GetRequest request = requestFactory.getRequest(id, index);
|
||||
try {
|
||||
return client.get(request, RequestOptions.DEFAULT).isExists();
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error while getting for request: " + request.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> bulkIndex(List<IndexQuery> queries, BulkOptions bulkOptions, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(queries, "List of IndexQuery must not be null");
|
||||
Assert.notNull(bulkOptions, "BulkOptions must not be null");
|
||||
|
||||
return doBulkOperation(queries, bulkOptions, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bulkUpdate(List<UpdateQuery> queries, BulkOptions bulkOptions, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(queries, "List of UpdateQuery must not be null");
|
||||
Assert.notNull(bulkOptions, "BulkOptions must not be null");
|
||||
|
||||
doBulkOperation(queries, bulkOptions, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String delete(String id, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(id, "id must not be null");
|
||||
Assert.notNull(index, "index must not be null");
|
||||
|
||||
DeleteRequest request = new DeleteRequest(index.getIndexName(), elasticsearchConverter.convertId(id));
|
||||
try {
|
||||
return client.delete(request, RequestOptions.DEFAULT).getId();
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error while deleting item request: " + request.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Query query, Class<?> clazz, IndexCoordinates index) {
|
||||
DeleteByQueryRequest deleteByQueryRequest = requestFactory.deleteByQueryRequest(query, clazz, index);
|
||||
try {
|
||||
client.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error for delete request: " + deleteByQueryRequest.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void delete(DeleteQuery deleteQuery, IndexCoordinates index) {
|
||||
DeleteByQueryRequest deleteByQueryRequest = requestFactory.deleteByQueryRequest(deleteQuery, index);
|
||||
try {
|
||||
client.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error for delete request: " + deleteByQueryRequest.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpdateResponse update(UpdateQuery query, IndexCoordinates index) {
|
||||
UpdateRequest request = requestFactory.updateRequest(query, index);
|
||||
try {
|
||||
org.elasticsearch.action.update.UpdateResponse updateResponse = client.update(request, RequestOptions.DEFAULT);
|
||||
UpdateResponse.Result result = UpdateResponse.Result.valueOf(updateResponse.getResult().name());
|
||||
return new UpdateResponse(result);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error while update for request: " + request.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> doBulkOperation(List<?> queries, BulkOptions bulkOptions, IndexCoordinates index) {
|
||||
BulkRequest bulkRequest = requestFactory.bulkRequest(queries, bulkOptions, index);
|
||||
try {
|
||||
return checkForBulkOperationFailure(client.bulk(bulkRequest, RequestOptions.DEFAULT));
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error while bulk for request: " + bulkRequest.toString(), e);
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region SearchOperations
|
||||
@Override
|
||||
public long count(Query query, @Nullable Class<?> clazz, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(query, "query must not be null");
|
||||
Assert.notNull(index, "index must not be null");
|
||||
|
||||
final boolean trackTotalHits = query.getTrackTotalHits();
|
||||
query.setTrackTotalHits(true);
|
||||
SearchRequest searchRequest = requestFactory.searchRequest(query, clazz, index);
|
||||
query.setTrackTotalHits(trackTotalHits);
|
||||
|
||||
searchRequest.source().size(0);
|
||||
|
||||
try {
|
||||
return SearchHitsUtil.getTotalCount(client.search(searchRequest, RequestOptions.DEFAULT).getHits());
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error for search request: " + searchRequest.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> SearchHits<T> search(Query query, Class<T> clazz, IndexCoordinates index) {
|
||||
SearchRequest searchRequest = requestFactory.searchRequest(query, clazz, index);
|
||||
SearchResponse response;
|
||||
try {
|
||||
response = client.search(searchRequest, RequestOptions.DEFAULT);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error for search request: " + searchRequest.toString(), e);
|
||||
}
|
||||
return elasticsearchConverter.read(clazz, SearchDocumentResponse.from(response));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ScrolledPage<SearchHit<T>> searchScrollStart(long scrollTimeInMillis, Query query, Class<T> clazz,
|
||||
IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(query.getPageable(), "Query.pageable is required for scan & scroll");
|
||||
|
||||
SearchRequest searchRequest = requestFactory.searchRequest(query, clazz, index);
|
||||
searchRequest.scroll(TimeValue.timeValueMillis(scrollTimeInMillis));
|
||||
|
||||
try {
|
||||
SearchResponse result = client.search(searchRequest, RequestOptions.DEFAULT);
|
||||
return elasticsearchConverter.mapResults(SearchDocumentResponse.from(result), clazz, null);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error for search request with scroll: " + searchRequest.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ScrolledPage<SearchHit<T>> searchScrollContinue(@Nullable String scrollId, long scrollTimeInMillis,
|
||||
Class<T> clazz) {
|
||||
SearchScrollRequest request = new SearchScrollRequest(scrollId);
|
||||
request.scroll(TimeValue.timeValueMillis(scrollTimeInMillis));
|
||||
SearchResponse response;
|
||||
try {
|
||||
response = client.searchScroll(request, RequestOptions.DEFAULT);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error for search request with scroll: " + request.toString(), e);
|
||||
}
|
||||
return elasticsearchConverter.mapResults(SearchDocumentResponse.from(response), clazz, Pageable.unpaged());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void searchScrollClear(String scrollId) {
|
||||
ClearScrollRequest request = new ClearScrollRequest();
|
||||
request.addScrollId(scrollId);
|
||||
try {
|
||||
client.clearScroll(request, RequestOptions.DEFAULT);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error for search request with scroll: " + request.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchResponse suggest(SuggestBuilder suggestion, IndexCoordinates index) {
|
||||
SearchRequest searchRequest = new SearchRequest(index.getIndexNames());
|
||||
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
|
||||
sourceBuilder.suggest(suggestion);
|
||||
searchRequest.source(sourceBuilder);
|
||||
|
||||
try {
|
||||
return client.search(searchRequest, RequestOptions.DEFAULT);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Could not execute search request : " + searchRequest.toString(), e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MultiSearchResponse.Item[] getMultiSearchResult(MultiSearchRequest request) {
|
||||
MultiSearchResponse response;
|
||||
try {
|
||||
response = client.multiSearch(request, RequestOptions.DEFAULT);
|
||||
} catch (IOException e) {
|
||||
throw new ElasticsearchException("Error for search request: " + request.toString(), e);
|
||||
}
|
||||
MultiSearchResponse.Item[] items = response.getResponses();
|
||||
Assert.isTrue(items.length == request.requests().size(), "Response should has same length with queries");
|
||||
return items;
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
+1086
-178
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2014 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
|
||||
*
|
||||
* 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 "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.io.IOException;
|
||||
|
||||
/**
|
||||
* DocumentMapper interface, it will allow to customize how we mapping object to json
|
||||
*
|
||||
* @author Artur Konczak
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
*/
|
||||
public interface EntityMapper {
|
||||
|
||||
public String mapToString(Object object) throws IOException;
|
||||
|
||||
public <T> T mapToObject(String source, Class<T> clazz) throws IOException;
|
||||
}
|
||||
@@ -1,610 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018-2020 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 lombok.AccessLevel;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
|
||||
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.mapping.IdentifierAccessor;
|
||||
import org.springframework.data.mapping.PersistentPropertyAccessor;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Common operations performed on an entity in the context of it's mapping metadata.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 3.2
|
||||
*/
|
||||
class EntityOperations {
|
||||
|
||||
private static final String ID_FIELD = "id";
|
||||
|
||||
public EntityOperations(
|
||||
@NonNull MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
private final @NonNull MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> context;
|
||||
|
||||
/**
|
||||
* Creates a new {@link Entity} for the given bean.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
<T> Entity<T> forEntity(T entity) {
|
||||
|
||||
Assert.notNull(entity, "Bean must not be null!");
|
||||
|
||||
if (entity instanceof Map) {
|
||||
return new SimpleMappedEntity((Map<String, Object>) entity);
|
||||
}
|
||||
|
||||
return MappedEntity.of(entity, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link AdaptibleEntity} for the given bean and {@link ConversionService}.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @param conversionService must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
<T> AdaptibleEntity<T> forEntity(T entity, ConversionService conversionService) {
|
||||
|
||||
Assert.notNull(entity, "Bean must not be null!");
|
||||
Assert.notNull(conversionService, "ConversionService must not be null!");
|
||||
|
||||
if (entity instanceof Map) {
|
||||
return new SimpleMappedEntity((Map<String, Object>) entity);
|
||||
}
|
||||
|
||||
return AdaptibleMappedEntity.of(entity, context, conversionService);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
IndexCoordinates determineIndex(Entity<?> entity, @Nullable String index, @Nullable String type) {
|
||||
return determineIndex(entity.getPersistentEntity(), index, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
IndexCoordinates determineIndex(ElasticsearchPersistentEntity<?> persistentEntity, @Nullable String index,
|
||||
@Nullable String type) {
|
||||
return persistentEntity.getIndexCoordinates();
|
||||
}
|
||||
|
||||
private static String indexName(@Nullable ElasticsearchPersistentEntity<?> entity, @Nullable String index) {
|
||||
|
||||
if (StringUtils.isEmpty(index)) {
|
||||
Assert.notNull(entity, "Cannot determine index name");
|
||||
return entity.getIndexCoordinates().getIndexName();
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* A representation of information about an entity.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
interface Entity<T> {
|
||||
|
||||
/**
|
||||
* Returns the identifier of the entity.
|
||||
*
|
||||
* @return the ID value, can be {@literal null}.
|
||||
*/
|
||||
@Nullable
|
||||
Object getId();
|
||||
|
||||
/**
|
||||
* Returns whether the entity is versioned, i.e. if it contains a version property.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
default boolean isVersionedEntity() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the version if the entity has a version property, {@literal null} otherwise.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Nullable
|
||||
Object getVersion();
|
||||
|
||||
/**
|
||||
* Returns the underlying bean.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
T getBean();
|
||||
|
||||
/**
|
||||
* Returns whether the entity is considered to be new.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean isNew();
|
||||
|
||||
/**
|
||||
* Returns the {@link ElasticsearchPersistentEntity} associated with this entity.
|
||||
*
|
||||
* @return can be {@literal null} if this entity is not mapped.
|
||||
*/
|
||||
@Nullable
|
||||
ElasticsearchPersistentEntity<?> getPersistentEntity();
|
||||
|
||||
/**
|
||||
* Returns the required {@link ElasticsearchPersistentEntity}.
|
||||
*
|
||||
* @return
|
||||
* @throws IllegalStateException if no {@link ElasticsearchPersistentEntity} is associated with this entity.
|
||||
*/
|
||||
default ElasticsearchPersistentEntity<?> getRequiredPersistentEntity() {
|
||||
|
||||
ElasticsearchPersistentEntity<?> persistentEntity = getPersistentEntity();
|
||||
if (persistentEntity == null) {
|
||||
throw new IllegalStateException("No ElasticsearchPersistentEntity available for this entity!");
|
||||
}
|
||||
|
||||
return persistentEntity;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Information and commands on an entity.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
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}.
|
||||
*/
|
||||
boolean hasParent();
|
||||
|
||||
/**
|
||||
* Returns the parent Id. Can be {@literal null}.
|
||||
*
|
||||
* @return can be {@literal null}.
|
||||
*/
|
||||
@Nullable
|
||||
Object getParentId();
|
||||
|
||||
/**
|
||||
* Populates the identifier of the backing entity if it has an identifier property and there's no identifier
|
||||
* currently present.
|
||||
*
|
||||
* @param id can be {@literal null}.
|
||||
* @return can be {@literal null}.
|
||||
*/
|
||||
@Nullable
|
||||
T populateIdIfNecessary(@Nullable Object id);
|
||||
|
||||
/**
|
||||
* Initializes the version property of the of the current entity if available.
|
||||
*
|
||||
* @return the entity with the version property updated if available.
|
||||
*/
|
||||
T initializeVersionProperty();
|
||||
|
||||
/**
|
||||
* Increments the value of the version property if available.
|
||||
*
|
||||
* @return the entity with the version property incremented if available.
|
||||
*/
|
||||
T incrementVersion();
|
||||
|
||||
/**
|
||||
* Returns the current version value if the entity has a version property.
|
||||
*
|
||||
* @return the current version or {@literal null} in case it's uninitialized or the entity doesn't expose a version
|
||||
* property.
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
Number getVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param <T>
|
||||
* @author Christoph Strobl
|
||||
* @since 3.2
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
private static class MapBackedEntity<T extends Map<String, Object>> implements AdaptibleEntity<T> {
|
||||
|
||||
private final T map;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.EntityOperations.Entity#getId()
|
||||
*/
|
||||
@Override
|
||||
public Object getId() {
|
||||
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)
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public T populateIdIfNecessary(@Nullable Object id) {
|
||||
|
||||
map.put(ID_FIELD, id);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.EntityOperations.AdaptibleEntity#initializeVersionProperty()
|
||||
*/
|
||||
@Override
|
||||
public T initializeVersionProperty() {
|
||||
return map;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.EntityOperations.AdaptibleEntity#getVersion()
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public Number getVersion() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.EntityOperations.AdaptibleEntity#incrementVersion()
|
||||
*/
|
||||
@Override
|
||||
public T incrementVersion() {
|
||||
return map;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.EntityOperations.Entity#getBean()
|
||||
*/
|
||||
@Override
|
||||
public T getBean() {
|
||||
return map;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.EntityOperations.Entity#isNew()
|
||||
*/
|
||||
@Override
|
||||
public boolean isNew() {
|
||||
return map.get(ID_FIELD) != null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.EntityOperations.Entity#getPersistentEntity()
|
||||
*/
|
||||
@Override
|
||||
public ElasticsearchPersistentEntity<?> getPersistentEntity() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Plain entity without applying further mapping.
|
||||
*
|
||||
* @param <T>
|
||||
* @since 3.2
|
||||
*/
|
||||
private static class UnmappedEntity<T extends Map<String, Object>> extends MapBackedEntity<T> {
|
||||
|
||||
UnmappedEntity(T map) {
|
||||
super(map);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple mapped entity without an associated {@link ElasticsearchPersistentEntity}.
|
||||
*
|
||||
* @param <T>
|
||||
* @since 3.2
|
||||
*/
|
||||
private static class SimpleMappedEntity<T extends Map<String, Object>> extends MapBackedEntity<T> {
|
||||
|
||||
SimpleMappedEntity(T map) {
|
||||
super(map);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.EntityOperations.UnmappedEntity#getId()
|
||||
*/
|
||||
@Override
|
||||
public Object getId() {
|
||||
return getBean().get(ID_FIELD);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapped entity with an associated {@link ElasticsearchPersistentEntity}.
|
||||
*
|
||||
* @param <T>
|
||||
* @since 3.2
|
||||
*/
|
||||
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
private static class MappedEntity<T> implements Entity<T> {
|
||||
|
||||
private final ElasticsearchPersistentEntity<?> entity;
|
||||
private final IdentifierAccessor idAccessor;
|
||||
private final PersistentPropertyAccessor<T> propertyAccessor;
|
||||
|
||||
private static <T> MappedEntity<T> of(T bean,
|
||||
MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> context) {
|
||||
|
||||
ElasticsearchPersistentEntity<?> entity = context.getRequiredPersistentEntity(bean.getClass());
|
||||
IdentifierAccessor identifierAccessor = entity.getIdentifierAccessor(bean);
|
||||
PersistentPropertyAccessor<T> propertyAccessor = entity.getPropertyAccessor(bean);
|
||||
|
||||
return new MappedEntity<>(entity, identifierAccessor, propertyAccessor);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.EntityOperations.Entity#getId()
|
||||
*/
|
||||
@Override
|
||||
public Object getId() {
|
||||
return idAccessor.getIdentifier();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.EntityOperations.Entity#isVersionedEntity()
|
||||
*/
|
||||
@Override
|
||||
public boolean isVersionedEntity() {
|
||||
return entity.hasVersionProperty();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.EntityOperations.Entity#getVersion()
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public Object getVersion() {
|
||||
return propertyAccessor.getProperty(entity.getRequiredVersionProperty());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.EntityOperations.Entity#getBean()
|
||||
*/
|
||||
@Override
|
||||
public T getBean() {
|
||||
return propertyAccessor.getBean();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.EntityOperations.Entity#isNew()
|
||||
*/
|
||||
@Override
|
||||
public boolean isNew() {
|
||||
return entity.isNew(propertyAccessor.getBean());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.EntityOperations.Entity#getPersistentEntity()
|
||||
*/
|
||||
@Override
|
||||
public ElasticsearchPersistentEntity<?> getPersistentEntity() {
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param <T>
|
||||
* @since 3.2
|
||||
*/
|
||||
private static class AdaptibleMappedEntity<T> extends MappedEntity<T> implements AdaptibleEntity<T> {
|
||||
|
||||
private final ElasticsearchPersistentEntity<?> entity;
|
||||
private final ConvertingPropertyAccessor<T> propertyAccessor;
|
||||
private final IdentifierAccessor identifierAccessor;
|
||||
|
||||
private AdaptibleMappedEntity(ElasticsearchPersistentEntity<?> entity, IdentifierAccessor identifierAccessor,
|
||||
ConvertingPropertyAccessor<T> propertyAccessor) {
|
||||
|
||||
super(entity, identifierAccessor, propertyAccessor);
|
||||
|
||||
this.entity = entity;
|
||||
this.propertyAccessor = propertyAccessor;
|
||||
this.identifierAccessor = identifierAccessor;
|
||||
}
|
||||
|
||||
static <T> AdaptibleEntity<T> of(T bean,
|
||||
MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> context,
|
||||
ConversionService conversionService) {
|
||||
|
||||
ElasticsearchPersistentEntity<?> entity = context.getRequiredPersistentEntity(bean.getClass());
|
||||
IdentifierAccessor identifierAccessor = entity.getIdentifierAccessor(bean);
|
||||
PersistentPropertyAccessor<T> propertyAccessor = entity.getPropertyAccessor(bean);
|
||||
|
||||
return new AdaptibleMappedEntity<>(entity, identifierAccessor,
|
||||
new ConvertingPropertyAccessor<>(propertyAccessor, conversionService));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.EntityOperations.AdaptibleEntity#hasParent()
|
||||
*/
|
||||
@Override
|
||||
public boolean hasParent() {
|
||||
return getRequiredPersistentEntity().getParentIdProperty() != null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.EntityOperations.AdaptibleEntity#getParentId()
|
||||
*/
|
||||
@Override
|
||||
public Object getParentId() {
|
||||
|
||||
ElasticsearchPersistentProperty parentProperty = getRequiredPersistentEntity().getParentIdProperty();
|
||||
return propertyAccessor.getProperty(parentProperty);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.EntityOperations.AdaptibleEntity#populateIdIfNecessary(java.lang.Object)
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public T populateIdIfNecessary(@Nullable Object id) {
|
||||
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
T bean = propertyAccessor.getBean();
|
||||
ElasticsearchPersistentProperty idProperty = entity.getIdProperty();
|
||||
|
||||
if (idProperty == null) {
|
||||
return bean;
|
||||
}
|
||||
|
||||
if (identifierAccessor.getIdentifier() != null) {
|
||||
return bean;
|
||||
}
|
||||
|
||||
propertyAccessor.setProperty(idProperty, id);
|
||||
|
||||
return propertyAccessor.getBean();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.EntityOperations.MappedEntity#getVersion()
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public Number getVersion() {
|
||||
|
||||
ElasticsearchPersistentProperty versionProperty = entity.getRequiredVersionProperty();
|
||||
|
||||
return propertyAccessor.getProperty(versionProperty, Number.class);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.EntityOperations.AdaptibleEntity#initializeVersionProperty()
|
||||
*/
|
||||
@Override
|
||||
public T initializeVersionProperty() {
|
||||
|
||||
if (!entity.hasVersionProperty()) {
|
||||
return propertyAccessor.getBean();
|
||||
}
|
||||
|
||||
ElasticsearchPersistentProperty versionProperty = entity.getRequiredVersionProperty();
|
||||
|
||||
propertyAccessor.setProperty(versionProperty, versionProperty.getType().isPrimitive() ? 1 : 0);
|
||||
|
||||
return propertyAccessor.getBean();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.elasticsearch.core.EntityOperations.AdaptibleEntity#incrementVersion()
|
||||
*/
|
||||
@Override
|
||||
public T incrementVersion() {
|
||||
|
||||
ElasticsearchPersistentProperty versionProperty = entity.getRequiredVersionProperty();
|
||||
Number version = getVersion();
|
||||
Number nextVersion = version == null ? 0 : version.longValue() + 1;
|
||||
|
||||
propertyAccessor.setProperty(versionProperty, nextVersion);
|
||||
|
||||
return propertyAccessor.getBean();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user